<article><h1>Laravel 的事件系统</h1><ul><li><a href="#introduction">简介</a></li><li><a href="#registering-events-and-listeners">注册事件与监听器</a><ul><li><a href="#generating-events-and-listeners">生成事件与监听器</a></li><li><a href="#manually-registering-events">手动注册事件</a></li></ul></li><li><a href="#defining-events">定义事件</a></li><li><a href="#defining-listeners">定义监听器</a></li><li><a href="#queued-event-listeners">队列化事件监听器</a><ul><li><a href="#manually-accessing-the-queue">手动访问队列</a></li><li><a href="#handling-failed-jobs">处理失败任务</a></li></ul></li><li><a href="#dispatching-events">触发事件</a></li><li><a href="#event-subscribers">事件订阅者</a><ul><li><a href="#writing-event-subscribers">编写事件订阅者</a></li><li><a href="#registering-event-subscribers">注册事件订阅者</a></li></ul></li></ul><p><a name="introduction"></a></p><h2><a href="#introduction">简介</a></h2><p>Laravel 事件机制实现了一个简单的观察者模式,让我们可以订阅和监听应用中出现的各种事件。事件类 (Event) 类通常保存在 <code class=" language-php">app<span class="token operator">/</span>Events</code> 目录下,而它们的监听类 (Listener) 类被保存在 <code class=" language-php">app<span class="token operator">/</span>Listeners</code> 目录下。如果你在应用中看不到这些文件夹也不要担心,因为当你使用 Artisan 命令来生成事件和监听器时他们会被自动创建。</p><p>事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器。例如,每次把用户的订单发完货后都希望给他发个 Slack 通知。这时候你可以发起一个 <code class=" language-php">OrderShipped</code> 事件,它会被监听器接收到再传递给 Slack 通知模块,这样你就不用把订单处理的代码跟 Slack 通知的代码耦合在一起了。</p><p><a name="registering-events-and-listeners"></a></p><h2><a href="#registering-events-and-listeners">注册事件和监听器</a></h2><p>Laravel 应用中的 <code class=" language-php">EventServiceProvider</code> 提供了一个很方便的地方来注册所有的事件监听器。它的 <code class=" language-php">listen</code> 属性是一个数组,包含所有的事件(键)以及事件对应的监听器(值)。你也可以根据应用需求来增加事件到这个数组中。例如,增加一个 <code class=" language-php">OrderShipped</code> 事件:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 应用程序的事件监听器映射。
*
* @var array
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$listen</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token string">'App\Events\OrderShipped'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span>
<span class="token string">'App\Listeners\SendShipmentNotification'</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre><p><a name="generating-events-and-listeners"></a></p><h3>生成事件和监听器</h3><p>当然,手动创建每个事件和监听器是很麻烦的。简单的方式是,在 <code class=" language-php">EventServiceProvider</code> 类中添加好事件和监听器,然后使用 <code class=" language-php">event<span class="token punctuation">:</span>generate</code> 命令。这个命令会自动生成 <code class=" language-php">EventServiceProvider</code> 类中列出的所有事件和监听器。当然已经存在的事件和监听器将保持不变:</p><pre class=" language-php"><code class=" language-php">php artisan event<span class="token punctuation">:</span>generate</code></pre><p><a name="manually-registering-events"></a></p><h3>手动注册事件</h3><p>一般来说,事件必须通过 <code class=" language-php">EventServiceProvider</code> 类的 <code class=" language-php"><span class="token variable">$listen</span></code> 数组进行注册;不过,你也可以在 <code class=" language-php">EventServiceProvider</code> 类的 <code class=" language-php">boot</code> 方法中注册闭包事件。</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 注册应用程序中的任何其他事件。
*
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">boot<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token scope"><span class="token keyword">parent</span><span class="token punctuation">::</span></span><span class="token function">boot<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token scope">Event<span class="token punctuation">::</span></span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'event.name'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$foo</span><span class="token punctuation">,</span> <span class="token variable">$bar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><h4>通配符事件监听器</h4><p>你甚至可以在注册监听器时使用 <code class=" language-php"><span class="token operator">*</span></code> 通配符参数,它让你在一个监听器中可以监听到多个事件。通配符监听器接受的第一个参数是事件名称,第二个参数是整个的事件数据:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Event<span class="token punctuation">::</span></span><span class="token function">listen<span class="token punctuation">(</span></span><span class="token string">'event.*'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$eventName</span><span class="token punctuation">,</span> <span class="token keyword">array</span> <span class="token variable">$data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="defining-events"></a></p><h2><a href="#defining-events">定义事件</a></h2><p>事件类就是一个包含与事件相关信息数据的容器。例如,假设我们生成的 <code class=" language-php">OrderShipped</code> 事件接受一个 <a href="/docs/5.4/eloquent">Eloquent ORM</a> 对象:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Events</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Order</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>SerializesModels</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">OrderShipped</span>
<span class="token punctuation">{</span>
<span class="token keyword">use</span> <span class="token package">SerializesModels</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token variable">$order</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 创建一个事件实例。
*
* @param Order $order
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span>Order <span class="token variable">$order</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">order</span> <span class="token operator">=</span> <span class="token variable">$order</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>正如你所见,这个事件类中没有包含其它逻辑。它仅只是一个被构建的 <code class=" language-php">Order</code> 对象的容器。如果使用 PHP 的 <code class=" language-php">serialize</code> 函数对事件进行序列化,使用了 <code class=" language-php">SerializesModels</code> trait 的事件将会优雅的序列化任何的 Eloquent 模型。</p><p><a name="defining-listeners"></a></p><h2><a href="#defining-listeners">定义监听器</a></h2><p>接下来,让我们看一下例子中事件的监听器。事件监听器在 <code class=" language-php">handle</code> 方法中接受了事件实例作为参数。 <code class=" language-php">event<span class="token punctuation">:</span>generate</code> 命令将会在事件的 <code class=" language-php">handle</code> 方法中自动加载正确的事件类和类型提示。在 <code class=" language-php">handle</code> 方法中,你可以运行任何需要响应该事件的业务逻辑。</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">SendShipmentNotification</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 创建事件监听器。
*
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 处理事件
*
* @param OrderShipped $event
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">handle<span class="token punctuation">(</span></span>OrderShipped <span class="token variable">$event</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 使用 $event->order 来访问 order ...
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 你的事件监听器也可以在构造函数中对任何依赖使用类型提示。所有的事件监听器会经由 Laravel 的 <a href="/docs/5.4/container">服务容器</a> 做解析,所以所有的依赖都将会被自动注入:</p></blockquote><h4>停止事件传播</h4><p>有时,你可能希望停止一个事件传播到其他的监听器。这时你可以通过在监听器的 <code class=" language-php">handle</code> 方法中返回 <code class=" language-php"><span class="token boolean">false</span></code> 来实现。</p><p><a name="queued-event-listeners"></a></p><h2><a href="#queued-event-listeners">队列化事件监听器</a></h2><p>如果你的监听器中需要实现一些耗时的任务,比如发送邮件或者进行 HTTP 请求,那把它放到队列中处理是非常有用的。在使用队列化监听器之前,一定要在服务器或者本地环境中配置 <a href="/docs/5.4/queues">队列</a> 并开启一个队列监听器。</p><p>要对监听器进行队列化的话,只需增加 <code class=" language-php">ShouldQueue</code> 接口到你的监听器类。由 Artisan 命令 <code class=" language-php">event<span class="token punctuation">:</span>generate</code> 生成的监听器已经将此接口导入到命名空间了,因此你可以直接使用它:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>ShouldQueue</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">SendShipmentNotification</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldQueue</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span><span class="token punctuation">}</span></code></pre><p>就这样!当事件被监听器调用时, 事件分发器会使用 Laravel 的 <a href="/docs/5.4/queues">队列系统</a> 自动将它进行队列化。如果监听器通过队列运行且没有抛出任何异常,则已执行完的任务将会自动从队列中删除。</p><h4>自定义队列的连接和名称</h4><p>如果你想要自定义队列的连接和名称,你可以在监听器类中定义 <code class=" language-php"><span class="token variable">$connection</span></code> 和 <code class=" language-php"><span class="token variable">$queue</span></code> 属性。</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>ShouldQueue</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">SendShipmentNotification</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldQueue</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 队列化任务使用的连接名称。
*
* @var string|null
*/</span>
<span class="token keyword">public</span> <span class="token variable">$connection</span> <span class="token operator">=</span> <span class="token string">'sqs'</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 队列化任务使用的队列名称。
*
* @var string|null
*/</span>
<span class="token keyword">public</span> <span class="token variable">$queue</span> <span class="token operator">=</span> <span class="token string">'listeners'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p><a name="manually-accessing-the-queue"></a></p><h3>手动访问队列</h3><p>如果你需要手动访问底层队列任务的 <code class=" language-php">delete</code> 和 <code class=" language-php">release</code> 方法,你可以使用 <code class=" language-php">Illuminate\<span class="token package">Queue<span class="token punctuation">\</span>InteractsWithQueue</span></code> trait 来实现。这个 trait 在生成的监听器中是默认加载的,它提供了这些方法:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>InteractsWithQueue</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>ShouldQueue</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">SendShipmentNotification</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldQueue</span>
<span class="token punctuation">{</span>
<span class="token keyword">use</span> <span class="token package">InteractsWithQueue</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">handle<span class="token punctuation">(</span></span>OrderShipped <span class="token variable">$event</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">release<span class="token punctuation">(</span></span><span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p><a name="handling-failed-jobs"></a></p><h3>处理失败任务</h3><p>有时你队列化的事件监听器可能失败了。如果队列监听器任务执行次数超过在工作队列中定义的最大尝试次数,监听器的 <code class=" language-php">failed</code> 方法将会被自动调用。 <code class=" language-php">failed</code> 方法接受事件实例和失败的异常作为参数:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>InteractsWithQueue</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Contracts<span class="token punctuation">\</span>Queue<span class="token punctuation">\</span>ShouldQueue</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">SendShipmentNotification</span> <span class="token keyword">implements</span> <span class="token class-name">ShouldQueue</span>
<span class="token punctuation">{</span>
<span class="token keyword">use</span> <span class="token package">InteractsWithQueue</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">handle<span class="token punctuation">(</span></span>OrderShipped <span class="token variable">$event</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">failed<span class="token punctuation">(</span></span>OrderShipped <span class="token variable">$event</span><span class="token punctuation">,</span> <span class="token variable">$exception</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p><a name="dispatching-events"></a></p><h2><a href="#dispatching-events">触发事件</a></h2><p>如果要触发事件,你可以传递一个事件实例给 <code class=" language-php">event</code> 辅助函数。这个函数将会把事件分发到它所有已经注册的监听器上。因为 <code class=" language-php">event</code> 函数是全局可访问的,所以你可以在应用中的任何地方调用它:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Order</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Events<span class="token punctuation">\</span>OrderShipped</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">OrderController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 将传递过来的订单发货。
*
* @param int $orderId
* @return Response
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">ship<span class="token punctuation">(</span></span><span class="token variable">$orderId</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token variable">$order</span> <span class="token operator">=</span> <span class="token scope">Order<span class="token punctuation">::</span></span><span class="token function">findOrFail<span class="token punctuation">(</span></span><span class="token variable">$orderId</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true"> // 订单的发货逻辑...
</span>
<span class="token function">event<span class="token punctuation">(</span></span><span class="token keyword">new</span> <span class="token class-name">OrderShipped</span><span class="token punctuation">(</span><span class="token variable">$order</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 测试时,不用真的触发监听器就能断言事件类型是很有用的。 Laravel <a href="/docs/5.4/mocking#mocking-events">内置的测试辅助方法</a> 能让这件事变得很容易。</p></blockquote><p><a name="event-subscribers"></a></p><h2><a href="#event-subscribers">事件订阅者</a></h2><p><a name="writing-event-subscribers"></a></p><h3>编写事件订阅者</h3><p>事件订阅者是一个在自身内部可以订阅多个事件的类,允许你在单个类中定义多个事件处理器。订阅者应该定义一个 <code class=" language-php">subscribe</code> 方法,这个方法接受一个事件分发器的实例。你可以调用事件分发器的 <code class=" language-php">listen</code> 方法来注册事件监听器:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Listeners</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">UserEventSubscriber</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 处理用户登录事件。
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">onUserLogin<span class="token punctuation">(</span></span><span class="token variable">$event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 处理用户注销事件。
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">onUserLogout<span class="token punctuation">(</span></span><span class="token variable">$event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token comment" spellcheck="true">/**
* 为订阅者注册监听器。
*
* @param Illuminate\Events\Dispatcher $events
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">subscribe<span class="token punctuation">(</span></span><span class="token variable">$events</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token variable">$events</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">listen<span class="token punctuation">(</span></span>
<span class="token string">'Illuminate\Auth\Events\Login'</span><span class="token punctuation">,</span>
<span class="token string">'App\Listeners\UserEventSubscriber@onUserLogin'</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$events</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">listen<span class="token punctuation">(</span></span>
<span class="token string">'Illuminate\Auth\Events\Logout'</span><span class="token punctuation">,</span>
<span class="token string">'App\Listeners\UserEventSubscriber@onUserLogout'</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p><a name="registering-event-subscribers"></a></p><h3>注册事件订阅者</h3><p>一旦订阅者被定义,它就可以被注册到事件分发器中。你可以在 <code class=" language-php">EventServiceProvider</code> 类的 <code class=" language-php"><span class="token variable">$subscribe</span></code> 属性注册订阅者。例如,添加 <code class=" language-php">UserEventSubscriber</code> 到列表中:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Providers</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Foundation<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Providers<span class="token punctuation">\</span>EventServiceProvider</span> <span class="token keyword">as</span> ServiceProvider<span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">EventServiceProvider</span> <span class="token keyword">extends</span> <span class="token class-name">ServiceProvider</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 应用中事件监听器的映射。
*
* @var array
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$listen</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 需要注册的订阅者类。
*
* @var array
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$subscribe</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token string">'App\Listeners\UserEventSubscriber'</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre></article>
- 入门指南
- 安装
- 配置信息
- 文件夹结构
- 请求周期
- 开发环境部署
- Valet
- Homestead
- 核心概念
- 服务提供者
- Facades
- Contracts
- 服务容器
- HTTP 层
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- Session
- 表单验证
- 前端
- Blade 模板
- 本地化
- 前端指南
- 编辑资源 Mix
- 安全
- API 认证
- 用户认证
- 用户授权
- 加密解密
- 哈希
- 重置密码
- 数据库
- 快速入门
- 查询构造器
- 分页
- 数据库迁移
- Redis
- 数据填充
- Eloquent ORM
- Eloquent ORM快速入门
- 模型关联
- Eloquent 集合
- 修改器
- 序列化
- 综合话题
- Artisan 命令行
- 广播系统
- 缓存系统
- 集合
- 错误与日志
- 事件系统
- 文件存储
- 辅助函数
- 邮件发送
- 消息通知
- 扩展包开发
- 队列
- 任务调度