laravel 事件监听队列化全过程
laravel 的事件提供了一种简单的观察者实现。它允许你在应用中进行订阅和监听事件。事件类通常都是存储在 app/Events
目录中,而他们的监听者都是存储在 app/Listeners
目录中。
注册事件/监听者
EventServiceProvider
提供了一个注册所有事件监听者的方便的场所。它的 listen
属性包含了一个所有事件(keys)以及他们的监听者(values)所组成的数组。当然,你可以在这个数组中添加任何你需要的事件。比如,让我们添加 PodcastWasPurchased
事件:
/** * The event listener mappiings for the application. * * @var array */ protected $listen = [ 'App\Events\PodcastWasPurchased' => [ 'App\Listeners\EmailPurchaseConfirmation', ], ],
生成事件/监听者类
当然,每次都手动的去创建一个事件文件和一个监听者文件是很麻烦的事情,所以,你可以在 EventServiceProvider
中添加事件和监听者然后使用 event:generate
命令来自动生成。这个命令会生成 EventServiceProvider
中的所有列出的事件和监听者,当然,已经存在的事件和监听者不会重新生成:
php artisan event:generate
手动的注册事件
事件应该被注册在 EventServiceProvider
的 $listen
数组中
<?php
namespace App\Providers;
use App\Listeners\BaseLogEventSubscriber;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\ExampleEvent' => [
'App\Listeners\ExampleListener',
],
'App\Events\UpdateSpuInfoEvent' => [
'App\Listeners\UpdateSpuInfoListener',
],
'App\Events\UpdateProductSearchIndexesEvent' => [
'App\Listeners\UpdateProductSearchIndexesListener',
],
'App\Events\UpdateSpuStatusEvent' => [
'App\Listeners\UpdateSpuStatusListener',
],
];
protected $subscribe = [
BaseLogEventSubscriber::class,
];
}
事件监听通配符
你可以在注册监听器的时候使用 *
来作为通配符。这允许你来在同一个监听器中监听多种事件。通配符监听器接收整个事件数据数组作为第一个参数:
$events->listen('event.*', function (array $data) {
//
});
定义事件
一个事件类只是简单的数据存储器,它应该持有事件相关的信息.比如,让我们假设我们生成的 PodcastWasPurchased
事件应该接收一个 Eloquent ORM 对象:
<?php namespace App\Events; use App\podcast; use App\Events\Event; use Illuminate\Queue\SerializesModels; class PodcastWasPurchased extends Event { use SerializesModels; public $podcast; /** * Create a new event instance. * * @param Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } }
就如你所看到的,这个事件类并没有包含什么业务逻辑。它只是简单的包含了一个被购买的 Podcast
对象。SerializesModels
trait 被用来序列化 Eloquent 模型,如果事件对象是使用 PHP 的 serialize
方法被进行序列化,那么 SerializesModels
就会优雅的将其内部的 Eloquent 对象序列化。
定义监听者
接着,让我们来看一下针对我们上面举例的事件的监听者。事件监听者会在其 handle
方法中接收事件的实例。event:generate
命令会自动的在 handle
方法中引入其对应的事件的类型提示。你可以在 handle
方法中来提供对事件的响应逻辑:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; class EmailPurchaseConfirmation { /** * Create the event listener. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param PodcastWasPurchased $event * @return void */ public function handle(PodcastWasPurchased $event) { // Access the podcast using $event->podcast... } }
你的事件监听者也可以在构造函数中进行类型提示来注入依赖。所有的事件监听器都是通过 laravel 的服务容器解析的。所以,它们的依赖可以被自动的注入:
use Illuminate\Contracts\Mail\Mailer; public function __construct(Mailer $mailer) { $this->mailer = $mailer; }
停止传递事件
有时候,你可能希望停止传递事件到后续的监听者中。你可以在监听者的 handle
方法中返回 false
,这样后续的监听者将不再进行事件的响应。
监听者队列化
需要对事件监听者进行队列化?没有比这更简单的了。直接添加 ShouldeQueue
接口到监听者类中就可以了。通过 event:generate
Artisan 命令生成的监听者已经在当前的命名空间中添加了对这个接口的支持,所以你立即就可以使用:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue {
/** * 任务应该发送到的队列的连接的名称 (可以不写,然后通过QUEUE_CONNECTION来配置) * * @var string|null */ public $connection = 'redis'; /** * 任务应该发送到的队列的名称 (默认为defalut) * * @var string|null */ public $queue = 'listeners';
}
// 可以在redis中通过 redis-cli keys "queues*"来查看
就这么简单!当事件触发时,事件分发器会使用 laravel 的队列系统对监听器进行自动的队列化调用。如果监听器队列化执行的过程中没有异常出现,队列任务会自动的在监听器进程完成后进行删除。
手动的访问队列
如果你需要手动的访问底层队列任务的 delete
和 release
方法。那么你就可以直接去使用它们。Illuminate\Queue\InteractsWithQueue
trait 对默认生成的监听器提供了访问这些方法的权限:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue { use InteractsWithQueue; public function handle(PodcastWasPurchased $event) { if (true) { $this->release(30); } } }
触发事件
你可以使用 Event
假面来进行事件的触发,你需要传递一个事件实例到 fire
方法中。fire
方法会分发事件到所有的监听器中:
<?php namespace App\Http\Controllers; use Event; use App\Podcast; use App\Events\PodcastWasPurchased; use App\Http\Controllers\Controller; class UserController extends Controller { /** * Show the profile for the given user. * * @param int $userId * @param int $podcastId * @return Response */ public function purchasePodcast($userId, $podcastId) { $podcast = Podcast::findOrFail($podcastId); // Purchase podcast logic... Event::fire(new PodcastWasPurchased($podcast)); } }
此外,你还可以通过使用全局 event
帮助函数来触发事件:
event( new PodcastWasPurchased($podcast));