الأحداث (Events) في Laravel

من موسوعة حسوب
اذهب إلى: تصفح، ابحث

مقدمة

توفّر أحداث Laravel تطبيق مراقب بسيط  يسمح بالإشتراك والاستماع لعدّة أحداث يطلقها التطبيق. تُخزَّن أصناف الأحداث في الدليل app/Events، في حين توجد المنصتات في app/Listeners. لا تقلق إن لم تجد هذه الدلائل في التطبيق إذ تُصنع حين تولّد أحداثًا و منصتات باستعمال أوامر artisan.

الأحداث طريقة ممتازة لفك ارتباط عدّة عناصر من التطبيق حيث يمكن أن يكون لحدث واحد عدّة منصتات منفصلة كليا عن بعضها البعض. مثلًا، قد تريد إرسال إعلان slack للمستخدم كلما شحن طلب. بدل ربط معالجة الطلبات بخدمة slack، يمكنك إطلاق حدث orderShipped وجعل منصت ينتظره ويحوله لإعلان slack.

تسجيل الأحداث و المنصتات

يوفّر مزوّد الخدمات EventServiceProvider المضمَّن في تطبيقات Laravel مكانًا ملائمًا لتسجيل كل المنصتات على الأحداث.  تحتوي الخاصية listen مصفوفة من كل الأحداث(مفاتيح) و المنصتات(قيم). طبعًا، يمكنك إضافة أي عدد من الأحداث يستحقّها التطبيق. مثلًا، فلنضف الحدث OrderShipped:
/**
*امنصتات على الأحداث.
*
* @var array
*/

protected $listen = [

   'App\Events\OrderShipped' => [
       'App\Listeners\SendShipmentNotification',
   ],
];

توليد الأحداث والمنصتات

قد يكون إنشاء الملفات يدويًّا لكل حدث ومنصت عملًا مرهقًا. بدل ذلك، أضف الأحداث والمنصتات للملف EventServiceProvider واستعمل الأمر event:generate. سيولّد هذا الأمر كل الأحداث والمنصتات المذكورة في EventServiceProvider. طبعًا، الأحداث الموجودة مسبقًا لن تتغير:
php artisan event:generate

التسجيل اليدوي للأحداث

في العادة، يجب تسجيل الأحداث في المصفوفة listen$ من الملف EventServiceProvider. لكن، يمكن أيضا تسجيل نطاق مغلق Closure للأحداث يدويًّا في التابع boot من EventServiceProvider:
/**
*تسجيل أحداث إضافية.
*
* @return void
*/

public function boot()
{

   parent::boot();
   Event::listen('event.name', function ($foo, $bar) {

      //

   });
}

منصتات الأحداث الشمولية

يمكن أيضا تسجيل منصتات الأحداث الشمولية باستعمال الرمز * كمعامل بديل. ممّا يسمح بالتقاط عدّة أحداث بمنصت واحد. تقبل المنصتات البديلة اسم حدث كمعامل أول وكامل مصفوفة بيانات الأحداث كمعامل ثاني:
Event::listen('event.*', function ($eventName, array $data) {
  //

});

تعريف الأحداث

صنف الأحداث هو حاوي بيانات يحفظ المعلومات المتعلقة بالأحداث. لنفترض مثلًا أنّ الحدث OrderShipped يتلقّى كائن رابط الكائنات بالعلاقات Eloquent:
<?php 
namespace App\Events;
use App\Order;
use Illuminate\Queue\SerializesModels;

class OrderShipped
{

   use SerializesModels;
   public $order;

   /**
    * إنشاء حدث جديد
    *
    * @param  \App\Order  $order
    * @return void
    */

   public function __construct(Order $order)
   {
       $this->order = $order;
   }
}
كما تلاحظ، لا يحتوي الصنف على أي عمل منطقي، هو فقط حامل لنسخة Order المتََحصَّل عليها. ستسلسل الخاصية SerializesModels كل نموذج Eloquent إذا تمّت سَلسَلة الحدث باستخدام دالة serialize PHP.  

تعريف المنصتات

بعد ذلك، لنلق نظرة على منصت الحدث. تتلقى المنصتات نسخة حدث في التابع handle. يُحمّل الأمر event:generate تلقائيًّا صنف الحدث الملائم ويلمح إلى نوعه للتابع. يمكنك تحديد أي عمل تريد كإجابة للحدث في هذا التابع:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;

class SendShipmentNotification

{
   /**
    * إنشاء المنصت     *
    * @return void
    */

   public function __construct()

   {
      //
   }

   /**
    * العمل على الحدث
    *
    * @param  \App\Events\OrderShipped  $event
    * @return void
    */

   public function handle(OrderShipped $event)
   {

      // ولوج الطلب

   }
}

ملاحظة : يمكن للمنصت أيضًا التلميح إلى نوع ااعتماديات التي يحتاجها التابع الباني. كل المنصتات تعالَج في حاوي خدمات Laravel لذا تُضاف الإعتماديات آليًّا.

إيقاف انتشار حدث

قد تريد في بعض الأحيان إيقاف انتشار حدث لمنصتات أخرى. يمكنك القيام بذلك بإرجاع القيمة false من التابع handle.

إضافة المنصتات للطابور

