События моделей 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». Последовательность срабатывания при создании объекта будет следующая:
- saving
- creating
- created
- 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. В следующей статье мы пробежимся по событиям работы с пользователями.
Вы должны авторизоваться, чтобы оставлять комментарии.
Комментарии ()