• Блог
  • События моделей Eloquent

Сегодня поговорим о событиях моделей. В Laravel их 12 — «booting», «booted», «creating», «created», «updating», «updated», «deleting», «deleted», «saving», «saved», «restoring», «restored». На каждую операцию с моделью предусмотрено 2 события. Например, при создании объекта модели сработает 2 события — creating и created. Первое срабатывает перед операцией добавления, второе после. Этот принцип распространяется на каждую операцию. Причём, если при обработке первого события вернуть false, то операция отменяется. Эту возможность можно использовать, например, для валидации данных. И так, давайте рассмотрим, как всё это устроено.

В Laravel все модели наследуются от абстрактного класса Model. Если в него заглянуть, то мы увидим, что он использует трейт HasEvents, который и подключает описанный функционал событий. В нём мы найдём методы, которые называются так же как и события — creating(), created() и т.д., за исключением «booting» и «booted». Эти 2 события специфичны и, как правило, редко используются. Они срабатывают при загрузке класса модели, т.е. при создании объекта класса. А модель при загрузке страницы может загружаться много раз как напрямую (через new myModelClass), так и при разрешении зависимости в роутере, классе или методе. Поэтому эти события малоинформативны. Но оставшиеся 10 очень полезны.

И так, давайте заглянем внутрь метода creating() трейта HasEvents:

public static function creating($callback)
{
    static::registerModelEvent('creating', $callback);
}

В нём вызывается закрытый метод registerModelEvent()

protected static function registerModelEvent($event, $callback)
{
    if (isset(static::$dispatcher)) {
        $name = static::class;
        static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);
    }
}

Т.е. для события «creating» регистрируется обработчик — анонимная функция (Closure), указанная в переменной $callback. Обратите внимание на то, как формируется название события — "eloquent.{$event}: {$name}". Таким образом, для модели App\User событие «creating» будет называться «eloquent.creating: App\User».

Как мы видим, для моделей механизм событий уже прописан. Осталось только его запустить. Для этого нужно зарегистрировать обработчик для определённого события. Как я писал раньше, сделать это можно в разных местах (сервис-провайдере, контроллере, файле маршрутизации), но удобнее всё же сделать это в самом классе модели, чтобы не искать потом по всему проекту. Для этого подойдёт метод boot(), который вызывается в конструкторе модели. Переопределяем его в классе модели и прописываем обработчик для события saving :

// Класс модели User
protected static function boot()
{
    parent::boot();
    
    # Проверка данных пользователя перед сохранением
    static::saving(function($user)  // Функция обработчика в качестве аргумента принимает объект модели
    {
        // Запрещаем называть пользователя цифрами
        if ( is_numeric($user->name) ) return false; // Отменяем операцию сохранения
    });
    # Выполняем действия после сохранения
    static::saved(function($user)
    {
        // Посылаем сообщение с регистрационными данными
    });
}

Теперь, если, например, в контроллере мы попытаемся сохранить пользователя с некорректными данными, то обработчик не позволит нам это сделать.

// UserController
# Сохраняем нового пользователя
public function store(Request $request)
{
    $user = new User($request-all());
    if ( ! $user->save() ) {
    // Ошибка валидации
    }
    ...
}

Регистрация обработчиков событий в обозревателе

Это ещё один способ регистрации обработчиков. Обозреватель (observer) — это класс, в который можно поместить нужные обработчики событий моделей. Он должен содержать методы, соответствующие названиям событий моделей. Например, creating(), updating() и saving(), а также любые другие методы. Вот пример обозревателя для модели User — регистрируем 2 обработчика:

<?php namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Обработчик для события "created".
     *
     * @param  User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Обработчик для события deleting.
     *
     * @param  User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

Остаётся всего лишь его зарегистрировать используя специальный метод модели observe(), которому в качестве аргумента нужно передать имя класса обозревателя:

User::observe('App\Observers\UserObserver')

Место регистрации можете выбрать сами — в сервис-провайдере, контроллере, модели. Как говориться, it's up to you.

События «saving» и «saved»

Особо отмечу эти события. Они дублируют события «creating», «created», «updating», «updated». Последовательность срабатывания при создании объекта будет следующая:

  1. saving
  2. creating
  3. created
  4. saved

Использовать эти два события удобно, когда необходимо выполнить какие-либо действия не зависимо от типа операции — создание или обновление. Кроме того, есть ещё одна тонкость — если попытаться сохранить не изменённый объект, то Laravel поймёт это и не будет делать лишний запрос в базу данных, а значит события «updating» и «updated» не сработают.

Пользовательские события

В Laravel есть возможность добавить собственные события для модели. Для этого в закрытом свойстве $observables укажите массив с нужными событиями. После этого останется зарегистрировать обработчик для них и определить место срабатывания этих событий.

Связь событий модели с классами событий

С помощью этой связи вы можете указать Laravel, что для определённого события модели вы уже зарегистрировали класс события с обработчиком. Указывается эта связь в закрытом свойстве модели $events

use App\Events\UserSaved;
use App\Events\UserDeleted;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $events = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

А в сервис-провайдере EventServiceProvider вы зарегистрировали эти классы событий:

protected $listen = [
    'App\Events\UserSaved' => [
        'App\Listeners\UserSavedListener',
    ],
    'App\Events\UserDeleted' => [
        'App\Listeners\UserDeletedListener',
    ],

];

Теперь при сохранении объекта пользователя сработает обработчик App\Listeners\UserSavedListener.

Использование маски

Ну и напоследок рассмотрим возможность регистрации обработчика для нескольких событий модели. Сделать это можно при помощи метода listen() диспетчера событий и символа *, называемым wildcard.

# Обработчик сработает на все события всех моделей
Event::listen('eloquent.*', function($eventName, $object) {...});

# Обработчик сработает на все события модели User
Event::listen('eloquent.*: App\User', function($eventName, $object) {...});

# Обработчик сработает на событие saving всех моделей
Event::listen('eloquent.saving: *', function($eventName, $object) {...});

# Обработчик только для события saving модели User. Передаётся только объект
Event::listen('eloquent.saving: App\User', function($object) {...});

Обратите внимание, что при использовании маски в функцию обработчика первым параметром передается название события и только вторым сам объект модели.

Заключение

Надеюсь, данная статья помогла в освоении механизма событий Laravel. В следующей статье мы пробежимся по событиям работы с пользователями.

0   7660

Комментарии ()

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.