Download the PHP package etel/cqrs without Composer

On this page you can find all versions of the php package etel/cqrs. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package cqrs

CI Status codecov Latest Stable Version License PHP Version Require

Etel CQRS Component

Contracts and a Symfony Messenger-based implementation of the CQRS pattern for PHP 8.4+.

The library ships three buses — Command, Query and Event — each following the same layering, and lets a handler's result type be declared once, on the message, so that:

Table of contents

Concepts

The package is split into two layers:

Layer Namespace Dependencies
Contracts Etel\CQRS\{Command,Query,Event}\ None — pure PHP
Implementation Etel\CQRS\{Command,Query,Event}\Implementation\ symfony/messenger

The contracts layer has no external dependencies; symfony/messenger is a suggest only. Depend on the contracts from your domain and only pull the implementation in at the composition root. This keeps your code testable against the interfaces and lets you swap the transport without touching handlers.

The three buses differ by intent, following Command/Query Separation:

Bus Method Result Handlers
CommandBus command() Optional (fire-and-forget by default) Exactly one
QueryBus query() Always returned (synchronous) Exactly one
EventBus publish() None (void) Zero or more

Requirements

Installation

To use the Messenger-based implementation, also require Messenger:

Wiring with Symfony Messenger

Each bus is a thin decorator over a Messenger MessageBusInterface. Configure one Messenger bus per CQRS bus and register the decorators.

Middleware order matters. The options middleware runs first (it emits the ValidationStamp/DelayStamp that later steps rely on); the input transformer runs after validation, so raw input is validated before it becomes the final message.

Mark handlers so the container can autoconfigure them (the library markers are optional but convenient):

AsCommandHandler, AsQueryHandler and AsEventHandler are plain class markers; wire them to a compiler pass or use them alongside Messenger's own #[AsMessageHandler].

Commands

A command expresses intent to change state. By default it is fire-and-forget.

When you need the handler's result synchronously (e.g. a freshly created id), declare it with #[CommandResult] — see Declaring & checking handler results.

Queries

A query reads state and always returns a result synchronously (a query that only enqueues work is a command).

A nullable result (e.g. "not found") is declared with nullable: true:

Events

An event is a domain fact that already happened. It may have zero or more handlers and never returns a result. Events carry no validation layer by design — their data is guaranteed valid at the point of emission.

Declaring & checking handler results

The #[QueryResult] / #[CommandResult] attributes declare a message's result once, on the message class. The bus reads them to verify the handler's return value at runtime, and a bundled PHPStan extension uses them to infer the dispatch call's return type — so you never repeat the FQCN at the call site.

Queries

A query always returns; the attribute pins the type:

Attribute query() return type Runtime check
#[QueryResult(Foo::class)] Foo Result must be a Foo, otherwise throws
#[QueryResult(Foo::class, nullable: true)] Foo\|null Foo or null
#[QueryResult] mixed none
no attribute mixed none

Commands

A command is fire-and-forget unless it opts in. Omitting the attribute means "returns nothing" (void / async / always-void handler) — that is the canonical way to say a command has no result:

Attribute Semantics command() return type
no attribute Void / fire-and-forget null
#[CommandResult(Foo::class)] Synchronous result of type Foo Foo (or Foo\|null with nullable: true)
#[CommandResult] A result is expected, but not a class (e.g. an id) mixed

Runtime guarantees

When a result type is declared, the bus throws if the handler violates it:

$expectResult: the per-call escape hatch

Both command() and query() still accept an explicit $expectResult argument. The attribute is the recommended, declarative source of truth; $expectResult exists for cases the attribute cannot cover:

An explicit class-string in $expectResult takes precedence over the attribute.

Inputs: raw data before validation

When a message must contain valid, strongly-typed data (non-nullable properties, enums/VOs instead of raw strings/ints), use an intermediate input DTO. CommandInput / QueryInput hold raw data and expose toCommand() / toQuery(), which either return the final typed message or throw if the data is invalid. The transformer middleware performs this swap after validation, preserving all stamps.

Events have no input equivalent: event data is always valid at emission.

Per-bus options

Each bus takes its own typed options value object instead of raw Messenger stamps. Buses know nothing about each other's options.

Option VO Fields
CommandBusOptions int $delayMs = 0, ?list<string> $validationGroups = null
QueryBusOptions ?list<string> $validationGroups = null
EventBusOptions int $delayMs = 0, bool $dispatchAfterCurrentBus = false

The matching *BusOptionsMiddleware translates these into native Messenger stamps (DelayStamp, ValidationStamp, DispatchAfterCurrentBusStamp).

Exceptions

You catch interfaces (in the contracts layer); the implementation throws concrete classes that implement them.

Interface Thrown when
InvalidCommandData / InvalidQueryData Input passed validation but is still invalid for some reason
UnexpectedCommandPropertyValue / UnexpectedQueryPropertyValue A validated property still holds an unexpected value
InvalidCommandReturnConfiguration / InvalidQueryReturnConfiguration A result is expected but the message reached no synchronous handler
UnexpectedCommandResult / UnexpectedQueryResult The handler returned a value that does not match the declared result type

Static analysis (PHPStan)

The package bundles a PHPStan extension that infers query() / command() return types from the result attributes:

Call Inferred type
query(new GetUserById(1)) with #[QueryResult(UserView::class)] UserView
query(new FindUser(...)) with #[QueryResult(UserView::class, nullable: true)] UserView\|null
query($q, expectResult: UserView::class) UserView
command(new CreateUser(...)) with #[CommandResult(CreatedUser::class)] CreatedUser
command($c) (no attribute) null

If you use phpstan/extension-installer the extension is registered automatically. Otherwise include it manually:

PhpStorm note. The extension drives PHPStan; it cannot drive PhpStorm's own type inference. The explicit $expectResult: Foo::class form is understood by PhpStorm natively; attribute-based inference is a PHPStan feature.

Development

This project targets PHP 8.4.

License

Released under the MIT License.


All versions of cqrs with dependencies

PHP Build Version
Package Version
Requires php Version ^8.4
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package etel/cqrs contains the following files

Loading the files please wait ...