code-mine / tactician-module
ZF2 模块,用于使用 League of Extraordinary Packages 的 Tactician 库 - 灵活的命令总线实现
Requires
- php: ^5.5 || ^7.0
- league/tactician: ^1.0
- zendframework/zend-mvc: ^3.0.3
- zendframework/zend-servicemanager: ^3.1.1
Requires (Dev)
- league/tactician-doctrine: ^1.0
- phpunit/phpunit: ^4.8
README
包装模块,方便在您的 ZF2 应用程序中使用 Tactician 命令总线。
安装
最佳安装方式为 Composer
composer require mikemix/tactician-module
在您的 config/application.config.php
文件中将它注册为 ZF2 模块
'modules' => [ 'YourApplicationModule', 'TacticianModule', ],
使用
该模块提供了一个名为 Controller Plugin 的 tacticianCommandBus()
,用于轻松分发命令。如果没有传递命令对象,将返回 CommandBus 对象。如果您传递了命令,它将被传递到 CommandBus 并处理,并返回处理程序的输出。
您可以在控制器中为该插件添加类型提示,例如:@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); } }
如果您需要将命令总线注入到服务层或类似的地方,只需从 Service Manager 中使用 CommandBus
的 FQCN 即可。
<?php namespace MyNamespace; use League\Tactician\CommandBus; use Zend\ServiceManager\ServiceManager; use MyNamespace\Service\MyService; class Module { public function getServiceConfig() { return [ 'factories' => [ MyService::class => function(ServiceManager $serviceManager) { $commandBus = $serviceManager->get(CommandBus::class); return new MyService($commandBus); }, ], ]; } }
配置
该模块自带一个 ZendLocator
、一个 CommandHandlerMiddleware
和一个 HandlerInflector
,默认配置。如果您想覆盖默认的定位器或默认的命令总线实现,只需在合并的配置中使用 tactician
键。
'tactician' => [ 'default-extractor' => League\Tactician\Handler\CommandNameExtractor\ClassNameExtractor::class, 'default-locator' => TacticianModule\Locator\ZendLocator::class, 'default-inflector' => League\Tactician\Handler\HandleInflector::class, 'handler-map' => [], 'middleware' => [ CommandHandlerMiddleware::class => 0, ], ],
default-extractor
、default-locator
和 default-inflector
是服务管理器键,用于注册服务。
ZendLocator
期望 handler-map
中的处理程序在 commandName => serviceManagerKey
或 commandName => Handler_FQCN
中,尽管为了防止额外的成本检查,使用 serviceManagerKey 并确保它作为 Zend 服务可用。
要向中间件堆栈添加自定义中间件,将其添加到 middleware
数组中,作为 ServiceName
=> priority
,其中中间件应执行(数字越高,执行越早)。例如
// ... your module config 'tactician' => [ 'middleware' => [ YourCustomMiddleware::class => -100, // execute last YourAnotherMiddleware::class => 100, // execute early ], ],
基本用法
基本上,您可能想要做的所有事情,就是定义您的模块配置中的 handler-map
数组。例如
// module.config.php file return [ // other keys 'tactician' => [ 'handler-map' => [ App\Command\SomeCommand::class => App\Handler\SomeCommandHandler::class, ], ], ];
插件
锁定中间件
现在可以直接使用 锁定中间件。只需将 League\Tactician\Plugins\LockingMiddleware
FQCN 添加到 TacticianModule 的中间件配置中,并设置适当的优先级。您可能希望在其之前执行它。
// module.config.php file return [ // other keys 'tactician' => [ 'middleware' => [ \League\Tactician\Plugins\LockingMiddleware::class => 500, ], ], ];
事务中间件
现在可以直接使用 事务中间件。只需将 League\Tactician\Doctrine\ORM\TransactionMiddleware
FQCN 添加到 TacticianModule 的中间件配置中,并设置适当的优先级。您可能希望在其之前执行它,并在 LockingMiddleware
之后执行它。
// module.config.php file return [ // other keys 'tactician' => [ 'middleware' => [ \League\Tactician\Doctrine\ORM\TransactionMiddleware::class => 250, ], ], ];
更改处理程序定位器
ClassnameZendLocator
此定位器简单地将单词 Handler
添加到命令的 FQCN,这样您就不必定义任何处理程序映射。例如,如果您请求命令 App\Commands\LoginCommand
,定位器将尝试从服务管理器获取 App\Command\LoginCommandHandler
。
定位器可以与未在服务管理器中注册的 FQCN 一起使用,尽管为了防止额外的成本检查,请确保定位器已注册为可调用或工厂。
要将定位器从 ZendLocator 更改为 ClassnameZendLocator,只需在配置中设置即可。
// ... your module config 'tactician' => [ 'default-locator' => TacticianModule\Locator\ClassnameZendLocator::class, ],