laraviet / commander
Laravel 中的命令和领域事件
Requires
- php: >=5.4.0
- illuminate/filesystem: ~4.2
- illuminate/support: ~4.2
- mustache/mustache: ~2.4
Requires (Dev)
- codeception/codeception: ~2.0.0
- phpspec/phpspec: ~2.0.0
This package is auto-updated.
Last update: 2024-08-27 16:39:59 UTC
README
此包为您提供了一种简单的方法,在您的 Laravel 项目中利用命令和领域事件。
安装
按照惯例,通过 Composer 安装 Commander。
"require": { "laraviet/commander": "~2.*" }
接下来,更新 app/config/app.php
以在 providers 数组中包含对包的 service provider 的引用。
'providers' => [ 'Laracasts\Commander\CommanderServiceProvider' ]
用法
我可以提供的最重要的建议就是记住这种方法并不适用于所有情况。如果您正在构建一个没有太多业务逻辑的简单 CRUD 应用程序,那么您可能不需要这个。还想继续吗?好的 - 我们继续!
目标
想象一下,您正在构建一个广告职位列表的应用程序。现在,当雇主发布一个新职位列表时,需要发生很多事情,对吧?好吧,不要把所有这些内容都放入您的控制器中!相反,让我们利用命令、处理程序和领域事件来清理我们的代码。
控制器
首先,我们可以将此包的 CommanderTrait
注入到您的控制器中(或者如果您愿意,可以是一个 BaseController)。这将为您提供一些辅助方法来管理将命令传递到命令总线的过程。
<?php use Laracasts\Commander\CommanderTrait; class JobsController extends \BaseController { use CommanderTrait; /** * Publish the new job listing. * * @return Response */ public function store() { } }
好的?接下来,我们将这个“指令”(发布职位列表)表示为一个命令。这将仅仅是一个简单的 DTO。
<?php use Laracasts\Commander\CommanderTrait; use Acme\Jobs\PostJobListingCommand; class JobsController extends \BaseController { use CommanderTrait; /** * Post the new job listing. * * @return Response */ public function store() { $this->execute(PostJobListingCommand::class); return Redirect::home(); }
注意我们如何将用户的指令(或命令)表示为可读的类:PostJobListingCommand
。execute
方法将期望命令的类路径,作为一个字符串。上面,我们使用有用的 PostJobListingCommand::class
来获取它。或者,您也可以手动将路径作为字符串写出。
命令 DTO
很简单,对吧?我们创建一个命令来表示指令,然后将这个命令扔到命令总线中。下面是这个命令可能的样子
<?php namespace Acme\Jobs; use Laracasts\Commander\Laraviet\BaseArrayCommand; class PostJobListingCommand extends BaseArrayCommand { }
当您在
CommanderTrait
上调用execute
方法时,它将自动将Input::all()
的数据映射到您的命令。您不需要手动做这些。
那么命令总线到底做了什么?把它想象成一个简单的工具,它将这个命令转换为一个相关的处理程序类,而这个类将处理命令!在这个例子中,这意味着根据需要委托发布新职位列表。
默认情况下,命令总线将对命令类的名称进行快速搜索和替换,以确定要从 IoC 容器中解析哪个处理程序类。因此
- PostJobListingCommand => PostJobListingCommandHandler
- ArchiveJobCommand => ArchiveJobCommandHandler
明白了吗?好。但是请注意,如果您更喜欢不同的命名约定,可以覆盖默认值。见下文。
装饰命令总线
可能有些时候您想装饰命令总线以首先执行某种操作...也许您需要首先清理一些数据。这很简单。首先,创建一个实现 Laracasts\Commander\CommandBus
协议的类...
<?php namespace Acme\Jobs; use Laracasts\Commander\CommandBus; class JobSanitizer implements CommandBus { public function execute($command) { // sanitize the job data } }
...然后,当您在控制器中执行命令时,引用此类。
$this->execute(PostJobListingCommand::class, null, [ 'JobSanitizer' ]);
就是这样!现在,您有一个钩子,可以在将命令和数据传递给处理程序类之前对其进行清理。关于这一点...
处理程序类
让我们现在创建我们的第一个处理程序类
<?php namespace Acme\Jobs; use Laracasts\Commander\CommandHandler; use Laracasts\Commander\Events\DispatchableTrait; class PostJobListingCommandHandler implements CommandHandler { use DispatchableTrait; public function handle($command) { $job = Job::post($command->$data); $this->dispatchEventsFor($job); return $job; } }
对于这个演示,我们的处理器相当简单。在实际生活中,这里将会有更多的事情发生。注意那个dispatchEventsFor
方法?这个方法将处理为实体触发所有队列事件的流程。这样,应用的其他部分可以监听任务发布的事件,并相应地做出反应。
触发事件
以下是一个快速简单的示例,说明Job
模型可能的样子
<?php namespace Acme\Jobs; use Laracasts\Commander\Events\EventGenerator; use Acme\Jobs\Events\JobWasPublished; class Job extends \Eloquent { use EventGenerator; protected $fillable = ['title', 'description']; public static function post($data) { // Persistence data $job = static::create($data); $job->raise(new JobWasPublished($job)); return $job; } }
请密切注意我们触发事件的位置。
$job->raise(new JobWasPublished($job));
再次强调,这个JobWasPublished
对象只是一个简单的传输对象。
<?php namespace Acme\Jobs\Events; use Acme\Jobs\Job; class JobWasPublished { public $job; public function __construct(Job $job) /* or pass in just the relevant fields */ { $this->job = $job; } }
此外,raise
方法通过EventGenerator
特性行使。它只是将事件存储在数组中。
事件监听器
现在,由于处理器类已分发所有事件,这意味着你可以注册任意数量的监听器。要监听的事件名称遵循简单的约定
- Path\To\Raised\Event => Path.To.Raised.Event
所以,基本上,我们用点替换斜杠,使其看起来更具面向对象。所以,如果我们发布
- Acme\Jobs\Events\JobWasPublished
那么,要监听的事件将是
- Acme.Jobs.Events.JobWasPublished
现在,让我们注册一个基本的事件监听器来发送电子邮件。
Event::listen('Acme.Jobs.Events.JobWasPublished', function($event) { var_dump('Send a notification email to the job creator.'); });
记住:你可以为同一事件注册多个监听器。也许你还需要在任务发布时执行一些与报告相关的事情。那么,添加一个新的事件监听器!
上面的示例使用了一个简单的闭包。如果你想,可以采用更“通配符”的方法,这个包可以帮到你。
首先,让我们设置一个EmailNotifier
类,它将有机会处理我们应用的所有触发事件。
Event::listen('Acme.*', 'Acme\Listeners\EmailNotifier');
因此,现在,每当你在Acme
命名空间中触发事件时,一旦分发,EmailNotifier
类的handle方法就会触发。当然,我们不需要对每个事件都做出反应!只需要几个。那么,再次强调,我们可以遵循简单的方法命名约定,只响应我们感兴趣的特定事件。
JobWasPublished
事件类将在事件监听器上寻找whenJobWasPublished
方法。如果存在,它将调用它。否则,它将简单地继续。这意味着我们的EmailNotifier
类可能看起来是这样的
<?php namespace Acme\Listeners; use Laracasts\Commander\Events\EventListener; use Acme\Jobs\Events\JobWasPublished; class EmailNotifier extends EventListener { public function whenJobWasPublished(JobWasPublished $event) { var_dump('send an email'); } }
因为这个类扩展了EventListener
,父类将管理确定是否调用whenJobWasPublished
的所有细节。
文件生成
你可能会发现自己需要手动创建大量的命令和处理类。相反,使用这个包中包含的Artisan命令!只需运行
php artisan commander:generate Acme/Bar/SubscribeUserCommand
这将生成SubscribeUserCommand
和SubscribeUserCommandHandler
类。默认情况下,它将在“app/”中的“Acme”目录内查找。如果你的基本域名目录在其他地方,请传递--base="src"
。
命令
<?php namespace Acme\Bar; class SubscribeUserCommand { /** * Constructor */ public function __construct() { } }
处理程序
<?php namespace Acme\Bar; use Laracasts\Commander\CommandHandler; class SubscribeUserCommandHandler implements CommandHandler { /** * Handle the command. * * @param object $command * @return void */ public function handle($command) { } }
或者,如果你想为属性生成样板代码,你也可以这样做。
php artisan commander:generate Acme/Bar/SubscribeUserCommand --properties="first, last"
当添加--properties
标志时,处理类将保持不变,但是命令本身将被搭建,如下所示
<?php namespace Acme\Bar; use Laracasts\Commander\Laraviet\BaseArrayCommand; class SubscribeUserCommand extends BaseArrayCommand { }
很酷,对吧?这将为你节省大量时间,所以请记住使用它。
在调用此命令时,使用斜杠作为类路径:
Acme/Bar/MyCommand
。如果你愿意使用反斜杠,则需要将其放在引号内。