league / tactician-bundle
用于将 Tactician 集成到 Symfony 项目的软件包
Requires
- php: >=7.2
- league/tactician: ^1.0
- league/tactician-container: ^2.0|^3.0
- league/tactician-logger: ^0.10|^0.11
- symfony/config: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/dependency-injection: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/http-kernel: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/yaml: ^3.4|^4.4|^5.0|^6.0|^7.0
Requires (Dev)
- matthiasnoback/symfony-config-test: ^4.2.1
- matthiasnoback/symfony-dependency-injection-test: ^4.2.1
- mockery/mockery: ~1.0
- phpspec/prophecy: ^1.18
- phpunit/phpunit: ~8.5
- symfony/console: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/framework-bundle: ^3.4.31|^4.4|^5.0|^6.0|^7.0
- symfony/security-bundle: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/security-core: ^3.4|^4.4|^5.0|^6.0|^7.0
- symfony/validator: ^3.4|^4.4|^5.0|^6.0|^7.0
Suggests
- league/tactician-doctrine: For doctrine transaction middleware
- symfony/console: For debugging command-to-handler routing using the tactician:debug console command
- symfony/security: For command security middleware
- symfony/validator: For command validator middleware
- v1.5.1
- v1.5.0
- v1.4.0
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.18
- v1.1.17
- v1.1.16
- v1.1.5
- v1.1.4
- 1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.1-RC1
- dev-master / 1.0.x-dev
- v1.0
- v1.0-RC1
- v0.4.1
- v0.4
- v0.3.1
- v0.3
- v0.2
- v0.1.0
- dev-github-actions
- dev-symfony-5
- dev-kejwmen-feature/tactician-debug-command
- dev-bugfix/try-local-phpunit
This package is auto-updated.
Last update: 2024-08-30 01:09:29 UTC
README
Symfony2 Bundle for the Tactician library https://github.com/thephpleague/tactician/
安装
步骤 1: 下载软件包
打开命令行控制台,进入您的项目目录,然后执行以下命令以下载此软件包的最新稳定版本
$ composer require league/tactician-bundle
此命令需要您全局安装 Composer,如 Composer 文档中的安装章节中所述。
步骤 2: 启用软件包
然后,通过将其添加到项目 app/AppKernel.php
文件中注册的软件包列表中来启用该软件包
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new League\Tactician\Bundle\TacticianBundle(), ); // ... } // ... }
使用命令总线
创建一个服务并注入命令总线
services: app.your_controller: class: AppBundle\Controller\YourNameController arguments: - '@tactician.commandbus'
然后传递一个命令给命令总线以执行
<?php namespace AppBundle\Controller; use League\Tactician\CommandBus; use AppBundle\Commands\DoSomethingCommand; class YourNameController { private $commandBus; public function __construct(CommandBus $commandBus) { $this->commandBus = $commandBus; } public function doSomething() { $command = new DoSomethingCommand(); $this->commandBus->handle($command); } }
自动装配
如果启用 Symfony 自动装配功能(从 Symfony 2.8 版本开始提供),则无需为每个控制器创建服务使用默认命令总线,而是可以按如下方式注入和使用它
<?php namespace AppBundle\Controller; use League\Tactician\CommandBus; use AppBundle\Commands\DoSomethingCommand; class YourNameController { public function doSomething(CommandBus $commandBus) { $command = new DoSomethingCommand(); $commandBus->handle($command); } }
请注意,这仅适用于默认命令总线,如果您想注入非默认的总线,您可以通过别名覆盖配置
services: League\Tactician\CommandBus: '@tactician.commandbus.your_commandbus'
如果您有多个总线,您可以使用命名参数别名,使用总线名称作为参数名称的一部分。例如,如果您想注入名为 default
的命令总线,请将参数命名为 defaultBus
。语法始终遵循 {bus_name}Bus
。
此功能仅从 Symfony 4.2 版本开始提供
<?php namespace AppBundle\Controller; use League\Tactician\CommandBus; class YourNameController { public function doSomething(CommandBus $defaultBus) { // } }
配置命令处理器
当您将命令传递给 Tactician 时,最终目标是将它映射到处理器。
由于处理器通常具有额外的依赖关系并且最好是懒加载的,因此您希望将它们注册在服务容器中。
有几种不同的方法可以将您的命令映射到处理器,所有这些方法都可以组合。以下我们将一一介绍
1. 手动映射
假设我们有两个类,RegisterUserCommand
和 RegisterUserHandler
。我们将 Handler 注册在服务容器中,以及它所需的存储库。
foo.user.register_user_handler: class: Foo\User\RegisterUserHandler arguments: - '@foo.user.user_repository'
但是,我们仍然需要将 Command 映射到 Handler。我们可以通过在 Handler 的 DI 定义中添加一个标签来实现这一点。
该标签应有两个属性:标签名称,它始终应为 tactician.handler
,以及命令,它是 Command 的完全限定名称。
foo.user.register_user_handler: class: Foo\User\RegisterUserHandler arguments: - '@foo.user.user_repository' tags: - { name: tactician.handler, command: Foo\User\RegisterUserCommand }
2. 基于类型提示映射
我们不仅可以重复命令的完整类名,还可以反射处理器的方法类型提示。
foo.user.register_user_handler: class: Foo\User\RegisterUserHandler arguments: - '@foo.user.user_repository' tags: - { name: tactician.handler, typehints: true }
这通过检查类的的方法来检测处理器接收哪些命令。匹配规则如下
- 方法必须是公开的。
- 方法必须只接受一个参数。
- 参数必须使用类名进行类型提示。
换句话说,RegisterUserHandler 类应如下所示
<?php class RegisterUserHandler { public function handle(RegisterUser $command) { // do stuff } }
如果您将多个命令发送到单个处理器,只要它们遵循上述规则,它们都会被检测到。实际的方法名称并不重要。
如果您正在使用类型提示和 FQCN 映射,那么 FQCN 映射始终优先。
通过类型提示进行注册可以非常有用,如果您正在使用Symfony最新版本中的自动装配功能。
3. 自定义映射规则
如果您想为容器中自动映射命令到处理程序定义自己的规则,您也可以做到这一点。
首先,实现处理程序映射接口。在编译时,您将收到一个ContainerBuilder和一个Tactician 路由对象,您可以使用它将命令映射到您的处理程序服务。
您的策略很可能会涉及使用某种类型的容器标签。如果是这样,请考虑扩展基于标签的映射抽象类。这将为您节省一些处理多个总线相关的样板代码。
一旦您的对象准备就绪,在设置AppKernel.php时将其传递给TacticianBundle实例。
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new League\Tactician\Bundle\TacticianBundle( new My\Custom\HandlerMapping() ), ); } }
4. 组合映射策略
如果您想将多个策略链在一起,可以使用CompositeMapping对象来链接它们。
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new League\Tactician\Bundle\TacticianBundle( new League\Tactician\Bundle\DependencyInjection\HandlerMapping\CompositeMapping( new League\Tactician\Bundle\DependencyInjection\HandlerMapping\ClassNameMapping(), // standard command: "FQCN" mapping new League\Tactician\Bundle\DependencyInjection\HandlerMapping\TypeHintMapping(), // standard typehints: true mapping new My\Custom\HandlerMapping() // your custom routing ) ), ); } }
如果有多个处理程序映射策略检测到相同的命令,但不同的处理程序,那么最后提到的映射策略获胜。因此,通常最好将自定义策略放在最后或ClassNameMapping放在最后,这样您就可以在必要时进行完全覆盖。
5. 编写您自己的中间件
请记住,Tactician完全基于中间件。如果您不想处理所有这些,并且您有一个简单的基于约定的方法来映射命令到处理程序,只需编写自己的中间件来执行处理程序。
检查您的连接
您可以使用debug:tactician
命令来获取映射到哪些服务的命令列表。
配置中间件
在Tactician中,所有内容都是中间件插件。如果没有配置任何中间件,当您将命令传递给$commandBus->handle()
时,不会发生任何事情。
默认情况下,启用的唯一中间件是命令处理程序支持。您可以在app/config.yml
中覆盖此设置并添加自己的中间件。
tactician: commandbus: default: middleware: # service ids for all your middlewares, top down. First in, last out. - tactician.middleware.locking - my.custom.middleware.plugin - tactician.middleware.command_handler
重要:鼓励添加自己的中间件,但请确保始终将tactician.middleware.command_handler
作为最后的中间件添加。否则,您的命令实际上不会执行。
查看Tactician文档获取更多信息以及完整的中间件列表。
配置多个命令总线
该捆绑包预先配置了一个名为“default”的命令总线,服务ID为tactician.commandbus
。尽管如此,一些用户想要配置多个命令总线。
假设您正在将远程会计系统集成到应用程序中,并且您希望为这些命令使用单独的命令总线。您可以通过这种方式连接两个命令总线
您可以通过以下方式配置此操作
tactician: commandbus: default: # the "regular" command bus in your application middleware: - tactician.middleware.validator - tactician.middleware.command_handler accounting: # the command bus for accounting specific commands middleware: - tactician.middleware.locking - some.middleware.service.to.call.the.remote.accounting.app - tactician.commandbus.accounting.middleware.command_handler # Because "tactician.middleware.command_handler" refers to the default bus
配置定义了两个总线:“default”和“accounting”。这些总线将分别注册为tactician.commandbus.default
和tactician.commandbus.accounting
服务。
请注意,每个总线现在都已配置了自己的命令处理程序中间件:tactician.middleware.command_handler
用于默认配置,tactician.commandbus.accounting.middleware.command_handler
用于账户配置。
如果您想更改注册到tactician.commandbus
的命令处理程序,您可以通过设置配置中的default_bus
值来完成此操作
tactician: default_bus: accounting commandbus: default: middleware: # ... accounting: middleware: # ...
默认情况下,所有命令在每个总线上都是可用的。如果您想使命令仅对特定总线可用,您需要指定其ID
foo.user.register_user_handler: class: Foo\User\RegisterUserHandler arguments: - '@foo.user.user_repository' tags: - { name: tactician.handler, command: Foo\User\RegisterUserCommand, bus: accounting }
然后您将能够在会计总线上处理此命令
$bus = $container->get('tactician.commandbus.accounting'); $bus->handle(new Foo\User\RegisterUserCommand('my', 'arguments'));
额外捆绑中间件
此捆绑包包含一些预配置的中间件。要启用它们,请将它们添加到您的总线配置中的中间件列表(参见配置中间件)
验证器中间件(tactician.middleware.validator)
此中间件使用Symfony的验证器来检查在传递之前命令对象。在实践中,这意味着您可以在命令上添加任何Symfony验证器注解,以确保它在执行之前完全正确。这并不是完全替代编写内部一致风格的代码,但它非常有帮助。
约束可以通过配置或注释添加,如默认的Symfony实践,请参阅他们的文档。
如果命令失败,它将抛出League\Tactician\Bundle\Middleware\InvalidCommandException。此异常还包含由验证器产生的ConstraintViolationList,因此您可以检查或记录错误。
锁定中间件(tactician.middleware.locking)
此中间件包含在Tactician中,请参阅官方文档以获取详细信息。
记录器中间件(tactician.middleware.logger)
此中间件包含在Tactician中,请参阅官方文档以获取详细信息。
安全中间件(tactician.middleware.security)
安全中间件将对所有命令执行授权。默认情况下,如果用户未授权,将抛出AccessDenied异常。
tactician: security: My\User\Command: - 'ROLE_USER' My\Admin\Command: - 'ROLE_ADMIN' My\UserAndAdmin\Command: - 'ROLE_USER' - 'ROLE_ADMIN'
此中间件基于Symfony的AccessDecisionManager和投票系统。我们建议在使用此中间件之前熟悉它。如果您想配置更复杂的场景,考虑实现自定义的Symfony投票者。
作为预防措施,该中间件需要您在它被Symfony的AccessDecisionManager评估之前,在安全配置中注册命令。
此外,虽然安全中间件基于可信组件,但我们始终建议深度防御策略。仅仅将您的命令总线连接到公共Web端点并完全依赖此中间件可能不足以覆盖您的应用程序。
安全中间件默认禁用。
命令处理器中间件(tactician.middleware.command_handler)
始终确保这是最后一个中间件
这是将您的命令与处理器匹配并执行的实际插件。如果您有复杂的匹配逻辑,您可以自由实现自己的变体并取消此中间件。
但是,对于99%的用户,此选项应该启用,并将其设置为列表中的最后一个中间件。
自定义tactician.middleware.command_handler
中间件使用的MethodNameInflector
默认情况下,该捆绑包使用Tactician核心中的HandleInflector
。也就是说,它期望您的命令处理器有一个接收要执行的命令的handle()
方法。
但是,如果您更喜欢不同的inflector,您可以在config.yml
中传递服务名称。
tactician: method_inflector: my_inflector.service.id
Tactician核心提供了一系列自定义Inflectors,所有这些都在此捆绑包中得到支持。假设有一个名为My\App\RegisterUserCommand()的类,处理器上调用的方法将是
tactician.handler.method_name_inflector.handle
-handle()
tactician.handler.method_name_inflector.handle_class_name
-handleRegisterUserCommand()
tactician.handler.method_name_inflector.handle_class_name_without_suffix
-handleRegisterUser()
tactician.handler.method_name_inflector.invoke
-__invoke()
尽管 handle()
是一个合理的默认选项,但使用类名方法之一可以让你在单个类上处理多个命令(如果它们共享公共依赖或以某种方式相互配合,这可能很有用)。同样,如果要将函数映射到闭包列表中,则 __invoke 也可能很有用。
当使用多个总线时,您还可以指定特定总线的 method_inflector
。
tactician: commandbus: command: middleware: - tactician.middleware.command_handler query: middleware: - tactician.middleware.command_handler method_inflector: tactician.handler.method_name_inflector.handle_class_name_without_suffix
测试
$ ./vendor/bin/phpunit
安全
披露信息可以在 Tactician 主仓库 上找到。
许可证
MIT 许可证(MIT)。有关更多信息,请参阅 许可证文件。