Laravel. События, слушатели и подписчики
События в Laravel представлены реализацией паттерна Observer
, что позволяет подписываться и прослушивать события приложения. Как правило, классы событий находятся в директории app/Events
, а классы обработчиков событий — в app/Listeners
.
Создание класса события
Создаем класс события с помощью artisan-команды:
> php artisan make:event CreatePageEvent
namespace App\Events; use App\Models\Page; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class CreatePageEvent { use Dispatchable, InteractsWithSockets, SerializesModels; public $page; /** * Create a new event instance. * * @return void */ public function __construct(Page $page) { $this->page = $page; } /** * Get the channels the event should broadcast on. * * @return \Illuminate\Broadcasting\Channel|array */ public function broadcastOn() { return []; } }
Как видите, этот класс события не содержит логики — это просто контейнер для объекта Page
. Трейт SerializesModels
необходим, чтобы корректно сериализовать (представлять в виде строки) Eloquent-модели.
Создание обработчика события
Создаем обработчик события с помощью artisan-команды:
> php artisan make:listener CreatePageListener --event="CreatePageEvent"
namespace App\Listeners; use App\Events\CreatePageEvent; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class CreatePageListener { /** * Create the event listener. * * @return void */ public function __construct() { // ... } /** * Handle the event. * * @param CreatePageEvent $event * @return void */ public function handle(CreatePageEvent $event) { // обрабатываем событие создания новой страницы $event->page->name .= ' (автор ' . auth()->user()->name . ')'; $event->page->save(); } }
Сразу после добавления новой страницы в базу данных мы добавляем к названию страницы автора и еще раз сохраняем модель.
false
из метода handle()
.
Регистрация слушателя события
Открываем на редактирование файл класса EventServiceProvider
:
namespace App\Providers; use App\Events\CreatePageEvent; use App\Listeners\CreatePageListener; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], // обработчик события создания новой страницы CreatePageEvent::class => [ CreatePageListener::class, ], ]; /** * Register any events for your application. * * @return void */ public function boot() { parent::boot(); // ... } }
Возбуждение события
Теперь в ресурсном контроллере, который отвечает за создание, редактирование и удаление страниц сайта, возбудим событие:
namespace App\Http\Controllers\Admin; use App\Events\CreatePageEvent; use App\Http\Controllers\Controller; use App\Models\Page; use Illuminate\Http\Request; use Illuminate\Http\Response; class PageController extends Controller { /** * Показывает список всех страниц */ public function index() { $pages = Page::all(); return view('admin.page.index', compact('pages')); } /** * Показывает форму для создания страницы */ public function create() { return view('admin.page.create'); } /** * Сохраняет новую страницу в базу данных */ public function store(Request $request) { $page = Page::create($request->all()); /* * возбуждаем событие создания страницы */ event(new CreatePageEvent($page)); return redirect()->route('admin.page.show', ['page' => $page->id]); } /** * Показывает информацию о странице сайта */ public function show(Page $page) { return view('admin.page.show', compact('page')); } /** * Показывает форму для редактирования страницы */ public function edit(Page $page) { return view('admin.page.edit', compact('page')); } /** * Обновляет страницу (запись в таблице БД) */ public function update(Request $request, Page $page) { $page->update($request->all()); return redirect()->route('admin.page.show', ['page' => $page->id]); } /** * Удаляет страницу (запись в таблице БД) */ public function destroy(Page $page) { $page->delete(); return redirect()->route('admin.page.index'); } }
Упрощенный вариант
Если слушателей событий не много и их код не сложный, можно не создавать отдельные классы для события и его слушателя, а использовать метод boot()
сервис-провайдера EventServiceProvider
.
// возбуждаем событие создания страницы event('CreatePageEvent', $page);
namespace App\Providers; use App\Models\Page; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Event; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], ]; /** * Register any events for your application. * * @return void */ public function boot() { parent::boot(); Event::listen('CreatePageEvent', function (Page $page) { // обрабатываем событие создания новой страницы $page->name .= ' (автор ' . auth()->user()->name . ')'; $page->save(); }); } }
Подписчик на события
Подписчик на события — это класс, который подписывается на несколько событий и сам эти события обрабатывает. Такой класс удобно использовать, если есть ряд однотипных событий — например, действия связанные с моделью Page
(добавление, редактирование, удаление).
namespace App\Listeners; use App\Models\Page; class PageEventSubscriber { /** * Обработка события после создания страницы */ public function handleAfterCreatePage(Page $page) { $page->name .= ' [created:' . now('Europe/Moscow') . ']'; $page->save(); } /** * Обработка события после обновления страницы */ public function handleAfterUpdatePage(Page $page) { $page->name .= ' [updated:' . now('Europe/Moscow') . ']'; $page->save(); } /** * Register the listeners for the subscriber. * * @param \Illuminate\Events\Dispatcher $events */ public function subscribe($events) { $events->listen( 'AfterCreatePageEvent', 'App\Listeners\PageEventSubscriber@handleAfterCreatePage' ); $events->listen( 'AfterUpdatePageEvent', 'App\Listeners\PageEventSubscriber@handleAfterUpdatePage' ); } }
Данный класс подписан на два события (создавать их классы отдельно не нужно) — AfterCreatePageEvent
и AfterUpdatePageEvent
— которые передаются первым аргументом метода listen()
. В качестве второго аргумента передается класс и метод, обрабатывающий каждое из этих событий.
listen()
функцию-замыкание, которая и выполнит обработку события.
Этот класс-подписчик нужно зарегистрировать в сервис-провайдере EventServiceProvider
:
class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ // ... ]; /** * The subscriber classes to register. * * @var array */ protected $subscribe = [ 'App\Listeners\PageEventSubscriber', ]; /* ... */ }
Кроме того, подписать можно в любом месте кода до момента возбуждения события:
Event::subscribe(new App\Listeners\PageEventSubscriber());
Возбуждение события осуществляется хелпером event()
:
namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use App\Models\Page; use Illuminate\Http\Request; use Illuminate\Http\Response; class PageController extends Controller { /** * Показывает список всех страниц */ public function index() { $pages = Page::all(); return view('admin.page.index', compact('pages')); } /** * Показывает форму для создания страницы */ public function create() { return view('admin.page.create'); } /** * Сохраняет новую страницу в базу данных */ public function store(Request $request) { $page = Page::create($request->all()); /* * возбуждаем событие после создания страницы */ event('AfterCreatePageEvent', $page); return redirect()->route('admin.page.show', ['page' => $page->id]); } /** * Показывает информацию о странице сайта */ public function show(Page $page) { return view('admin.page.show', compact('page')); } /** * Показывает форму для редактирования страницы */ public function edit(Page $page) { return view('admin.page.edit', compact('page')); } /** * Обновляет страницу (запись в таблице БД) */ public function update(Request $request, Page $page) { $page->update($request->all()); /* * возбуждаем событие после обновления страницы */ event('AfterCreatePageEvent', $page); return redirect()->route('admin.page.show', ['page' => $page->id]); } /** * Удаляет страницу (запись в таблице БД) */ public function destroy(Page $page) { $page->delete(); return redirect()->route('admin.page.index'); } }
handle()
, а Подписчик — сразу несколько событий в методах handleOneEvent()
, handleTwoEvent()
.
События Eloquent
Список предопределенных событий, которые можно использовать при работе с Eloquent моделями, можно посмотреть в трейте HasEvents
. Это retrieved
— при извлечении модели из базы данных, creating
— перед записью новой модели в базу данных, created
— после записи новой модели в базу данных и так далее.
namespace Illuminate\Database\Eloquent\Concerns; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Events\NullDispatcher; use Illuminate\Support\Arr; use InvalidArgumentException; trait HasEvents { /** * Get the observable event names. * * @return array */ public function getObservableEvents() { return array_merge( [ 'retrieved', 'creating', 'created', 'updating', 'updated', 'saving', 'saved', 'restoring', 'restored', 'replicating', 'deleting', 'deleted', 'forceDeleted', ], $this->observables ); } /* ... */ }
Подробнее об этом можно прочитать здесь.
- Laravel. Наблюдатели за событиями Eloquent
- Laravel. Отправка почты по событию
- Блог на Laravel 7, часть 1. Создание таблиц БД, наполнение тестовыми данными
- Laravel. Работа с БД. Часть вторая — конструктор запроса
- Laravel. Работа с БД. Часть первая — сырой запрос
- Laravel. Отправка письма из приложения
- Laravel. Аксессоры и мутаторы
Поиск: Laravel • PHP • Web-разработка • Класс • Событие • Фреймворк • Event • Listener • Observer • Наблюдатель • Слушатель