{"id":20314958,"url":"https://github.com/phphd/pipeline-bundle","last_synced_at":"2026-01-21T00:33:38.322Z","repository":{"id":213423633,"uuid":"731788566","full_name":"phphd/pipeline-bundle","owner":"phphd","description":"A streamlined implementation of Chain of Responsibility pattern on top of Symfony Messenger component","archived":false,"fork":false,"pushed_at":"2024-06-22T20:10:46.000Z","size":25,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-03T14:50:11.785Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phphd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-14T22:08:14.000Z","updated_at":"2024-06-22T20:10:49.000Z","dependencies_parsed_at":"2025-04-08T19:31:14.407Z","dependency_job_id":"02ecfd23-646f-42f6-8bdd-c26d3b78838f","html_url":"https://github.com/phphd/pipeline-bundle","commit_stats":null,"previous_names":["phphd/pipeline-bundle"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/phphd/pipeline-bundle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fpipeline-bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fpipeline-bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fpipeline-bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fpipeline-bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phphd","download_url":"https://codeload.github.com/phphd/pipeline-bundle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phphd%2Fpipeline-bundle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28619787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T23:49:58.628Z","status":"ssl_error","status_checked_at":"2026-01-20T23:47:29.996Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T18:17:24.993Z","updated_at":"2026-01-21T00:33:38.296Z","avatar_url":"https://github.com/phphd.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"PhdPipelineBundle\n-----------------\n\n🧰 Provides [Symfony Messenger](https://symfony.com/doc/current/messenger.html) middleware for basic per-bus pipelining.\nIt enables streamlined chaining of the messages created by message handlers. For instance, when handler (hdl1) processes\nmessage (msg1), it creates a subsequent message (msg2), triggering the invocation of the next\nhandler (hdl2), which may, in turn, produce yet another new message, and this cycle continues.\n\n[![Codecov](https://codecov.io/gh/phphd/pipeline-bundle/graph/badge.svg?token=GZRXWYT55Z)](https://codecov.io/gh/phphd/pipeline-bundle)\n[![Psalm coverage](https://shepherd.dev/github/phphd/pipeline-bundle/coverage.svg)](https://shepherd.dev/github/phphd/pipeline-bundle)\n[![Psalm level](https://shepherd.dev/github/phphd/pipeline-bundle/level.svg)](https://shepherd.dev/github/phphd/pipeline-bundle)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/phphd/pipeline-bundle/ci.yaml?branch=main)](https://github.com/phphd/pipeline-bundle/actions?query=branch%3Amain)\n[![Packagist Downloads](https://img.shields.io/packagist/dt/phphd/pipeline-bundle.svg)](https://packagist.org/packages/phphd/pipeline-bundle)\n[![Licence](https://img.shields.io/github/license/phphd/pipeline-bundle.svg)](https://github.com/phphd/pipeline-bundle/blob/main/LICENSE)\n\n## Installation 📥\n\n1. Install via composer\n\n    ```sh\n    composer require phphd/pipeline-bundle\n    ```\n\n2. Enable the bundle in the `bundles.php`\n\n    ```php\n    PhPhD\\PipelineBundle\\PhdPipelineBundle::class =\u003e ['all' =\u003e true],\n    ```\n\n## Configuration ⚒️\n\nTo leverage chain of pipelined handlers for your command/query buses,\nyou should add `phd_pipeline.forward_chain` middleware to the list:\n\n```diff\nframework:\n    messenger:\n        buses:\n            command.bus:\n                middleware:\n                    - doctrine_transaction\n+                   - phd_pipeline.forward_chain\n                    - validation\n            query.bus:\n                middleware:\n+                   - phd_pipeline.forward_chain\n                    - validation\n```\n\n## Usage 🚀\n\nConsider having this _original message_ that is initially dispatched to the message bus:\n\n```php\nfinal readonly class CreateVacationRequestCommandDto\n{\n    public function __construct(\n        public int $userId,\n        public int $vacationTypeId,\n        #[Assert\\DateTime]\n        public string $startDate,\n        #[Assert\\DateTime]\n        public string $endDate,\n    ) {\n    }\n}\n```\n\nThy upfront message handler _returns a new message_ that will be used for subsequent redispatch:\n\n```php\n#[AsMessageHandler(bus: 'command.bus')]\nfinal readonly class ConvertVacationRequestCommandHandler\n{\n    public function __invoke(CreateVacationRequestCommandDto $dto): CreateVacationRequestCommand\n    {\n        $employee = $this-\u003eemployeeRepository-\u003efind($dto-\u003euserId);\n        $vacationType = $this-\u003evacationTypeRepository-\u003efind($dto-\u003evacationTypeId);\n\n        $vacationPeriod = VacationPeriod::fromStringDates($dto-\u003estartDate, $dto-\u003eendDate);\n\n        return new CreateVacationRequestCommand($employee, $vacationType, $vacationPeriod);\n    }\n}\n```\n\nThe _new created message_ conveys basically the same business concept, but on the _higher level of abstraction_ than\ninitially. Thereof, instead of scalar types, it has business objects (e.g. `VacationType` entity instead\nof `$vacationTypeId` scalar). Basically, new class no longer merely represents the DTO. It now embodies the complete\ndomain object.\n\nYou should add `#[NextForwarded]` _attribute to enable forwarding_ of this new message to the next handler:\n\n```php\nuse PhPhD\\Pipeline\\NextForwarded;\n\n#[NextForwarded]\nfinal readonly class CreateVacationRequestCommand\n{\n    public function __construct(\n        public Employee $employee,\n        public VacationType $vacationType,\n        public VacationPeriod $vacationPeriod,\n    ) {\n    }\n}\n```\n\n\u003e Messages lacking `#[NextForwarded]` attribute will not be forwarded. This attribute must be put on each message\n\u003e expected of redispatching.\n\nFinally, one _ultimate handler_ must implement the core business logic.\nIt may or may not return a result to the calling code.\n\n```php\n#[AsMessageHandler(bus: 'command.bus')]\nfinal readonly class CreateVacationRequestHandler\n{\n    public function __invoke(CreateVacationRequestCommand $command)\n    {\n        // The core business logic that deals with domain entities rather than primitives...\n    }\n}\n```\n\n\u003e You may chain as many message handlers as needed, even in a recursive manner,\n\u003e by returning an instance of the same class as the original message,\n\u003e provided that it has the forwarding attribute enabled.\n\n## Extended forwarding\n\nIf you don't want to use the attribute on the message class, you don't have to. There could be some cases when you'd\nlike to apply some dynamic configurations for `NextForwarded` instance. In such cases, you can return an instance of\n`NextForwarded` class right from the handler method:\n\n```php\n#[AsMessageHandler(bus: 'command.bus')]\nfinal readonly class ConvertVacationRequestCommandHandler\n{\n    /** @return NextForwarded\u003cCreateVacationRequestCommand\u003e */\n    public function __invoke(CreateVacationRequestCommandDto $dto): NextForwarded\n    {\n        return new NextForwarded($this-\u003ecreateCommandFromDto($dto));\n    }\n}\n```\n\nThe code above is no different from the one shown earlier.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphphd%2Fpipeline-bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphphd%2Fpipeline-bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphphd%2Fpipeline-bundle/lists"}