شروع کار با ایونت های مدل لاراول (Model Events)

ایونت مدل ها به شما این امکان رو میده به نقاط مختلف در انواع چرخه های عمر مدل های لاراول وارد شوید و کاری میخاهید به صورت تابع callback فراخوانی کنید(حتی وقتی مدل در حال ذخیره و یا حذف شدنه).
در داکیومنت لاراول ایونت های مدل چگونگی کار ایونت ها شرح داده شده ولی این مقاله با جزئیات بیشتر و با هدف تکمیل در مورد تنظیمات ایونت ها و لیستنر ها منتشر شده است.

مروری بر ایونت ها

ایلوکوئنت (Eloquent) تعداد زیادی ایونت به صورت پیشفرض داره که شما میتونین از آنها استفاده کنین و یا اونا رو سفارشی کنید. لیست زیر ایونت ها رو نشون میده:

  • retrieved
  • creating
  • created
  • updating
  • updated
  • saving
  • saved
  • deleting
  • deleted
  • restoring
  • restored

با توجه به داکیومنت های لاراول نشون میده هر ایونت دقیقا چجوری کار میکنه.

به عنوان مثال retrieved وقتی صدا زده میشه مدل از دیتابیس بازابی میشود.


ثبت ایونت ها

برای ثبت ایونت ها اولین کاری که لازمه بکنین با استفاده از پراپرتی dispatchesEvents$ به اصطلاح ایونت مربوطه به مدل هوک میکنیم(یا قلاب میکنیم), و با متد ()HasEvents::fireCustomModelEvent اجرا میشه که به صورت زیر میشه




/**
 * Fire a custom model event for the given event.
 *
 * @param  string  $event
 * @param  string  $method
 * @return mixed|null
 */
protected function fireCustomModelEvent($event, $method)
{
    if (! isset($this->dispatchesEvents[$event])) {
        return;
    }

    $result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this));

    if (! is_null($result)) {
        return $result;
    }

بعضی از ایونت ها مانند حذف کردن delete مقدار برگشتی رو برسی میکنه که اگر false بود عملیات رو به حالت اولیه برمیگردونه. برای مثال شما میتوانید از حذف شدن یا ذخیره شدن بعضی از کاربران جلوگیری کنین.

به عنوان مثال با استفاده از App\User, به صورت زیر میتوانید ایونت مدل خود را کانفیگ کنید.

protected $dispatchesEvents = [
    'saving' => \App\Events\UserSaving::class,
];

با استفاده از کامند artisan make:event میتوانید یک ایونت بسازین که پیش فرض آن به صورت زیرِ:

namespace App\Events;

use App\User;
use Illuminate\Queue\SerializesModels;

class UserSaving
{
    use SerializesModels;

    public $user;

    /**
     * Create a new event instance.
     *
     * @param \App\User $user
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

کلاس ایونت یک پراپرتی پابلیک برای مدل user میسازه که در هنگام ذخیره سازی قابل دسترسیه.

قدم بعدی ساخت یک لیسینرِ که هنگامی که ایونت saving در مدل user فایر و یا اجرا شد به اون گوش بده و ما میتوانیم کاری که لازمه رو انجام بدیم.


ساخت یک ایونت لیسینر

در حال حاضر ایونت مدل user سفارشی ما وقتی قایر میشه، ما باید یک لیسینر را برای آن ثبت کنیم. ما در استفاده از یک ناظر مدل در یک ثانیه نگاه خواهیم کرد، اما من می خواستم از طریقه پیکربندی اولیه ایونت و لیسینر شرح بدم.

لیسینر ایونت شبیه هر لیسینر رویداد Laravel است و متد ()handle یک نمونه از کلاس ایونت App\Events\UserSaving را قبول می کند. که با استفاده از php artisan make:listener یک لیسینر ساخته میشه و پیشفرض به صورت زیر است:

namespace App\Listeners;

use App\Events\UserSaving as UserSavingEvent;

class UserSaving
{
    /**
     * Handle the event.
     *
     * @param  \App\Events\UserSavingEvent $event
     * @return mixed
     */
    public function handle(UserSavingEvent $event)
    {
        app('log')->info($event->user);
    }
}

