的的 -> 的
hldh214
4年前
修改理由:
相关信息:
- 类型:文档文章
- 文章: 事件系统
- 文档: 《Laravel 5.5 中文文档(5.5)》
此投稿已在 4年前 合并。
内容修改:
Old | New | Differences |
---|---|---|
1 | # Laravel 的事件系统 | |
1 | # Laravel 的事件系统 | |
2 | ||
3 | - [简介](#introduction) | |
4 | - [注册事件与监听器](#registering-events-and-listeners) | |
5 | - [生成事件 & 监听器](#generating-events-and-listeners) | |
6 | - [手动注册事件](#manually-registering-events) | |
7 | - [定义事件](#defining-events) | |
8 | - [定义监听器](#defining-listeners) | |
9 | - [事件监听器队列](#queued-event-listeners) | |
10 | - [手动访问队列](#manually-accessing-the-queue) | |
11 | - [处理失败任务](#handling-failed-jobs) | |
12 | - [分发事件](#dispatching-events) | |
13 | - [事件订阅者](#event-subscribers) | |
14 | - [编写事件订阅者](#writing-event-subscribers) | |
15 | - [注册事件订阅者](#registering-event-subscribers) | |
16 | ||
17 | <a name="introduction"></a> | |
18 | ## 简介 | |
19 | ||
20 | Laravel 的事件提供了一个简单的观察者实现,能够订阅和监听应用中发生的各种事件。事件类保存在 `app/Events` 目录中,而这些事件的监听器则被保存在 `app/Listeners` 目录下。这些目录只有当你使用 Artisan 命令来生成事件和监听器时才会被自动创建。 | |
21 | ||
22 | 事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器。例如,如果你希望每次订单发货时向用户发送一个 Slack 通知。你可以简单地发起一个 `OrderShipped` 事件,让监听器接收之后转化成一个 Slack 通知,这样你就可以不用把订单的业务代码跟 Slack 通知的代码耦合在一起了。 | |
23 | ||
24 | <a name="registering-events-and-listeners"></a> | |
25 | ## 注册事件和监听器 | |
26 | ||
27 | Laravel 应用中的 `EventServiceProvider` 有个 `listen` 数组包含所有的事件(键)以及事件对应的监听器(值)来注册所有的事件监听器,可以灵活地根据需求来添加事件。例如,让我们增加一个 `OrderShipped` 事件: | |
28 | ||
29 | ```` | |
30 | /** | |
31 | * 应用程序的事件监听器映射。 | |
32 | * | |
33 | * @var array | |
34 | */ | |
35 | protected $listen = [ | |
36 | 'App\Events\OrderShipped' => [ | |
37 | 'App\Listeners\SendShipmentNotification', | |
38 | ], | |
39 | ]; | |
40 | ```` | |
41 | ||
42 | <a name="generating-events-and-listeners"></a> | |
43 | ### 生成事件 & 监听器 | |
44 | ||
45 | 为每个事件和监听器手动创建文件是件很麻烦的事情,而在这里,你只需将监听器和事件添加到 `EventServiceProvider` 中,再使用 `event:generate` 命令即可。这个命令会生成在 `EventServiceProvider` 中列出的所有事件和监听器。当然,已经存在的事件和监听器将保持不变: | |
46 | ||
47 | ```` | |
48 | php artisan event:generate | |
49 | ```` | |
50 | ||
51 | <a name="manually-registering-events"></a> | |
52 | ### 手动注册事件 | |
53 | ||
54 | 事件通常是在 `EventServiceProvider` 类的 `$listen` 数组中注册,但是,你也可以在 `EventServiceProvider` 类的 `boot` 方法中注册基于事件的闭包。 | |
55 | ||
56 | ```` | |
57 | /** | |
58 | * 注册应用程序中的任何其他事件。 | |
59 | * | |
60 | * @return void | |
61 | */ | |
62 | public function boot() | |
63 | { | |
64 | parent::boot(); | |
65 | ||
66 | Event::listen('event.name', function ($foo, $bar) { | |
67 | // | |
68 | }); | |
69 | } | |
70 | ```` | |
71 | ||
72 | #### 通配符事件监听器 | |
73 | ||
74 | 你可以在注册监听器时使用 `*` 通配符参数,这样能够在同一个监听器上捕获多个事件。通配符监听器接受事件名称作为其第一个参数,并将整个事件数据数组作为其第二个参数: | |
75 | ||
76 | ```` | |
77 | Event::listen('event.*', function ($eventName, array $data) { | |
78 | // | |
79 | }); | |
80 | ```` | |
81 | ||
82 | <a name="defining-events"></a> | |
83 | ## 定义事件 | |
84 | ||
85 | 事件类其实就只是一个保存与事件相关的信息的数据容器。例如,假设我们生成的 `OrderShipped` 事件接收一个 [Eloquent ORM](/docs/{{version}}/eloquent) 对象: | |
86 | ||
87 | ```` | |
88 | <?php | |
89 | ||
90 | namespace App\Events; | |
91 | ||
92 | use App\Order; | |
93 | use Illuminate\Queue\SerializesModels; | |
94 | ||
95 | class OrderShipped | |
96 | { | |
97 | use SerializesModels; | |
98 | ||
99 | public $order; | |
100 | ||
101 | /** | |
102 | * 创建一个事件实例。 | |
103 | * | |
104 | * @param Order $order | |
105 | * @return void | |
106 | */ | |
107 | public function __construct(Order $order) | |
108 | { | |
109 | $this->order = $order; | |
110 | } | |
111 | } | |
112 | ```` | |
113 | ||
114 | 正如你所见,这个事件类中没有包含其它逻辑。它只是一个被构建的 `Order` 对象的容器。如果使用 PHP 的 `serialize` 函数序列化事件对象,事件使用的 `SerializesModels` trait 将会优雅地序列化任何 Eloquent 模型。 | |
115 | ||
116 | <a name="defining-listeners"></a> | |
117 | ## 定义监听器 | |
118 | ||
119 | 接下来,让我们看一下例子中事件的监听器。事件监听器在 `handle` 方法中接收事件实例。 `event:generate` 命令会自动加载正确的事件类和在 `handle` 加入的类型提示。在 `handle` 方法中,你可以执行任何必要的响应事件的操作: | |
120 | ||
121 | ```` | |
122 | <?php | |
123 | ||
124 | namespace App\Listeners; | |
125 | ||
126 | use App\Events\OrderShipped; | |
127 | ||
128 | class SendShipmentNotification | |
129 | { | |
130 | /** | |
131 | * 创建事件监听器。 | |
132 | * | |
133 | * @return void | |
134 | */ | |
135 | public function __construct() | |
136 | { | |
137 | // | |
138 | } | |
139 | ||
140 | /** | |
141 | * 处理事件 | |
142 | * | |
143 | * @param OrderShipped $event | |
144 | * @return void | |
145 | */ | |
146 | public function handle(OrderShipped $event) | |
147 | { | |
148 | // 使用 $event->order 来访问 order ... | |
149 | } | |
150 | } | |
151 | ```` | |
152 | ||
153 | > {tip} 你的事件监听器也可以在构造函数中加入任何依赖关系的类型提示。所有的事件监听器都是通过 Laravel 的 [服务容器](/docs/{{version}}/container) 来解析的,因此所有的依赖都将会被自动注入。 | |
154 | ||
155 | #### 停止事件传播 | |
156 | ||
157 | 你可以通过在监听器的 `handle` 方法中返回 `false` 来阻止事件被其他的监听器获取。 | |
158 | ||
159 | <a name="queued-event-listeners"></a> | |
160 | ## 事件监听器队列 | |
161 | ||
162 | 如果你的监听器中要执行诸如发送邮件或者进行 HTTP 请求等比较慢的任务,你可以选择将其丢给队列处理。在开始使用监听器队列之前,请确保在你的服务器或本地开发环境中能够配置并启动 [队列](/docs/{{version}}/queues) 监听器。 | |
163 | ||
164 | 要指定监听器启动队列,只需将 `ShouldQueue` 接口添加到监听器类。由 Artisan 命令 `event:generate` 生成的监听器已经将此接口导入到当前命名空间中,因此你可以直接使用它: | |
165 | ||
166 | ```` | |
167 | <?php | |
168 | ||
169 | namespace App\Listeners; | |
170 | ||
171 | use App\Events\OrderShipped; | |
172 | use Illuminate\Contracts\Queue\ShouldQueue; | |
173 | ||
174 | class SendShipmentNotification implements ShouldQueue | |
175 | { | |
176 | // | |
177 | } | |
178 | ```` | |
179 | ||
180 | 当这个监听器被事件调用时,事件调度器会自动使用 Laravel 的 [队列系统](/docs/{{version}}/queues)。如果在队列中执行监听器时没有抛出异常,任务会在执行完成后自动从队列中删除。 | |
181 | ||
182 | #### 自定义队列的连接和名称 | |
183 | ||
184 | 如果你想要自定义事件监听器使用的队列的连接和名称,可以在监听器类中定义 `$connection` 和 `$queue` 属性。 | |
185 | ||
186 | ```` | |
187 | <?php | |
188 | ||
189 | namespace App\Listeners; | |
190 | ||
191 | use App\Events\OrderShipped; | |
192 | use Illuminate\Contracts\Queue\ShouldQueue; | |
193 | ||
194 | class SendShipmentNotification implements ShouldQueue | |
195 | { | |
196 | /** | |
197 | * 任务应该发送到的队列的连接的名称 | |
198 | * | |
199 | * @var string|null | |
200 | */ | |
201 | public $connection = 'sqs'; | |
202 | ||
203 | /** | |
204 | * 任务应该发送到的队列的名称 | |
205 | * | |
206 | * @var string|null | |
207 | */ | |
208 | public $queue = 'listeners'; | |
209 | } | |
210 | ```` | |
211 | ||
212 | <a name="manually-accessing-the-queue"></a> | |
213 | ### 手动访问队列 | |
214 | ||
215 | 如果你需要手动访问监听器下面队列任务的 `delete` 和 `release` 方法,你可以添加 `Illuminate\Queue\InteractsWithQueue` trait 来实现。这个 trait 会默认加载到生成的监听器中,并提供对这些方法的访问: | |
216 | ||
217 | ```` | |
218 | <?php | |
219 | ||
220 | namespace App\Listeners; | |
221 | ||
222 | use App\Events\OrderShipped; | |
223 | use Illuminate\Queue\InteractsWithQueue; | |
224 | use Illuminate\Contracts\Queue\ShouldQueue; | |
225 | ||
226 | class SendShipmentNotification implements ShouldQueue | |
227 | { | |
228 | use InteractsWithQueue; | |
229 | ||
230 | /** | |
231 | * Handle the event. | |
232 | * | |
233 | * @param \App\Events\OrderShipped $event | |
234 | * @return void | |
235 | */ | |
236 | public function handle(OrderShipped $event) | |
237 | { | |
238 | if (true) { | |
239 | $this->release(30); | |
240 | } | |
241 | } | |
242 | ```` | |
243 | ||
244 | <a name="handling-failed-jobs"></a> | |
245 | ### 处理失败任务 | |
246 | ||
247 | 事件监听器的队列任务可能会失败,而如果监听器的队列任务超过了队列中定义的最大尝试次数,则会监听器上调用 `failed` 方法。`failed` 方法接受接收事件实例和导致失败的异常作为参数: | |
248 | ||
249 | ```` | |
250 | <?php | |
251 | ||
252 | namespace App\Listeners; | |
253 | ||
254 | use App\Events\OrderShipped; | |
255 | use Illuminate\Queue\InteractsWithQueue; | |
256 | use Illuminate\Contracts\Queue\ShouldQueue; | |
257 | ||
258 | class SendShipmentNotification implements ShouldQueue | |
259 | { | |
260 | use InteractsWithQueue; | |
261 | ||
262 | /** | |
263 | * 处理事件 | |
264 | * | |
265 | * @param \App\Events\OrderShipped $event | |
266 | * @return void | |
267 | */ | |
268 | public function handle(OrderShipped $event) | |
269 | { | |
270 | // | |
271 | } | |
272 | ||
273 | /** | |
274 | * 处理任务失败 | |
275 | * | |
276 | * @param \App\Events\OrderShipped $event | |
277 | * @param \Exception $exception | |
278 | * @return void | |
279 | */ | |
280 | public function failed(OrderShipped $event, $exception) | |
281 | { | |
282 | // | |
283 | } | |
284 | } | |
285 | ```` | |
286 | ||
287 | <a name="dispatching-events"></a> | |
288 | ## 分发事件 | |
289 | ||
290 | 如果要分发事件,你可以将事件实例传递给辅助函数 `event`。这个函数将会把事件分发到所有已经注册的监听器上。因为辅助函数 `event` 是全局可访问的,所以你可以在应用中的任何地方调用它: | |
291 | ||
292 | ```` | |
293 | <?php | |
294 | ||
295 | namespace App\Http\Controllers; | |
296 | ||
297 | use App\Order; | |
298 | use App\Events\OrderShipped; | |
299 | use App\Http\Controllers\Controller; | |
300 | ||
301 | class OrderController extends Controller | |
302 | { | |
303 | /** | |
304 | * 将传递过来的订单发货。 | |
305 | * | |
306 | * @param int $orderId | |
307 | * @return Response | |
308 | */ | |
309 | public function ship($orderId) | |
310 | { | |
311 | $order = Order::findOrFail($orderId); | |
312 | ||
313 | // 订单的发货逻辑... | |
314 | ||
315 | event(new OrderShipped($order)); | |
316 | } | |
317 | } | |
318 | ```` | |
319 | ||
320 | > {tip} 在测试时,Laravel [内置的测试辅助函数](/docs/{{version}}/mocking#mocking-events) 能不需要实际触发监听器就能对事件类型断言。 | |
321 | ||
322 | <a name="event-subscribers"></a> | |
323 | ## 事件订阅者 | |
324 | ||
325 | <a name="writing-event-subscribers"></a> | |
326 | ### 编写事件订阅者 | |
327 | ||
328 | 事件订阅者是一个可以在自身内部订阅多个事件的类,即能够在单个类中定义多个事件处理器。订阅者应该定义一个 `subscribe` 方法,这个方法接受一个事件分发器的实例。你可以调用给定的事件分发器上的 `listen` 方法来注册事件监听器: | |
329 | ||
330 | ```` | |
331 | <?php | |
332 | ||
333 | namespace App\Listeners; | |
334 | ||
335 | class UserEventSubscriber | |
336 | { | |
337 | /** | |
338 | * 处理用户登录事件。 | |
339 | */ | |
340 | public function onUserLogin($event) {} | |
341 | ||
342 | /** | |
343 | * 处理用户注销事件。 | |
344 | */ | |
345 | public function onUserLogout($event) {} | |
346 | ||
347 | /** | |
348 | * 为订阅者注册监听器。 | |
349 | * | |
350 | * @param Illuminate\Events\Dispatcher $events | |
351 | */ | |
352 | public function subscribe($events) | |
353 | { | |
354 | $events->listen( | |
355 | 'Illuminate\Auth\Events\Login', | |
356 | 'App\Listeners\UserEventSubscriber@onUserLogin' | |
357 | ); | |
358 | ||
359 | $events->listen( | |
360 | 'Illuminate\Auth\Events\Logout', | |
361 | 'App\Listeners\UserEventSubscriber@onUserLogout' | |
362 | ); | |
363 | } | |
364 | ||
365 | } | |
366 | ```` | |
367 | ||
368 | <a name="registering-event-subscribers"></a> | |
369 | ### 注册事件订阅者 | |
370 | ||
371 | 订阅者写好后,就将其注册到事件分发器中。你可以在 `EventServiceProvider` 类的 `$subscribe` 属性中注册订阅者。例如,将 `UserEventSubscriber` 添加到数组列表中: | |
372 | ||
373 | ```` | |
374 | <?php | |
375 | ||
376 | namespace App\Providers; | |
377 | ||
378 | use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; | |
379 | ||
380 | class EventServiceProvider extends ServiceProvider | |
381 | { | |
382 | /** | |
383 | * 应用中事件监听器的映射。 | |
384 | * | |
385 | * @var array | |
386 | */ | |
387 | protected $listen = [ | |
388 | // | |
389 | ]; | |
390 | ||
391 | /** | |
392 | * 需要注册的订阅者类。 | |
393 | * | |
394 | * @var array | |
395 | */ | |
396 | protected $subscribe = [ | |
397 | 'App\Listeners\UserEventSubscriber', | |
398 | ]; | |
399 | } | |
400 | ```` | |
401 | ||
402 | ## 译者署名 | |
403 | ||
404 | | 用户名 | 头像 | 职能 | 签名 | | |
405 | |---|---|---|---| | |
406 | | [@JokerLinly](https://learnku.com/users/5350) | <img class="avatar-66 rm-style" src="https://cdn.learnku.com/uploads/avatars/5350_1481857380.jpg"> | 翻译 | Stay Hungry. Stay Foolish. | | |
407 | ||
408 | --- | |
409 | ||
410 | > {note} 欢迎任何形式的转载,但请务必注明出处,尊重他人劳动共创开源社区。 | |
411 | > | |
412 | > 转载请注明:本文档由 Laravel China 社区 [laravel-china.org](https://laravel-china.org) 组织翻译,详见 [翻译召集帖](https://learnku.com/laravel/t/5756/laravel-55-document-translation-call-come-and-join-the-translation)。 | |
413 | > | |
414 | > 文档永久地址: https://learnku.com/docs/laravel | |
2 | 415 | |
3 | ||
4 | ||
5 | ||
6 | ||
7 | ||
8 | ||
9 | ||
10 | ||
11 | ||
12 | ||
13 | ||
14 | ||
15 | ||
16 | ||
17 | ||
18 | ||
19 | ||
20 | ||
21 | ||
22 | ||
23 | ||
24 | ||
25 | ||
26 | ||
27 | ||
28 | ||
29 | ||
30 | ||
31 | ||
32 | ||
33 | ||
34 | ||
35 | ||
36 | ||
37 | ||
38 | ||
39 | ||
40 | ||
41 | ||
42 | ||
43 | ||
44 | ||
45 | ||
46 | ||
47 | ||
48 | ||
49 | ||
50 | ||
51 | ||
52 | ||
53 | ||
54 | ||
55 | ||
56 | ||
57 | ||
58 | ||
59 | ||
60 | ||
61 | ||
62 | ||
63 | ||
64 | ||
65 | ||
66 | ||
67 | ||
68 | ||
69 | ||
70 | ||
71 | ||
72 | ||
73 | ||
74 | ||
75 | ||
76 | ||
77 | ||
78 | ||
79 | ||
80 | ||
81 | ||
82 | ||
83 | ||
84 | ||
85 | ||
86 | ||
87 | ||
88 | ||
89 | ||
90 | ||
91 | ||
92 | ||
93 | ||
94 | ||
95 | ||
96 | ||
97 | ||
98 | ||
99 | ||
100 | ||
101 | ||
102 | ||
103 | ||
104 | ||
105 | ||
106 | ||
107 | ||
108 | ||
109 | ||
110 | ||
111 | ||
112 | ||
113 | ||
114 | ||
115 | ||
116 | ||
117 | ||
118 | ||
119 | ||
120 | ||
121 | ||
122 | ||
123 | ||
124 | ||
125 | ||
126 | ||
127 | ||
128 | ||
129 | ||
130 | ||
131 | ||
132 | ||
133 | ||
134 | ||
135 | ||
136 | ||
137 | ||
138 | ||
139 | ||
140 | ||
141 | ||
142 | ||
143 | ||
144 | ||
145 | ||
146 | ||
147 | ||
148 | ||
149 | ||
150 | ||
151 | ||
152 | ||
153 | ||
154 | ||
155 | ||
156 | ||
157 | ||
158 | ||
159 | ||
160 | ||
161 | ||
162 | ||
163 | ||
164 | ||
165 | ||
166 | ||
167 | ||
168 | ||
169 | ||
170 | ||
171 | ||
172 | ||
173 | ||
174 | ||
175 | ||
176 | ||
177 | ||
178 | ||
179 | ||
180 | ||
181 | ||
182 | ||
183 | ||
184 | ||
185 | ||
186 | ||
187 | ||
188 | ||
189 | ||
190 | ||
191 | ||
192 | ||
193 | ||
194 | ||
195 | ||
196 | ||
197 | ||
198 | ||
199 | ||
200 | ||
201 | ||
202 | ||
203 | ||
204 | ||
205 | ||
206 | ||
207 | ||
208 | ||
209 | ||
210 | ||
211 | ||
212 | ||
213 | ||
214 | ||
215 | ||
216 | ||
217 | ||
218 | ||
219 | ||
220 | ||
221 | ||
222 | ||
223 | ||
224 | ||
225 | ||
226 | ||
227 | ||
228 | ||
229 | ||
230 | ||
231 | ||
232 | ||
233 | ||
234 | ||
235 | ||
236 | ||
237 | ||
238 | ||
239 | ||
240 | ||
241 | ||
242 | ||
243 | ||
244 | ||
245 | ||
246 | ||
247 | ||
248 | ||
249 | ||
250 | ||
251 | ||
252 | ||
253 | ||
254 | ||
255 | ||
256 | ||
257 | ||
258 | ||
259 | ||
260 | ||
261 | ||
262 | ||
263 | ||
264 | ||
265 | ||
266 | ||
267 | ||
268 | ||
269 | ||
270 | ||
271 | ||
272 | ||
273 | ||
274 | ||
275 | ||
276 | ||
277 | ||
278 | ||
279 | ||
280 | ||
281 | ||
282 | ||
283 | ||
284 | ||
285 | ||
286 | ||
287 | ||
288 | ||
289 | ||
290 | ||
291 | ||
292 | ||
293 | ||
294 | ||
295 | ||
296 | ||
297 | ||
298 | ||
299 | ||
300 | ||
301 | ||
302 | ||
303 | ||
304 | ||
305 | ||
306 | ||
307 | ||
308 | ||
309 | ||
310 | ||
311 | ||
312 | ||
313 | ||
314 | ||
315 | ||
316 | ||
317 | ||
318 | ||
319 | ||
320 | ||
321 | ||
322 | ||
323 | ||
324 | ||
325 | ||
326 | ||
327 | ||
328 | ||
329 | ||
330 | ||
331 | ||
332 | ||
333 | ||
334 | ||
335 | ||
336 | ||
337 | ||
338 | ||
339 | ||
340 | ||
341 | ||
342 | ||
343 | ||
344 | ||
345 | ||
346 | ||
347 | ||
348 | ||
349 | ||
350 | ||
351 | ||
352 | ||
353 | ||
354 | ||
355 | ||
356 | ||
357 | ||
358 | ||
359 | ||
360 | ||
361 | ||
362 | ||
363 | ||
364 | ||
365 | ||
366 | ||
367 | ||
368 | ||
369 | ||
370 | ||
371 | ||
372 | ||
373 | ||
374 | ||
375 | ||
376 | ||
377 | ||
378 | ||
379 | ||
380 | ||
381 | ||
382 | ||
383 | ||
384 | ||
385 | ||
386 | ||
387 | ||
388 | ||
389 | ||
390 | ||
391 | ||
392 | ||
393 | ||
394 | ||
395 | ||
396 | ||
397 | ||
398 | ||
399 | ||
400 | ||
401 | ||
402 | ||
403 | ||
404 | ||
405 | ||
406 | ||
407 | ||
408 | ||
409 | ||
410 | ||
411 | ||
412 | ||
413 | ||
414 | ||
415 |