laracasts/commander

Laravel中的命令和领域事件

安装: 310 206

依赖项: 13

建议者: 1

安全: 0

星标: 281

关注者: 23

分支: 68

开放问题: 24

1.4.3 2014-12-11 19:23 UTC

This package is auto-updated.

Last update: 2024-08-29 03:16:10 UTC


README

此包为您提供了一个简单的方式来利用命令和领域事件在您的Laravel项目中。

Laravel 5用户:此包不再需要,因为框架自带灵活的命令总线。

安装

像往常一样,通过Composer安装Commander。

"require": {
    "laracasts/commander": "~1.0"
}

接下来,更新 app/config/app.php,在提供者数组中包含对该包服务提供者的引用。

'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();
	}

注意我们如何将用户的指令(或命令)表示为可读的类:PostJobListingCommandexecute方法将期望命令的类路径,作为字符串。上面,我们使用有用的PostJobListingCommand::class来获取这个路径。或者,您也可以手动将路径作为字符串写出。

命令DTO

很简单,对吧?我们创建一个命令来表示指令,然后将该命令扔入命令总线。下面是这个命令可能的样子

<?php namespace Acme\Jobs;

class PostJobListingCommand {

    public $title;

    public $description;

    public function __construct($title, $description)
    {
        $this->title = $title;
        $this->description = $description;
    }

}

当您在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->title, $command->description);

        $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($title, $description)
    {
        // We're ignoring persistence for this demo
        $job = new static(compact('title', 'description'));

        $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 特性来访问。它只是将事件存储在一个数组中。

事件监听器

现在,由于处理程序类派发了所有事件,这意味着你可以注册任意数量的监听器。要监听的事件名称遵循以下简单的约定

  • 路径\到\引发的事件 => 路径.到.引发的事件

所以,本质上,我们用点替换斜杠,使其看起来更面向对象一点。所以,如果我们引发

  • 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

这将生成 SubscribeUserCommandSubscribeUserCommandHandler 类。默认情况下,它将在 "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;

class SubscribeUserCommand {

    /**
     * @var string
     */
    public $first;

    /**
     * @var string
     */
    public $last;

    /**
     * Constructor
     *
     * @param string first
     * @param string last
     */
    public function __construct($first, $last)
    {
        $this->first = $first;
        $this->last = $last;
    }

}

不错吧?这会节省你很多时间,所以请记得使用它。

调用此命令时,请使用斜杠作为类路径的分隔符:Acme/Bar/MyCommand。如果你更喜欢使用反斜杠,则需要将其放在引号内。

这就完成了!

阅读这些东西可能会很复杂。请务必查看 Laracasts 上的 Commands and Domain Events 系列来了解更多关于这些内容。