من فقط یک کال(call) به logger اضافه کردم تا بتوانم مدل را که به لیسینر منتقل شده است بازبینی کنم. برای این کار، ما باید لیسینر را در پراپرتی EventServiceProvider::$listen ثبت کنیم:


namespace App\Providers;

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

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        \App\Events\UserSaving::class => [
            \App\Listeners\UserSaving::class,
        ],
    ];

    // ...
}

حالا زمانی که مدل ایونت را ارسال می کند، لیسینر ما که ثبت شده است و در حال حاضر در طول ایونت ذخیره سازی فراخوانی میشه.


امتحان کردن ایونت هندلر

میتوانیم کدای ایونت لیسینرمان رو به tinker بدیم

php artisan tinker
>>> factory(\App\User::class)->create();
=> App\User {#794
name: "Aiden Cremin",
email: "josie05@example.com",
updated_at: "2018-03-15 03:57:18",
created_at: "2018-03-15 03:57:18",
id: 2,
}

اگر ایونت و لیسینر را به درستی ثبت کرده باشید، باید نمایشی JSON از مدل را در فایل laravel.log ببینید:

[2018-03-15 03:57:18] local.INFO: {"name":"Aiden Cremin","email":"josie05@example.com"}

توجه داشته باشید که در این مرحله مدل پراپرتی های created_at یا updated_at را ندارد. اگر روی مدل ()save فراخوانی کنید، لاگ سیستم یک رکورد جدید با timestamps خواهد داشت، زیرا رویداد save بر روی هر دو رکورد ایجاد شده و رکورد موجود فراخوانی می شود:

>>> $u = factory(\App\User::class)->create();
=> App\User {#741
name: "Eloisa Hirthe",
email: "gottlieb.itzel@example.com",
updated_at: "2018-03-15 03:59:37",
created_at: "2018-03-15 03:59:37",
id: 3,
}
>>> $u->save();
=> true
>>>

جلوگیری از ذخیره کردن

برخی از ایونت های مدل به شما این امکان را می دهند تا از برخی اقدامات جلوگیری کنید. در مثال ساده ی ما بگذارید بگوییم ما نمی خواهیم در یک مدل کاربری به نام paul ثبت شود:


/**
 * Handle the event.
 *
 * @param  \App\Events\UserSaving $event
 * @return mixed
 */
public function handle(UserSaving $event)
{
    if (stripos($event->user->name, 'paul') !== false) {
        return false;
    }
}
در Eloquent متد ()Model::save ، این ایونت بر اساس نتایج حاصل از این رویداد را ذخیره سازی را متوقف می کند:
public function save(array $options = [])
{
    $query = $this->newQueryWithoutScopes();

    // If the "saving" event returns false we'll bail out of the save and return
    // false, indicating that the save failed. This provides a chance for any
    // listeners to cancel save operations if validations fail or whatever.
    if ($this->fireModelEvent('saving') === false) {
        return false;
    }
    }

استفاده از Observer ها

اگر از ایونت های متعدد استفاده میکنید، ممکن است راحت ترباشد تا از یک کلاس Observer برای گروه بندی ایونت های متعدد استفاده کنید. داکیومنت های مربوط به Observers:

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Handle the User "created" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Handle the User "updated" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updated(User $user)
    {
        //
    }

    /**
     * Handle the User "deleted" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleted(User $user)
    {
        //
    }
}

و باید Observer خود را در سرویس پروایدر AppServiceProvider ثبت کنید یه صورت زیر:‌

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    User::observe(UserObserver::class);
}


منبع :‌ https://laravel-news.com/laravel-model-events-getting-started