Giter VIP home page Giter VIP logo

jsend's People

Contributors

alexmpunkt avatar demv-system avatar dgame avatar kenowessels avatar marcusjochimsen avatar spaceemotion avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsend's Issues

Feld "data" optional für Success/Fail?

Das Feld "data" ist laut Spezifikation für Success & Fail required, kann aber null/empty sein. Es bietet sich daher ggf. aus Bequemlichkeitsgründen an, dieses Feld als optional einzustufen.

ResponseFactory: Add successOrFail helper for boolean responses

Quite often we find ourselves writing the following snippet of code whenever we want to return either a success or fail message based on a boolean:

$response = new JSendResponse($success ? Status::success() : Status::fail(), null);
$response->respond();

Proposal
Add a new successOrFail method to the ResponseFactory class so it can be used as follows:

ResponseFactory::successOrFail($success)->respond();
ResponseFactory::successOrFail(true)->respond();
ResponseFactory::successOrFail(false)->respond();

The expected output for the data key is in all cases null.

Komponente zum senden des Response

Es wäre hilfreich wenn wir eine Komponente hätten wo man das Response-Objekt reingibt und dieses dann als richtiger Response mit entsprechenden Headern etc. gesendet wird.
Dies sollte ggfl. in ein eigenes package

Überarbeitung der API

JSend 2.0

Status: Draft

