mikemix / tactician-module
Laminas/Mezzio Module to use the League of Extraordinary Packages' Tactician library - flexible command bus implementation
Installs: 140 783
Dependents: 0
Suggesters: 0
Security: 0
Stars: 16
Watchers: 3
Forks: 10
Open Issues: 0
Requires
- php: ^7.1 || ^8.0
- league/tactician: ^1.0
- psr/container: ^1.0 || ^2.0
Requires (Dev)
- doctrine/orm: ^2.5
- laminas/laminas-mvc: ^3.1.1
- league/tactician-doctrine: ^1.0
- phpunit/phpunit: ^7.5.20 || ^9.3.8
- squizlabs/php_codesniffer: ^3.6
This package is auto-updated.
Last update: 2024-12-04 11:23:22 UTC
README
Wrapper module for easy use of the Tactician Command Bus in your Laminas or Mezzio applications.
Installation
Best install with Composer:
composer require mikemix/tactician-module
Register as Laminas Framework module inside your config/application.config.php
file using TacticianModule
name.
You can also use this package as Mezzio module by TacticianModule\ConfigProvider
Using
The module presents a Controller Plugin called tacticianCommandBus()
for easy use of dispatching commands. If no command object is passed to it, the CommandBus object will be returned. If you pass the command however, it will be passed over to the CommandBus and handled, and the output from the handler will be returned.
You can type hint this plugin in your controller, for example: @method \League\Tactician\CommandBus|mixed tacticianCommandBus(object $command)
.
// Real life example. // Namespaces, imports, class properties skipped for brevity class LoginController extends AbstractActionController { public function indexAction() { if ($this->request->isPost()) { $this->form->setData($this->request->getPost()); if ($this->form->isValid()) { $command = new UserLoginCommand( $this->form->getLogin(), $this->form->getPassword() )); try { $this->tacticianCommandBus($command); return $this->redirect()->toRoute('home'); } catch (\Some\Kind\Of\Login\Failure $exception) { $this->flashMessenger()->addErrorMessage($exception->getMessage()); return $this->redirect()->refresh(); } } } $view = new ViewModel(); $view->setVariable('form', $this->form); $view->setTemplate('app/login/index'); return $view; } } final class UserLoginCommand { public function __construct($login, $password) { $this->login = $login; $this->password = $password; } } final class UserLoginHandler { // constructor skipped for brevity public function handle(UserLoginCommand $command) { $this->authenticationService->login($command->username, $command->password); } }
You can inject the CommandBus
into yout service layer through a factory by simply requesting the League\Tactician\CommandBus::class
from the Container.
Configuring
The module ships with a LaminasLocator
and a CommandHandlerMiddleware
and a HandlerInflector
configured as default. If you wish to override the default locator or default command bus implementations, then simply use the tactician
key in the merged config.
'tactician' => [ 'default-extractor' => League\Tactician\Handler\CommandNameExtractor\ClassNameExtractor::class, 'default-locator' => TacticianModule\Locator\LaminasLocator::class, 'default-inflector' => League\Tactician\Handler\HandleInflector::class, 'handler-map' => [], 'middleware' => [ CommandHandlerMiddleware::class => 0, ], ],
default-extractor
, default-locator
and default-inflector
are service manager keys to registered services.
LaminasLocator
expects handlers in the handler-map
to be commandName => serviceManagerKey
or commandName => Handler_FQCN
, altough to prevent additional costly checks, use serviceManagerKey and make sure it is available as a Laminas Service.
To add custom middleware to the middleware stack, add it to the middleware
array as ServiceName
=> priority
in which the middleware are supposed to be executed (higher the number, earlier it will execute). For example
// ... your module config 'tactician' => [ 'middleware' => [ YourAnotherMiddleware::class => 100, // execute early YourCustomMiddleware::class => 50, // execute last ], ],
Basic usage
Basicly, all you probably will want to do, is to define the handler-map
array in your module's configuration. For example:
// module.config.php file return [ // other keys 'tactician' => [ 'handler-map' => [ App\Command\SomeCommand::class => App\Handler\SomeCommandHandler::class, ], ], ];
Plugins
LockingMiddleware
The LockingMiddleware can now be used out of the box.
Simply add the League\Tactician\Plugins\LockingMiddleware
FQCN to the TacticianModule's middleware configuration with
appropriate priority. You probably want to execute it before the CommandHandlerMiddleware
:
// module.config.php file return [ // other keys 'tactician' => [ 'middleware' => [ \League\Tactician\Plugins\LockingMiddleware::class => 500, ], ], ];
TransactionMiddleware
The TransactionMiddleware can now be used out of the box.
Simply add the League\Tactician\Doctrine\ORM\TransactionMiddleware
FQCN to the TacticianModule's middleware configuration with
appropriate priority. You probably want to execute it before the CommandHandlerMiddleware
and after the LockingMiddleware
:
// module.config.php file return [ // other keys 'tactician' => [ 'middleware' => [ \League\Tactician\Doctrine\ORM\TransactionMiddleware::class => 250, ], ], ];
Changing the Handler Locator
ClassnameLaminasLocator
This locator simply appends the word Handler
to the command's FQCN so you don't have to define any handler map. For example, if you request command App\Commands\LoginCommand
, locator will try to get App\Command\LoginCommandHandler
from the Service Manager.
Locator will work with FQCN's not registered in the Service Manager, altough to prevent additional costly checks, make sure the locator is registered as a invokable or factory.
To change the locator from LaminasLocator to ClassnameLaminasLocator simply set it in the config:
// ... your module config 'tactician' => [ 'default-locator' => TacticianModule\Locator\ClassnameLaminasLocator::class, ],