Laravel事件 event

概述

事件是一种常见的观察者模式的应用。简单的来说,就是当...干...。这个当...和干...在Laravel 事件中分别对应:
当(event)...干(listener)...
放置event和listener文件的位置分别是:
app/Events
app/Listeners

对于产品经理来说,事件主要用来规范你的业务逻辑,使支线逻辑与主线逻辑独立分拆。对于程序员来说,事件可以让Controller变得非常简洁,解耦,可维护。

定义事件(Event)

用Artisan命令可以快速生成一个模板:
php artisan event:generate

 
 
  1. <?php
  2. namespace App\Events;
  3. use App\Podcast;
  4. use App\Events\Event;
  5. use Illuminate\Queue\SerializesModels;
  6. class PodcastWasPurchased extends Event
  7. {
  8. use SerializesModels;
  9. public $podcast;
  10. /**
  11. * Create a new event instance.
  12. *
  13. * @param Podcast $podcast
  14. * @return void
  15. */
  16. public function __construct(Podcast $podcast)
  17. {
  18. $this->podcast = $podcast;
  19. }
  20. }

这样就定义了一个事件,这个事件里没有任何业务逻辑,就是一个数据传输层DTL(Data Transpotation Layer),记住这个概念,在很多设计模式中都需要涉及到。

定义事件的侦听和处理器(Listener and Handler)

你在用artisan命令生成Event的时候,对应的Listner也一并生成好了:

 
 
  1. <?php
  2. namespace App\Listeners;
  3. use App\Events\PodcastWasPurchased;
  4. use Illuminate\Queue\InteractsWithQueue;
  5. use Illuminate\Contracts\Queue\ShouldQueue;
  6. class EmailPurchaseConfirmation
  7. {
  8. /**
  9. * Create the event listener.
  10. *
  11. * @return void
  12. */
  13. public function __construct()
  14. {
  15. //
  16. }
  17. /**
  18. * Handle the event.
  19. *
  20. * @param PodcastWasPurchased $event
  21. * @return void
  22. */
  23. public function handle(PodcastWasPurchased $event)
  24. {
  25. // Access the podcast using $event->podcast...
  26. }
  27. }

handler里就是写业务逻辑的地方了,这里可以用type-hint依赖注入的方式,注入任何你需要的类。

将Event和Listener绑定并注册

这里就用到Service Provider: providers/EventServiceProvider.php 注册事件和Listener:

 
 
  1. protected $listen = [
  2. 'App\Events\PodcastWasPurchased' => [
  3. 'App\Listeners\EmailPurchaseConfirmation',
  4. ],
  5. ];

触发事件

经过上面的设置,你的事件和事件处理器就可以在controller里使用了:

 
 
  1. <?php
  2. namespace App\Http\Controllers;
  3. use Event;
  4. use App\Podcast;
  5. use App\Events\PodcastWasPurchased;
  6. use App\Http\Controllers\Controller;
  7. class UserController extends Controller
  8. {
  9. /**
  10. * Show the profile for the given user.
  11. *
  12. * @param int $userId
  13. * @param int $podcastId
  14. * @return Response
  15. */
  16. public function purchasePodcast($userId, $podcastId)
  17. {
  18. $podcast = Podcast::findOrFail($podcastId);
  19. // Purchase podcast logic...
  20. Event::fire(new PodcastWasPurchased($podcast));
  21. }
  22. }

Event::fire(new PodcastWasPurchased($podcast));就是触发事件的写法,程序运行到这里,就会触发跟这个事件绑定的listener(handler)。
Event::fire()有个辅助函数可以简写:

 
 
  1. event(new PodcastWasPurchased($podcast));

将事件加入队列

如果要处理的事件很多,那么会影响当前进程的执行效率,这时我们需要把事件加入队列,让它延迟异步执行。

定义队列执行是在Listener那里定义的:

 
 
  1. <?php
  2. namespace App\Listeners;
  3. use App\Events\PodcastWasPurchased;
  4. use Illuminate\Queue\InteractsWithQueue;
  5. use Illuminate\Contracts\Queue\ShouldQueue;
  6. class EmailPurchaseConfirmation implements ShouldQueue
  7. {
  8. //
  9. }

