ignaciocastro0713 / cqbus-mediator
A Mediator for Laravel
Requires
- php: ^8.1
- illuminate/container: ^9.0 || ^10.0 || ^11.0 || ^12.0
- illuminate/contracts: ^9.0 || ^10.0 || ^11.0 || ^12.0
- illuminate/filesystem: ^9.0 || ^10.0 || ^11.0 || ^12.0
- illuminate/pipeline: ^9.0 || ^10.0 || ^11.0 || ^12.0
- illuminate/support: ^9.0 || ^10.0 || ^11.0 || ^12.0
- spatie/php-structure-discoverer: ^2.3
- symfony/finder: ^6.0 || ^7.0
README
A simple, extensible, and configurable Command/Query Bus (Mediator) implementation for Laravel applications. This package helps you implement the Command/Query Responsibility Segregation (CQRS) and Mediator patterns, promoting cleaner, more maintainable code by separating concerns and decoupling components.
✨ Features
-
Attribute-Based Handler Discovery: Define your command/query handlers using a simple PHP 8 attribute (
#[RequestHandler]
). -
Configuration-Driven Scanning: Easily configure the directories and namespace mappings where your handlers are located via a dedicated configuration file.
-
Automatic Dependency Injection: Handlers are resolved from the Laravel service container, allowing for seamless dependency injection.
-
Clear Separation of Concerns: Decouples the sender from the handlers, improving testability and code organization.
-
Global pipelines (middleware) support: that will apply to every request sent through the Mediator
🚀 Installation
You can install this package via Composer.
- Require the Package: In your Laravel project's root directory, run:
composer require ignaciocastro0713/cqbus-mediator
- Publish the Configuration File: The package comes with a configurable file that allows you to define handler discovery paths. Publish it using the Artisan command:
php artisan vendor:publish --tag=mediator-config
This will create config/mediator.php
in your Laravel application.
⚙️ Configuration (config/mediator.php
)
After publishing, you'll find a mediator.php
file in your config
directory. This file is crucial for discovers your handlers.
<?php return [ /* |-------------------------------------------------------------------------- | Handler Discovery Paths |-------------------------------------------------------------------------- | | This array defines the directories where the MediatorService will scan | for command/request handlers. These paths are relative to your Laravel | application's base directory (typically the 'app' directory). | | Example: app_path('Handlers/Commands') would scan 'app/Handlers/Commands'. | */ 'handler_paths' => [ app_path('Handlers'), // A common directory for all handlers ], /* |-------------------------------------------------------------------------- | Global Pipelines |-------------------------------------------------------------------------- | | The global pipelines (middleware) that will be applied to every request | sent through the Mediator. Each class should have a handle($request, Closure $next) method. | | Example: | App\Pipelines\LoggingMiddleware::class, | App\Pipelines\AuthMiddleware::class, */ 'pipelines' => [ ], ];
Important: Adjust handler_paths to include all directories where your command/query handlers are located.
🚀 Usage
- Define your Command/Query (Request) A request is a simple DTO (Data Transfer Object) that encapsulates the data needed for an operation.
<?php namespace App\Handlers\Users\Queries\GetUsers; // Example: app/Handlers/Users/Queries/GetUsers/GetUsersQuery.php class GetUsersQuery { public function __construct(public ?string $search = null) { // } }
- Define your Handler
Create a handler class that will process your command/query. This class must have a public
handle
method and be decorated with the#[RequestHandler]
attribute.
<?php namespace App\Handlers\Users\Queries\GetUsers; use Ignaciocastro0713\CqbusMediator\Attributes\RequestHandler; // Example: app/Handlers/Users/Queries/GetUsers/GetUsersQueryHandler.php #[RequestHandler(GetUsersQuery::class)] class GetUsersQueryHandler { // You can inject dependencies here, e.g., a UserRepository public function __construct() { // } public function handle(GetUsersQuery $query) { // Your logic to retrieve users based on $query->search // This is where you would interact with your database, services, etc. if ($query->search) { return ['Filtered User for: ' . $query->search]; } return ['User 1', 'User 2', 'User 3']; } }
- send the Command/Query
You can inject the
Mediator
interface into your controllers, services, or anywhere you need to send a command or query.
<?php namespace App\Http\Controllers; use App\Handlers\Users\Queries\GetUsers\GetUsersQuery; use Ignaciocastro0713\CqbusMediator\Contracts\Mediator; // Import the Mediator interface use Illuminate\Http\Request; use Illuminate\Routing\Controller; // Use Illuminate\Routing\Controller class UserController extends Controller { public function __construct(private readonly Mediator $mediator) { // The Mediator instance is automatically injected by Laravel } public function index(Request $request) { $query = new GetUsersQuery($request->input('search')); // send the query to its handler $users = $this->mediator->send($query); return response()->json($users); } // Example for a Command (assuming you have a CreateUserCommand and CreateUserCommandHandler) /* public function store(Request $request) { $command = new CreateUserCommand($request->input('name'), $request->input('email')); $result = $this->mediator->send($command); return response()->json($result, 201); } */ }
How to use global pipelines
-
Define your middleware class:
The middleware should implement ahandle($request, \Closure $next)
method.namespace App\Pipelines; class LoggingPipeline { public function handle($request, \Closure $next) { \Log::info('Mediator request received', ['request' => $request]); return $next($request); } }
-
Register your middleware in
config/mediator.php
:return [ 'handler_paths' => [ app_path('Handlers') ], 'pipelines' => [ App\Pipelines\LoggingPipeline::class, // Add more middleware classes here ], ];
🤝 Contributing
Feel free to open issues or submit pull requests on the GitHub repository.
📄 License
This package is open-sourced software licensed under the MIT license.