رویدادهای سفارشی (Custom Events) در لاراول — راهنمای کاربردی
در این مقاله قصد داریم به بررسی مبانی مدیریت رویداد (Event) در فریمورک وب لاراول بپردازیم. این یکی از قابلیتهایی است که شما به عنوان یک توسعهدهنده باید در فریمورک مورد نظر خود به آن تسلط داشته باشید. ما در این مسیر با روش ایجاد یک مثال واقعی از رویدادهای سفارشی در لاراول نیز آشنا خواهیم شد. همچنین یک شنونده برای رویداد خود میسازیم.
مفهوم رویدادها در لاراول مبتنی بر الگوی طراحی نرمافزار بسیار رایجی به نام الگوی مشاهدهگر (observer) است. در این الگو، تصور میشود که سیستم به رویدادها گوش میدهد و بر همین مبنا واکنش میدهد. این قابلیتی بسیار مفید است، زیرا امکان جداسازی کامپوننتها را در یک سیستم فراهم میسازد و بدین ترتیب از ایجاد یک کد به شدت درهمتنیده و پیچیده جلوگیری میکند.
برای نمونه تصور کنید، میخواهید وقتی فردی وارد وبسایت شما میشود، این واقعه را به همه ماژولها در یک سیستم اطلاع دهید. بدین ترتیب، ماژولها میتوانند به این رویداد لاگین کردن واکنش نشان دهند. این واکنش میتواند از ارسال ایمیل یا نوتیفیکیشن درون اپلیکیشن یا هر چیزی که قرار است در پاسخ به آن رویداد اتفاق بیفتد متفاوت باشد.
مفاهیم مقدماتی رویدادها و شنوندهها
در این بخش، به بررسی روش مورد استفاده از سوی لاراول برای پیادهسازی رویدادها و شنوندهها در هسته مرکزی فریمورک میپردازیم. اگر با معماری لاراول آشنا باشید، احتمالاً میدانید که لاراول مفهوم یک ارائهدهنده سرویس را پیادهسازی میکند و بدین ترتیب میتوان سرویسهای متفاوتی را به اپلیکیشن تزریق کرد. به طور مشابه، لاراول یک کلاس داخلی به نام EventServiceProvider.php دارد که امکان تعریف کردن نگاشت شنونده رویداد برای یک اپلیکیشن را فراهم میسازد.
در ادامه فایل app/Providers/EventServiceProvider.php را باز کنید:
1<?php
2namespace App\Providers;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
5class EventServiceProvider extends ServiceProvider
6{
7 /**
8 * The event listener mappings for the application.
9 *
10 * @var array
11 */
12 protected $listen = [
13 'App\Events\SomeEvent' => [
14 'App\Listeners\EventListener',
15 ],
16 ];
17 /**
18 * Register any events for your application.
19 *
20 * @return void
21 */
22 public function boot()
23 {
24 parent::boot();
25 //
26 }
27}
اگر نگاهی دقیق به مشخصه listen$ داشته باشید، میبینید که این مشخصه امکان تعریف یک آرایه از رویدادها و شنوندههای مرتبط را فراهم میسازد. کلید آرایه متناظر با رویدادها در سیستم است و مقادیر آن متناظر با شنوندههایی است که در زمان رخ دادن رویداد متناظر در سیستم تحریک میشوند. در ادامه از یک مثال دنیای واقعی برای توضیح بیشتر این مفهوم استفاده میکنیم. چنان که شاید بدانید، لاراول یک سیستم احراز هویت داخلی دارد که قابلیتهایی مانند ثبت نام، لاگین کردن و غیره را تسهیل میکند.
مثالی از ارسال نوتیفیکیشن به عنوان معیار امنیتی
فرض کنید میخواهید وقتی فردی وارد اپلیکیشن میشود، به عنوان معیار امنیتی، یک نوتیفیکیشن ایمیل برای وی ارسال کنید. اگر لاراول از قابلیت شنونده رویداد پشتیبانی نمیکرد، در نهایت مجبور بودید کلاس مرکزی لاراول را ویرایش کنید و یا این که نوعی کد دیگر را برای ارسال ایمیل مورد استفاده قرار دهید. در واقع، ما خوششانس هستیم که لاراول از طریق استفاده از شنونده رویداد به حل این مشکل کمک میکند. در ادامه فایل app/Providers/EventServiceProvider.php را طوری تغییر میدهیم که به صورت زیر درآید:
1<?php
2namespace App\Providers;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
5class EventServiceProvider extends ServiceProvider
6{
7 /**
8 * The event listener mappings for the application.
9 *
10 * @var array
11 */
12 protected $listen = [
13 'Illuminate\Auth\Events\Login' => [
14 'App\Listeners\SendEmailNotification',
15 ],
16 ];
17 /**
18 * Register any events for your application.
19 *
20 * @return void
21 */
22 public function boot()
23 {
24 parent::boot();
25 //
26 }
27}
Illuminate\Auth\Events\Login رویدادی است که وقتی فردی وارد اپلیکیشن میشود، از سوی پلاگین Auth ایجاد میشود. ما این رویداد را به شنونده App\Listeners\SendEmailNotification متصل میکنیم به طوری که در زمان رویداد لاگین تحریک شود. البته باید کلاس شنونده App\Listeners\SendEmailNotification را نیز نوشته باشید. در این مورد هم لاراول از طریق استفاده از دستور آرتیزان زیر، امکان ایجاد یک کد قالب برای شنونده را فراهم میسازد:
php artisan event:generate
دستور فوق کلاسهای رویداد و شنونده را زیر مشخصه listen$ تولید میکند. در این حالت، رویداد Illuminate\Auth\Events\Login از قبل موجود است، به طوری که صرفاً کلاس شونده App\Listeners\SendEmailNotification ایجاد میشود. در واقع، در این وضعیت باید کلاس رویداد Illuminate\Auth\Events\Login نیز در صورتی که از قبل موجود نیست، ایجاد شود.
بررسی شنونده رویداد
در ادامه شنوندهای که در مسیر app/Listeners/SendEmailNotification.php ایجاد شده را بررسی میکنیم:
1<?php
2namespace App\Listeners;
3use Illuminate\Auth\Events\Login;
4use Illuminate\Queue\InteractsWithQueue;
5use Illuminate\Contracts\Queue\ShouldQueue;
6class SendEmailNotification
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 Login $event
21 * @return void
22 */
23 public function handle(Login $event)
24 {
25
26 }
27}
این handle است که در زمان تحریک شدن شنونده به همراه وابستگیهای مناسب فراخوانی میشود. در این مثال، آرگومان event$ باید شامل اطلاعات زمینهای در مورد رویداد لاگین یعنی کاربر وارد شده به اپلیکیشن باشد.
همچنین میتوان از شیء event$ برای اجرای پردازش بیشتر در متد handle استفاده کرد. در این مثال، ما میخواهیم یک نوتیفیکیشن ایمیل به کاربر لاگین کرده ارسال کنیم. متد handle پس از بازبینی موارد فوق به صورت زیر درمیآید:
1public function handle(Login $event)
2{
3 // get logged in user's email and username
4 $email = $event->user->email;
5 $username = $event->user->name;
6
7 // send email notification about login
8}
بنابراین استفاده از قابلیت رویداد در لاراول به این صورت است. در بخش بعدی پا را فراتر میگذاریم و یک رویداد سفارشی و کلاس شنونده متناظرش را در لاراول میسازیم.
ایجاد رویداد سفارشی در لاراول
در سناریوی مثال بخش قبل که در مثال این بخش نیز مورد استفاده میدهیم، قرار است کارهای زیر را انجام دهیم:
- یک اپلیکیشن در موارد به خصوصی نیاز دارد که کش سیستم خود را پاک کند. ما رویداد CacheClear را همراه با اطلاعات زمینهای در مواردی که اپلیکیشن رویداد فوق را اجرا کند، تحریک میکنیم. در ادامه کلیدهای گروه کش را همراه با رویدادی که پاک شده است ارسال خواهیم کرد.
- ماژولهای دیگر در سیستم میتوانند به رویداد CacheClear گوش دهند. ما کدی را پیادهسازی میکنیم که به بررسی کش های مرتبط میپردازد.
بنابراین در ادامه فایل app/Providers/EventServiceProvider.php را بازبینی میکنیم و رویداد سفارشی و نگاشتهای شنوندههای خود را در آن ثبت میکنیم:
1<?php
2namespace App\Providers;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
5class EventServiceProvider extends ServiceProvider
6{
7 /**
8 * The event listener mappings for the application.
9 *
10 * @var array
11 */
12 protected $listen = [
13 'App\Events\ClearCache' => [
14 'App\Listeners\WarmUpCache',
15 ],
16 ];
17 /**
18 * Register any events for your application.
19 *
20 * @return void
21 */
22 public function boot()
23 {
24 parent::boot();
25 //
26 }
27}
استفاده از دستور artisan
چنان که میبینید، ما رویداد App\Events\ClearCache و کلاس شنوندههای مرتبط App\Listeners\WarmUpCache را زیر مشخصه listen$ تعریف میکنیم. سپس باید فایلهای کلاس مرتبط را ایجاد کنیم. به خاطر داشته باشید که همواره میتوانید از دستور artisan استفاده کنید و یک کد قالب مبنا تولید کنید.
php artisan event:generate
دستور فوق کلاس رویدادی را در فایل app/Events/ClearCache.php ایجاد کرده و کلاس شنونده را در فایل app/Listeners/WarmUpCache.php قرار میدهد. کلاس app/Events/ClearCache.php با چند تغییر به صورت زیر درمیآید:
1<?php
2namespace App\Events;
3use Illuminate\Broadcasting\Channel;
4use Illuminate\Queue\SerializesModels;
5use Illuminate\Broadcasting\PrivateChannel;
6use Illuminate\Broadcasting\PresenceChannel;
7use Illuminate\Foundation\Events\Dispatchable;
8use Illuminate\Broadcasting\InteractsWithSockets;
9use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
10class ClearCache
11{
12 use Dispatchable, InteractsWithSockets, SerializesModels;
13
14 public $cache_keys = [];
15 /**
16 * Create a new event instance.
17 *
18 * @return void
19 */
20 public function __construct(Array $cache_keys)
21 {
22 $this->cache_keys = $cache_keys;
23 }
24 /**
25 * Get the channels the event should broadcast on.
26 *
27 * @return Channel|array
28 */
29 public function broadcastOn()
30 {
31 return new PrivateChannel('channel-name');
32 }
33}
چنان که احتمالاً متوجه شدهاید، ما مشخصه جدیدی به نام cache_keys$ اضافه کردهایم که از آن برای نگهداری اطلاعات ارسال شده به همراه یک رویداد استفاده خواهیم کرد. در این مثال، گروههای کش را که پاک شدهاند ارسال میکنیم. سپس نگاهی به کلاس شنوندهای خواهیم داشت که دارای یک متد handle بهروزرسانی شده در فایل app/Listeners/WarmUpCache.php است.
1<?php
2namespace App\Listeners;
3use App\Events\ClearCache;
4use Illuminate\Queue\InteractsWithQueue;
5use Illuminate\Contracts\Queue\ShouldQueue;
6class WarmUpCache
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 ClearCache $event
21 * @return void
22 */
23 public function handle(ClearCache $event)
24 {
25 if (isset($event->cache_keys) && count($event->cache_keys)) {
26 foreach ($event->cache_keys as $cache_key) {
27 // generate cache for this key
28 // warm_up_cache($cache_key)
29 }
30 }
31 }
32}
تست شنونده رویداد (Event Listener)
زمانی که شنونده فراخوانی میشود، متد handle به همراه وهلهای از رویداد مرتبط ارسال میشود. در این مثال، این مورد باید وهلهای از رویداد ClearCache باشد که به عنوان آرگومان اول متد handle ارسال میشود. سپس کافی است روی همه کلیدهای کش، حلقهای تعریف کنیم و کش های مرتبط را آمادهسازی کنیم.
اکنون همه چیز را آماده تست ساختهایم. در ادامه نگاهی به فایل کنترلر در مسیر app/Http/Controllers/EventController.php میاندازیم تا نشان دهیم که یک رویداد چگونه تحریک میشود.
1<?php
2namespace App\Http\Controllers;
3use App\Http\Controllers\Controller;
4use App\Library\Services\Contracts\CustomServiceInterface;
5use App\Post;
6use Illuminate\Support\Facades\Gate;
7use App\Events\ClearCache;
8class EventController extends Controller
9{
10 public function index()
11 {
12 // ...
13
14 // you clear specific caches at this stage
15 $arr_caches = ['categories', 'products'];
16
17 // want to raise ClearCache event
18 event(new ClearCache($arr_caches));
19
20 // ...
21 }
22}
قبل از هر چیز یک آرایه از کلیدهای کش در زمان ایجاد یک وهله از رویداد ClearCache به عنوان آرگومان نخست ارسال میشود. تابع کمکی رویداد برای تحریک یک رویداد از هر کجا درون یک اپلیکیشن مورد استفاده قرار میگیرد. زمانی که یک رویداد تحریک میشود، لاراول همه شنوندهها را فرامیخواند تا به آن رویداد خاص گوش دهند.
در مورد مثال خودمان، شنونده App\Listeners\WarmUpCache طوری تنظیم شده است که به رویداد App\Events\ClearCache گوش دهد. از این رو متد handle شنونده App\Listeners\WarmUpCache زمانی که یک رویداد از کنترلر تحریک میشود، فراخوانی خواهد شد. باقی کد برای آمادهسازی کش ها جهت پاکسازی استفاده میشود. بنابراین اینک شما با روش ایجاد رویدادهای سفارشی در اپلیکیشن خود آشنا شدهاید و میتوانید با آنها کار کنید.
اشتراک رویداد چیست؟
«اشتراک رویداد» (Event Subscriber) امکان ثبت نام در چند شنونده رویداد را در یک مکان منفرد فراهم میسازد. چه بخواهید از نظر منطقی شنوندههای رویداد را گروهبندی کنید و چه بخواهید رویدادهای زیادی را در یک مکان منفرد گرد هم آورید، اشتراک رویداد گزینه مناسبی برای استفاده خواهد بود.
اگر بخواهیم مثالهایی را که تا به اینجا مورد بررسی قرار دادیم، با استفاده از اشتراک رویداد پیادهسازی کنیم، چیزی مانند زیر خواهد بود:
1<?php
2// app/Listeners/ExampleEventSubscriber.php
3namespace App\Listeners;
4class ExampleEventSubscriber
5{
6 /**
7 * Handle user login events.
8 */
9 public function sendEmailNotification($event) {
10 // get logged in username
11 $email = $event->user->email;
12 $username = $event->user->name;
13
14 // send email notification about login...
15 }
16 /**
17 * Handle user logout events.
18 */
19 public function warmUpCache($event) {
20 if (isset($event->cache_keys) && count($event->cache_keys)) {
21 foreach ($event->cache_keys as $cache_key) {
22 // generate cache for this key
23 // warm_up_cache($cache_key)
24 }
25 }
26 }
27 /**
28 * Register the listeners for the subscriber.
29 *
30 * @param Illuminate\Events\Dispatcher $events
31 */
32 public function subscribe($events)
33 {
34 $events->listen(
35 'Illuminate\Auth\Events\Login',
36 'App\Listeners\ExampleEventSubscriber@sendEmailNotification'
37 );
38
39 $events->listen(
40 'App\Events\ClearCache',
41 'App\Listeners\ExampleEventSubscriber@warmUpCache'
42 );
43 }
44}
متد subscribe مسئول ثبت شنوندهها است. آرگومان اول متد subscribe وهلهای از کلاس Illuminate\Events\Dispatcher است که میتوان برای اتصال رویدادها به شنوندهها با استفاده از متد listen استفاده کرد. آرگومان اول متد listen رویدادی است که میخواهید به آن گوش دهید و آرگومان دوم شنوندهای است که هنگام تحریک رویداد فراخوانی خواهد شد.
به این ترتیب میتوانید چندین رویداد و شنونده را در خود کلاس اشتراک تعریف کنید. کلاس اشتراک رویداد به صورت خودکار انتخاب نمیشود. شما باید آن را در کلاس EventServiceProvider.php زیر مشخصه subscriber$ چنان که در قطعه کد زیر میبینید ثبت کنید.
1<?php
2namespace App\Providers;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
5class EventServiceProvider extends ServiceProvider
6{
7 /**
8 * The subscriber classes to register.
9 *
10 * @var array
11 */
12 protected $subscribe = [
13 'App\Listeners\ExampleEventSubscriber',
14 ];
15 /**
16 * Register any events for your application.
17 *
18 * @return void
19 */
20 public function boot()
21 {
22 parent::boot();
23 //
24 }
25}
بنابراین کلاس اشتراک رویداد در اختیار شما قرار دارد و بدین ترتیب به پایان این مقاله میرسیم.
سخن پایانی
در این مقاله به بررسی چند قابلیت جالب لاراول پرداختیم که شامل رویدادها و شنوندهها است. این قابلیتها مبتنی بر الگوی طراحی مشاهدهگر هستند که امکان تحریک رویدادها در تمام نقاط اپلیکیشن را فراهم میسازد و به ماژولهای دیگر امکان میدهد که به این رویدادها گوش دهند و بر همین مبنا واکنش نشان دهند.
اگر این مطالب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی PHP
- مجموعه آموزشهای برنامهنویسی
- آموزش REST API در Laravel (لاراول) با بسته Passport
- زمانبندی وظایف در لاراول (Laravel) — راهنمای مقدماتی
- جستجوی تمام متن در لاراول با Scout — به زبان ساده
==