Service decoupling through events and event listeners


brief introduction

Larave events provide a simple observer mode implementation, allowing you to subscribe to and listen to events in the application. Event classes are usually stored in app/Events Directory where listeners are stored app/Listeners If you don't see these directories in the application, don't worry, they will be created automatically when you use Artisan commands to generate events and listeners.

Events provide an effective solution to the decoupling of application function modules, because a single event can have multiple listeners and these listeners are not interdependent. For example, you may want to send a Slack notification to the user every time an order is sent. After the event is generated, you don't need to couple the order processing code and the Slack notification code, but simply trigger a Slack notification that can be received and processed by the listener OrderShipped Event.

Register event/listener

Larravel's own EventServiceProvider It provides a convenient place for event listener registration. Of which listen Property contains an array of events (keys) and corresponding listeners (values). If the application needs, you can add multiple events to the array. Let's add a OrderShipped event:

 /** *Applied event listener mapping * * @var array * @translator laravelacademy.org */ protected $listen = [ 'App\Events\OrderShipped' => [ 'App\Listeners\SendShipmentNotification', ], ];

Generate event/listener class

Of course, manually creating files for each event and listener is cumbersome. Instead, we simply add listeners and events to EventServiceProvider Then run event:generate Command. This command will generate a list in EventServiceProvider All events and listeners in. Of course, existing events and listeners will not be created repeatedly:

 php artisan event:generate

Manually register events

Usually, we need to EventServiceProvider Of $listen Array registration events. In addition, you can also register events in the EventServiceProvider Of boot Manually register closure based events in the method:

 /** *Register other events of the application * * @return void */ public function boot() { parent::boot(); Event::listen('event.name', function ($foo, $bar) { // }); }

Wildcard Event Listener

You can even use wildcards * To register listeners, so that multiple events can be captured through the same listener. The wildcard listener receives the entire event data array as a parameter:

 $events->listen('event.*', function ($eventName, array $data) { // });

Define Events

The event class is a simple data container for processing event related data. For example, suppose we generate OrderShipped Event receives a Eloquent ORM Object:

 <? php namespace App\Events; use App\Order; use Illuminate\Queue\SerializesModels; class OrderShipped { use SerializesModels; public $order; /** *Create a new event instance * * @param  Order  $order * @return void */ public function __construct(Order $order) { $this->order = $order; } }

As you can see, this event class does not contain any specific logic, but only stores the purchased Order Object container. If the event object is serialized, the SerializesModels Trait will use PHP's serialize Function to serialize all Eloquent models.

Define Listener

Next, let's take a look at the listener of the sample event handle Method to receive an event instance, event:generate The command will automatically handle Method to import the corresponding event class and type prompt event. stay handle Method, you can execute any required logic to respond to events:

 <? php namespace App\Listeners; use App\Events\OrderShipped; class SendShipmentNotification { /** *Create an event listener * * @return void */ public function __construct() { // } /** *Handling events * * @param  OrderShipped  $event * @return void */ public function handle(OrderShipped $event) { //Use $event ->order to send and access orders } }
Note: The event listener can also prompt any required dependency in the constructor. All event listeners are resolved through the service container, so the dependency will be automatically injected.
Stop the event and continue to propagate

Sometimes, you want to stop the event from being propagated to other listeners handle Method false To achieve.

Event listener queue

If the listener is going to perform time-consuming tasks such as sending mail or HTTP requests, it is a good choice to put the listener on the queue. Before queuing listeners, ensure that the queue has been configured and a queue listener is started on the server or local environment.

To specify that a listener needs to be put on the queue, just let the listener class implement ShouldQueue Interface, through Artisan command event:generate The generated listener class has imported this interface into the current namespace, so you can use it directly:

 <? php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue { // }

It's that simple! When this listener is called, Larave's Queue system It is automatically pushed to the queue through the event distributor. If no exception is thrown when the listener is executed through the queue, the queue task will be automatically deleted after execution.

Custom Queue Connection&Queue Name

If you want to customize the queue connection and queue name used by the event listener, you can define them in the listener class $connection and $queue Properties:

 <? php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue { /** *The name of the connection to which the task will be pushed * * @var string|null */ public $connection = 'sqs'; /** *The name of the connection to which the task will be pushed * * @var string|null */ public $queue = 'listeners'; }

Manual access to queues

If you need to manually access the delete and release Method. In the generated listener, the default imported Illuminate\Queue\InteractsWithQueue Trait provides access to these two methods:

 <? php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue { use InteractsWithQueue; public function handle(OrderShipped $event) { if (true) { $this->release(30); } } }

Processing failed tasks

Sometimes the event listener in the queue may fail to execute. If the listener task in the queue exceeds the maximum number of attempts defined by the queue process failed The method will be called, failed Method receives the event instance and the exception that caused the failure:

 <? php namespace App\Listeners; use App\Events\OrderShipped; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class SendShipmentNotification implements ShouldQueue { use InteractsWithQueue; public function handle(OrderShipped $event) { // } public function failed(OrderShipped $event, $exception) { // } }

Distribute Events

To distribute an event, you can pass the event instance to the auxiliary function event This auxiliary function will distribute events to all registered listeners. Due to auxiliary function event It is globally valid, so it can be called anywhere in the application:

 <? php namespace App\Http\Controllers; use App\Order; use App\Events\OrderShipped; use App\Http\Controllers\Controller; class OrderController extends Controller { /** *Process the given order * * @param  int  $orderId * @return Response */ public function ship($orderId) { $order = Order::findOrFail($orderId); //Order Processing Logic event(new OrderShipped($order)); } }
Note: When testing, it is only necessary to assert that specific events are distributed without actually triggering the listener, Laravel Built in test function Make this easy.

Event Subscriber

Write event subscriber

Event subscribers refer to those classes that subscribe to multiple events in the class itself. Through event subscribers, you can define multiple event handlers in a single class. The subscriber needs to define a subscribe Method, in which an event distributor instance is passed in. You can call the listen Method to register an event listener:

 <? php namespace App\Listeners; class UserEventSubscriber { /** *Handle user login events * @translator laravelacademy.org */ public function onUserLogin($event) {} /** *Handle the user exit event */ public function onUserLogout($event) {} /** *Register listeners for subscribers * * @param  Illuminate\Events\Dispatcher  $events */ public function subscribe($events) { $events->listen( 'Illuminate\Auth\Events\Login', 'App\Listeners\ UserEventSubscriber@onUserLogin ' ); $events->listen( 'Illuminate\Auth\Events\Logout', 'App\Listeners\ UserEventSubscriber@onUserLogout ' ); } }

Register event subscribers

After the subscriber is written, you can register the subscriber through the event distributor. You can use EventServiceProvider Provided $subcribe Property to register subscribers. For example, let's add a UserEventSubscriber

 <? php namespace App\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider { /** *Applied event listener mapping * * @var array */ protected $listen = [ // ]; /** *Subscriber class to register * * @var array */ protected $subscribe = [ 'App\Listeners\UserEventSubscriber', ]; }

give the thumbs-up Cancel Like Collection Cancel Collection

<<Previous: Use Artisan to build powerful console applications

>>Next: Implementation and use of Laravel queue system