يكون صفّ المنصتات في الطابور مفي دًا إذا كانت المنصتات تقوم بعمل بطيء كإرسال بريد إلكتروني أو طلب HTTP. قبل البدء بصفّ المنصتات، تأكد من ضبط الطابور وإطلاق منصت على الطابور في الخادم أو في بيئة التطوير المحلية.

لتحديد أنّ منصتا يجب إضافته للطابور، أضف الواجهة الثابتة ShouldQueue لصنف المنصت. تملك المنصتات المولّدة باستعمال الأمر  event:generate هذه الواجهة منذ البدء محمَّلة في مساحة الإسم الحالية، لذا يمكنك استخدامها مباشرة:
<?php

namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue

{
  //
}
الآن إذا نودي المنصت لحدث ما سيُضاف مباشرة للطابور. إن لم يُطلق أي استثناء عند تنفيذ المنصت من الطابور، ستُحذف المهمة عند انتهاء التنفيذ.

تخصيص اتصال الطابور واسمه

إذا أردت تخصيص اسم الطابور والاتصال المستخدَم للمنصت. يمكنك استعمال الخاصيتين connection$ و queue$ في صنف المنصت:
<?php
namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue

{

   /**
    * اسم الاتصال
    *
    * @var string|null
    */

   public $connection = 'sqs';

   /**
    * اسم الطابور
    *
    * @var string|null
    */

   public $queue = 'listeners';

}

الوصول إلى الطوابير يدويا

إذا احتجت للوصول يدويًّا إلى توابع الأعمال المصفوفة في الطوابير: delete و release، استخدم Illuminate\Queue\InteractsWithQueue. تُحمَّل هذه الخاصية تلقائيًّا مع المنصتات المولّدة وتمنح حرية استخدام التوابع delete و release:
<?php

namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue
{

   use InteractsWithQueue;

   /**
    * معالجة الحدث     *
    * @param  \App\Events\OrderShipped  $event
    * @return void
    */

   public function handle(OrderShipped $event)

   {
       if (true) {
           $this->release(30);
       }
   }
}

معالجة الأعمال الفاشلة

قد يفشل تنفيذ بعض المهام في الطابور. إذا تجاوز المنصت عدد المحاولات الأقصى المحدد للطابور،  ينادى التابع failed من المنصت. يتلقى التابع failed نسخة من الاستثناء الذي سبب الفشل:
<?php
namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue
{

   use InteractsWithQueue;

   /**
    * معالجة الحدث
    *
    * @param  \App\Events\OrderShipped  $event
    * @return void
    */

   public function handle(OrderShipped $event)

   {
      //
   }

   /**
    * معالجة العمل الفاشل
    *
    * @param  \App\Events\OrderShipped  $event
    * @param  \Exception  $exception
    * @return void
    */

   public function failed(OrderShipped $event, $exception)

   {
      //
   }
}

إرسال الأحداث

لإرسال حدث، مرّر  نسخة منه للمساعد event. سيُطلق المساعد لكل المنصتات المسجّلة. حيث أنّ المساعد عامّ يمكن استخدامه من كامل التطبيق:
<?php

namespace App\Http\Controllers;

use App\Order;
use App\Events\OrderShipped;
use App\Http\Controllers\Controller;

class OrderController extends Controller

{

   /**
    * تحميل طلب معين.
    *
    * @param  int $orderId
    * @return Response
    */

   public function ship($orderId)

   {
       $order = Order::findOrFail($orderId);

      //منطق تحميل الطلبات

       event(new OrderShipped($order));

   }
}

ملاحظة : عند الاختبار، من المفيد التأكد من إطلاق الأحداث دون تشغيل منصتاتها. مساعدات الاختبار المضمّنة في Laravel تجعل هذا ممكنًا.

الاشتراك في الأحداث

كتابة مشتركات الأحداث

مشتركات الأحداث هي أصناف يمكنها الاشتراك في أحداث عديدة من داخل الصنف نفسه. مما يسمح بتعريف عدّة معالجات (handlers) في صنف واحد. يجب على المشتركات تعريف التابع subscribe، الذي يقبل نسخة من مطلق الأحداث. يمكن نداء call مع مطلق أحداث معين لتسجيل منصتات الحدث:
<?php

namespace App\Listeners;

class UserEventSubscriber
{

   /**
    * معالجة أحداث تسجيل دخول المستخدم     */

   public function onUserLogin($event) {}

   /**
    * معالجة أحداث تسجيل خروج المستخدم     */

   public function onUserLogout($event) {}

   /**
    * تسجيل المنصتات للمشترك
    *
    * @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'
       );
   }

}

تسجيل مشتركات الأحداث

بعد كتابة المشتركات، أنت جاهز لتسجيلها في مطلق الأحداث. يتم التسجيل باستعمال الخاصية subscribe$ من EventServiceProvider. مثلا، لنضف UserEventSubscriber للقائمة:
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{

   /**
    * عمليات ربط منصت الحدث
    *
    * @var array
    */

   protected $listen = [

      //
   ];

   /**
    *صنف المشترك للتسجيل
    *
    * @var array
    */

   protected $subscribe = [

       'App\Listeners\UserEventSubscriber',

   ];
}

مصادر