只要implements ShouldQueue一下就好了。

如果你想手动指定一下任务延迟执行的时间:

 
 
  1. <?php
  2. namespace App\Listeners;
  3. use App\Events\PodcastWasPurchased;
  4. use Illuminate\Queue\InteractsWithQueue;
  5. use Illuminate\Contracts\Queue\ShouldQueue;
  6. class EmailPurchaseConfirmation implements ShouldQueue
  7. {
  8. use InteractsWithQueue;
  9. public function handle(PodcastWasPurchased $event)
  10. {
  11. if (true) {
  12. $this->release(10);
  13. }
  14. }
  15. }

触发后延迟10秒执行。

事件订阅(Event Subscribers)

Event Subscribers是一种特殊的Listener,前面讲的是一个listener里只能放一个hander(),事件订阅可以把很多处理器(handler)放到一个类里面,然后用一个listner把它们集合起来,这样不同的事件只要对应一个listner就可以了。

 
 
  1. <?php
  2. namespace App\Listeners;
  3. class UserEventListener
  4. {
  5. /**
  6. * Handle user login events.
  7. */
  8. public function onUserLogin($event) {}
  9. /**
  10. * Handle user logout events.
  11. */
  12. public function onUserLogout($event) {}
  13. /**
  14. * Register the listeners for the subscriber.
  15. *
  16. * @param Illuminate\Events\Dispatcher $events
  17. * @return array
  18. */
  19. public function subscribe($events)
  20. {
  21. $events->listen(
  22. 'App\Events\UserLoggedIn',
  23. 'App\Listeners\UserEventListener@onUserLogin'
  24. );
  25. $events->listen(
  26. 'App\Events\UserLoggedOut',
  27. 'App\Listeners\UserEventListener@onUserLogout'
  28. );
  29. }
  30. }

看后面的subscribe(),每个事件和处理器是一一对应的。

绑定 Event Subscriber到Service Provider

 
 
  1. <?php
  2. namespace App\Providers;
  3. use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
  4. use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
  5. class EventServiceProvider extends ServiceProvider
  6. {
  7. /**
  8. * The event listener mappings for the application.
  9. *
  10. * @var array
  11. */
  12. protected $listen = [
  13. //
  14. ];
  15. /**
  16. * The subscriber classes to register.
  17. *
  18. * @var array
  19. */
  20. protected $subscribe = [
  21. 'App\Listeners\UserEventListener',
  22. ];
  23. }

究竟为什么要使用Event

使用Event一段时间后,你可以觉得比较麻烦,想知道到底有什么好处。
假设创建一个类 Event, 那么$event->sendWelcomeMessage($user) 这样去使用, 和用观察者模式的事件有啥区别,观察者模式好处在哪里?

首先你要明白,事件是一种『钩子』,Fire事件的位置就是放置钩子的地方。而上面那种写法是直接嵌入的,没有钩子,也就是说,上面的写法没有事件的概念,事件是不用管你怎么做的,事件只定义发生了什么事(当...时),这样就可以解耦。

区别就在于,在主逻辑线上的事件,没有做任何事情,它只是说有这样一件事,对于这件事,你可以做点事情,也可以什么都不做。而$event->sendWelcomeMessage($user)这种写法就是hardcoding了,到了那个地方必须发生sendWelcomeMessage这个行为。

作为团队的一个leader,你可以把主逻辑定义后,然后在主逻辑线上设计事件节点,然后把具体怎么处理这些事件的事务交给团队里的成员去做,成员根本不用管主逻辑和插入事件(钩子)的地方,成员只用写触发事件时要处理的逻辑就可以了。

这样是不是很方便合理啊,如果把所有处理逻辑都写在Event类里面,那多人处理的时候岂不是要同时修改一个文件,这样就会有版本冲突问题。

另外Event还可以异步队列执行,这也是好处之一。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值