JSend in der derzeitigen Implementierung hat inzwischen viel Spielraum für Verbesserungen:

  • Soll-Zustand Ein JSendResponse sollte das Psr\ResponseInterface direkt implementieren

    Ist-Zustand Ein JSendResponse hat eine Funktion asResponse (https://github.com/demvsystems/jsend/blob/master/src/AbstractJSendResponse.php#L131) die ein JSendResponse in ein ResponseInterface umwandelt. Allerdings wird dazu die Implementierung von Guzzle verwendet, wodurch es z.B. für den Tarifservice (der illuminate/http verwendet) unbrauchbar ist.

  • Soll-Zustand Die Optionen von json_encode sollten frei konfigurierbar sein.

    Ist-Zustand JSend::encode (https://github.com/demvsystems/jsend/blob/master/src/JSend.php#L53) nimmt ein array entgegen und gibt einen string zurück. Die Optionen sind nicht konfigurierbar

  • Soll-Zustand Der Zugriff auf Daten sollte leicht sein und Default-Argumente mit angegeben werden. Ideal wäre $response->getData(<key>, <default>) wobei <default> optional und mit nullvorbelegt ist.

    Ist-Zustand Der Zugriff ist schwergängig: $response->getData()[<key>] ?? <default>

  • Soll-Zustand Es sollte direkt am Response möglich sein abzufragen, ob das JSendResponse ein Success/Fail/Error ist. Ideal wäre $response->is(Success|Fail|Error)()

    Ist-Zustand Es muss immer erst getStatus()aufgerufen werden: $response->getStatus()->is(Success|Fail|Error)()

  • Soll-Zustand Es sollte durch die API möglich sein anzugeben, was in einem Erfolgs-/Fail-/Fehler-Fall passieren soll. Ideal wäre $response->on(Success|Fail|Error)(<closure>). Auch ein bool-Return-Wert der angibt, ob es ein Success/Fail/Error war und das Closure daher ausgeführt wurde wäre nützlich.

    Ist-Zustand Bisher ist so etwas nicht möglich. Es müssen immer if-Bedingungen der Form if ($response->getStatus()->isFail()) { <logik> } geschrieben werden

  • Soll-Zustand JSendSuccessResponse, JSendFailResponse und JSendErrorResponse sollten klar getrennte Enheiten sein und geteilte/gemeinsame Logik durch traits implementiert werden.

    Ist-Zustand Alle drei haben eine gemeinsame Oberklasse und JSendSuccess und JSendFail sind eng gekoppelt. Um allerdings konkret an die JSendErrorResponse-Instanz zu kommen, muss zunächst geprüft werden, ob es ein JSendError ist und dann muss noch getError (https://github.com/demvsystems/jsend/blob/master/src/JSendErrorResponse.php#L78) aufgerufen werden.

Neu-Implementierung und Major-Versionssprung

Auf Grund der API-Änderungen (speziell der die durch den 1. Punkt hervorgehen) ist ein Major-Versionssprung notwendig. Außerdem sind wir dadurch nicht mehr an die bisherigen Strukturen und an die bisherige ggf. imkompatible API gebunden. Kompatible und nützliche Features der 2er Versionen können allerdings auch in die 1er Version integriert werden wenn (einfach) möglich & gewünscht. Welche und ob bleibt diskutabel.

Implementierung

Die gemeinsame Oberklasse (AbstractJSendResponse) fällt weg und die statische JSend Klasse wird stark reduziert. Gemeinsame Logiken werden stattdessen durch Traits implementiert. Die Status-Klasse wird weitgehend so gelassen, das überflüssige StatusInterface wird abgeschafft.

Status

Bleibt weitgehend so wie bisher, Interface fällt weg, Konstanten verlieren das überflüssige STATUS_-Prefix

JSendStatus - Trait + Interface

Benutzen alle drei Response-Arten (Success / Fail / Error)

interface JSendStatusInterface
{
    public function getStatus(): Status;
    public function isFail(): bool;
    public function isSuccess(): bool;
    public function isError(): bool;
    public function onSuccess(callable $closure): bool;
    public function onFail(callable $closure): bool;
    public function onError(callable $closure): bool;
}

trait JSendStatusTrait
{
    private $status;

    final public function getStatus(): Status
    {
        return $this->status;
    }

    final public function isFail(): bool
    {
        return $this->status->isFail();
    }

    final public function isSuccess(): bool
    {
        return $this->status->isSuccess();
    }

    final public function isError(): bool
    {
        return $this->status->isError();
    }

    final public function onSuccess(callable $closure): bool
    {
        if ($this->isSuccess()) {
            $closure($this);

            return true;
        }

        return false;
    }

    final public function onFail(callable $closure): bool
    {
        if ($this->isFail()) {
            $closure($this);

            return true;
        }

        return false;
    }

    final public function onError(callable $closure): bool
    {
        if ($this->isError()) {
            $closure($this);

            return true;
        }

        return false;
    }
}

JSendData - Trait + Interface

Wird von Success & Fail benutzt. Der Parameter $key ist ebenfalls optional:

  • hasData: Wird der Parameter $key nicht angegeben wird überprüft, ob überhaupt Daten vorhanden sind (das data nicht leer ist)
  • getData: Wird der Parameter $key nicht angegeben werden alle Daten in Form eines Arrays zurückgegeben
interface JSendDataInterface
{
    public function hasData(string $key = null): bool;
    public function getData(string $key = null, $default = null);
}

trait JSendDataTrait
{
    private $data = [];

    final public function hasData(string $key = null): bool
    {
        if ($key === null) {
            return !empty($this->data);
        }

        return array_key_exists($key, $this->data ?? []);
    }

    final public function getData(string $key = null, $default = null)
    {
        $data = $this->data ?? [];
        if ($key === null) {
            return $data;
        }

        return $data[$key] ?? $default;
    }
}

JSend

Die Klasse mit den statischen Fabrik-Methoden reduziert sich auf eine Helfer-Klasse, die die Überführung von unbekannten JSend-Eingaben in die entsprechenden Objekte sicherstellt. Mit unbekannt ist gemeint, das zum Zeitpunkt der Angabe nicht bekannt bzw. nicht technisch verifiziert wurde, ob es ein Success, Fail oder Error ist.

final class JSend implements JSendStatusInterface, JSendJsonInterface
{
    use JSendStatusTrait;
    use JSendJsonTrait;

    private $status;
    private $decoded = [];

    public function __construct(Status $status, array $decoded)
    {
        $this->status = $status;
        $this->decoded = $decoded;
    }

    public function asArray(): array
    {
        return $this->decoded;
    }

    public function onSuccess(callable $closure): bool
    {
        if ($this->isSuccess()) {
            $closure($this->asSuccess());
            return true;
        }
        return false;
    }

    public function onFail(callable $closure): bool
    {
        if ($this->isFail()) {
           $closure($this->asFail());
            return true;
        }
        return false;
    }

    public function onError(callable $closure): bool
    {
        if ($this->isError()) {
            $closure($this->asError());
            return true;
        }
        return false;
    }

    public function asSuccess(): JSendSuccess
    {
        return JSendSuccess::from($this->decoded);
    }

    public function asFail(): JSendFail
    {
        return JSendFail::from($this->decoded);
    }

    public function asError(): JSendError
    {
        return JSendError::from($this->decoded);
    }

    public static function safeDecode(string $json): array
    {
        $decoded = json_decode($json, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new InvalidJsonException(json_last_error_msg());
        }

        return $decoded;
    }

    public static function decode(string $json): self
    {
        $decoded = self::safeDecode($json);
        
        return self::from($decoded);
    }

    public static function from(array $decoded): self
    {
        ['status' => $status] = export('status')->requireAll()->from($decoded);

        return new self(Status::from($status), $decoded);
    }
}

Abfrage / Identifikation

Empfangenes JSend in Form von Json würde dann wie folgt behandelt werden können:

$jsend = JSend::decode('{...}');
$jsend->onSuccess(function(JSendSuccess $jsend) {
    $this->process($jsend->getData());
});
$jsend->onFail(function(JsendFail $jsend) {
    $this->recoverOrLog($jsend->getData());
});
$jsend->onError(function(JsendError $jsend) {
    $this->throwOrLog($jsend->getMessage());
});

Die Abfrage, ob es sich um ein Success/Fail/Error handelt und die entsprechende Konvertierung ist durch die Methoden is(Success|Fail|Error) und as(Success|Fail|Error) problemlos möglich.

Offene Fragen

  • Sollte die JSend-Klasse (welche ja nur ein Übergangsobjekt zum konkreten JSend ist) auch ein ResponseInterface nach Definition des PSR-Standards sein?

JSendJson - Trait + Interface

Wird von allen drei Response-Arten verwendet.
Das encode / decode ist nun pro Einheit implementiert anstatt wie bisher in einer statischen Methode der JSend-Klasse:

interface JSendJsonInterface extends JsonSerializable
{
    public function asArray(): array;
    public function encode(int $options = 0): string;
    public static function decode(string $json);
    public static function from(array $decoded);
}

trait JSendJsonTrait
{
    final public function encode(int $options = 0): string
    {
        return json_encode($this, $options);
    }

    final public function jsonSerialize(): array
    {
        return $this->asArray();
    }
}
  • decode wird in der jeweiligen konkreten Response-Klasse definiert, um den Rückgabe-Wert & die Überprüfung vorzunehmen.
  • from wird ebenfalls in der jeweiligen konkreten Response-Klasse definiert und akzeptiert ein Array, dessen Inhalt - nach einer vorhergehenden Validierung - in das Objekt geschaufelt wird.

In JSendSuccess würden die beiden Methoden wie folgt definiert:

    public static function decode(string $json): self
    {
        $decoded = JSend::safeDecode($json);
        
        return self::from($decoded);
    }

    public static function from(array $decoded): self
    {
        ['status' => $status, 'data' => $data] = export('status', 'data')->requireAll()->from($decoded);
        enforce(Status::from($status)->isSuccess())->orThrow('Decode non-success in JSendSuccess');

        return new self($data);
    }

JSendResponse - Trait + Interface

Die respondMethode wird in dieses Trait rausgezogen und verbleibt identisch zur bisherigen Implementierung. Selbiges gilt für die Methode getDefaultHttpStatusCode die bis auf kleine Anpassungen genauso verbleibt wie bisher:

final public function respond(int $code = null): void
{
   $code = $this->getHttpStatusCode($code);
   ensure($code)->isInt()->isBetween(100, 511);
   header('Content-Type: application/json; charset="UTF-8"', true, $code);
   print json_encode($this);
   exit;
}

final public function getHttpStatusCode(int $code = null): int
{
   if ($this->isError()) {
       return $this->getCode() ?? $code ?? 500;
   }

   return $code ?? 200;
}

Außerdem erbt das JSendResponseInterface vom ResponseInterface des PSR-Projekts, womit das entsprechende Trait ebenfalls die Response-Funktionalität implementiert.

Instanziierung

So würde bspw. ein neues JSendSuccess instantiiert werden:

new JSendSuccess(); // Ohne $data da optional
new JSendSuccess([...]); // Mit $data

Damit hätte man auch implizit bereits ein Response das jederzeit verarbeitet werden kann.

Offene Fragen

  • Fabrik-Methoden wie JSend::success(...) anbieten?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.