GithubHelp home page GithubHelp logo

thekonz / sequence Goto Github PK

View Code? Open in Web Editor NEW

This project forked from shampine/sequence

0.0 1.0 0.0 674 KB

Framework agnostic pipelining made simple.

Home Page: https://medium.com/gosteady/day-5-sequence-how-to-guide-56c0af1b2303

License: MIT License

PHP 99.52% Shell 0.48%

sequence's Introduction

sequence

example workflow name

A framework agnostic pipelining package to handle complete requests.

Laravel Demo - https://github.com/shampine/sequence-demo
Tutorial - https://medium.com/gosteady/day-5-sequence-how-to-guide-56c0af1b2303

why

Using the pipeline pattern developers can move quickly, recycle processes, and test everything.

pipeline diagram

Benefits to using pipelines for an MVC framework include

  • skinny and consistent controllers
  • ability to share processes amongst different pipelines
  • simple injection of service or repository classes into the processes to keep code clean
  • ease of testing individual processes
  • clear, consistent api responses
  • eliminate need to try/catch exceptions inside the stack

installation

composer require shampine/sequence

usage

These examples are using Laravel conventions but this package is framework agnostic.

See these three files for verbose usage examples and live demos inside the phpunit tests.

Sample RequestPayload
Sample ResponsePayload
Sample Pipeline

Request Payloads

This is the active workspace. The request payload is mutated as it passes thru each stage. Any data needed from one stage to another needs to be set on the payload, and then retrieved from the payload.

When defining your RequestPayloads you can optionally define a $whitelist and $overrides.

$payload = new SampleRequestPayload($whitelist, $overrides);

A whitelist will limit what user supplied input will be hydrated into the RequestPayload. The overrides parameter allows mapping of different external keys to internal keys. E.g. if the post contains email_address but on the payload the method is called setEmail. Mapping ['email_address' => 'email'] will properly align hydration.

Pipeline Composition

A pipeline can have multiple named closures stored in the $pipelines property. This will allow grouping of similar pipelines together. You can pass attributes into the pipeline either thru the class constructor OR the closure constructor.

Services, repositories, and other dependency injectable parameters are best set by using the class constructor. While flags and other stage related properties can be injected using ->process($pipelineName, $payload, ...$argments).

This example pipeline has a service injected into the constructor but two boolean flags passed through the $arguments parameter on ->process().

class SamplePipeline extends AbstractPipeline
{
    public const SAMPLE_PIPELINE = 'SamplePipeline';

    public function __construct(?SampleUseService $sampleUseService = null)
    {
        $this->pipelines = [
            self::SAMPLE_PIPELINE => static function(
                bool $validationFailure = false,
                bool $sequenceFailure = false
            ) use ($sampleUseService)
            {
                return (new Pipeline)
                    ->pipe(new ValidationExceptionProcess($validationFailure, $sampleUseService))
                    ->pipe(new SequenceExceptionProcess($sequenceFailure))
                    ->pipe(new HydrateResponsePayloadProcess(SampleResponsePayload::class));
            }
        ];

        $this->excludeWhenEmpty = [
            'empty_value',
        ];

        $this->excludeWhenNull = [
            'null_value',
        ];
    }
}

The property $excludeWhenEmpty or $excludeWhenNull will check ANY root or data keys to see if their value is empty() or === null. If so they are excluded from the final array, all keys should use snake_case.

Response Payloads

Response payloads are the final output containers and should be hydrated in the final stage of a pipeline. All properties on the class are REQUIRED to have a getter and setter.


public function __construct(SampleRequestPayload $payload)
{
    $this->setSampleAbout('This is an about statement.');
}

During the format process getSampleAbout would be used to compile the final array that will be returned as json.

Controller Examples (Laravel)

Using dependency injection on your controller to instantiate the pipeline.

class SampleController
{
    public function __construct(SamplePipeline $samplePipeline)
    {
        $this-samplePipeline = $samplePipeline;
    }
}

GET

public function get(Request $request)
{
    $payload = new SampleRequestPayload();
    $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format();

    return response()->json($response, $responde['http_code']);
}

POST

public function post(Request $request)
{
    $payload = (new SampleRequestPayload())->hydratePost($request->all());
    $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format();

    return response()->json($response, $responde['http_code']);
}

PATCH

Patch requests payloads require the PatchInterface and PatchTrait. The payload will contain methods to decipher what is requested to be patched ->getPatch() and whether the payload is a patch request ->isPatch().

public function patch(Request $request)
{
    $payload = (new SampleRequestPayload())->hydratePatch($request->all());
    $response = $this->samplePipeline->process(SamplePipeline::SAMPLE_PIPELINE, $payload)->format();

    return response()->json($response, $responde['http_code']);
}

Exceptions

Included are two exceptions, ValidationException and SequenceException. Both are caught and rendered to json. You can define specific exception by extending these classes. They are caught and rendered the same as a normal payload to easily allow json to be return.

class Fetchuser extends AbstractProcess
{
    public function process()
    {
        $user = $this->userService->getUserRepository()->fetchUserById($id);

        if ($user === null) {
            throw new SequenceException(1000, 'User not found', 400);
        }
    
        $payload->setUser($user);

        return $payload;
    }
}

A null user returns

array(5) {
  'error_code' => int(1000)
  'status_code' => int(400)
  'data' => NULL
  'message' => string(29) 'User not found'
  'error_messages' => NULL
}

This should allow a consistent api returns without the need for try catch. Any standard php exception will fatal the application like normal.

testing

To run all tests run ./tests/run.sh.

This will execute:

  • phpstan level 8
  • phpunit with code coverage (expects 100% coverage)

license

MIT

sequence's People

Contributors

shampine avatar

Watchers

 avatar

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.