Создание событий (Event) в Laravel 5
Доброго времени суток. В данной статье мы поговорим о событиях (events) в Laravel 5. И применим на практике использование события. Events в Laravel – это простая реализация паттерна Obserever. О паттерне Observer можно прочитать тут.
Мы можем подписаться на событие (event), которое будет сгенерировано в некотором месте нашего приложения и обработано как правило в этом же запросе. Давайте приступим.
И так, ситуация следующая.
Постановка задачи
У нас есть таблица users:
Ей соответствует модель User.php
<?php namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; class User extends Model implements AuthenticatableContract,AuthorizableContract,CanResetPasswordContract { use Authenticatable, Authorizable, CanResetPassword; /** * The database tableused by the model. * * @var string */ protected $table ='users'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['username', 'password', 'blocked']; /** * The attributes excluded from the model's JSON form. * * @var array */ protected $hidden = ['password', 'remember_token']; }
Наша задача: когда из админки блокируем пользователя, полю blocked присваиваем значение 1 или наоборот при разблокировке blocked приваиваем 0. Мы должны записывать данные действие в лог. И это мы реализуем с помощью event.
Есть контроллер UserController в котором есть два метода – один блокирует пользователя, второй разблокирует пользователя.
Метод блокировки пользователя:
public function lock($id) { $user = User::find($id); if($user){ $user->blocked = 1; $user->save(); return redirect()->back()->with('message','saved'); } return redirect()->back()->with('errMessage','error'); }
Рассмотрим подробнее этот метод. В метод lock передается параметр $id, где $id – это ID пользователя, которого мы хотим заблокировать. Дальше мы ищем пользователя по его ID, необходимо для того, чтобы избежать блокировки несуществующего пользователя. Если пользователь найден, то атрибуту blocked присваиваем значение 1 и сохраняем. Дальше редиректим обратно с сообщением «saved». Более подробно прочитать по redirect в Laravel.
И второй метод разблокировки пользователя:
public function unlock($id) { $user = User::find($id); if($user){ $user->blocked = 0; $user->save(); return redirect()->back()->with('message','saved'); } return redirect()->back()->with('errMessage','error'); }
Данный метод аналогичен предыдущему, кроме того, что мы атрибуту blocked присваиваем значение 0.
Создание event и listener в Laravel 5
Отлично теперь можно перейти к решению нашей задачи. Первое создадим таблицу logs со следующей структурой:
Где user_id – ID пользователя, который осуществил блокировку (мы же в админке работаем только авторизованным пользователем), data – это поле в которое мы будем записывать «ID: $id Status: $blocked» $id – это ID блокируемого или разблокируемого пользователя, а $blocked – это статус: 1- заблокирован, 0 — не заблокирован.
Я не буду описывать как создать файл миграции и затем запустить эту миграцию. Это тема для отдельной статьи.
Теперь перейдем к созданию event в Laravel 5. Для этого в начале откроем файл
app/Providers/EventServiceProvider.php
и в свойстве $listen добавим следующий код:
'App\Events\AddLogs' => [ 'App\Handlers\Events\Log@handle', ],
Кратко опишем, что мы сделали. Мы добавили в массив ключ и значение, где ключем является событие (event) AddLogs, а элементом массив его слушателе (listener). В нашем случае массив состоит из одного слушателя Log.
Теперь запустим команду artisan для генерации файлов события (event) и слушателя (listener):
php artisan event:generate
У нас появились два файла:
App\Events\AddLogs.php
и
App\Handlers\Events\Log
Вот листинг первого:
<?php namespace App\Events; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class AddLogs extends Event { use SerializesModels; /** * Create a new event instance. * * @return void */ public function __construct() { } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return []; } }
Изменим его, с учетом того, что в наше событие надо передать два параметра $user_id и $data, которые event передаст дальше слушателю.
<?php namespace App\Events; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class AddLogs extends Event { use SerializesModels; public $user_id; public $data; /** * Create a new event instance. * * @return void */ public function __construct($user_id,$data) { $this->user_id = $user_id; $this->data = $data; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return []; } }
Теперь рассмотрим файл слушателя:
<?php namespace App\Handlers\Events; use App\Events\AddLogs; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class Log { /** * Create the event handler. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param AddLogs $event * @return void */ public function handle(AddLogs $event) { } }
Изменим его:
<?php namespace App\Handlers\Events; use App\Log; use App\Events\AddLogs; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; class Log { /** * Create the event handler. * * @return void */ public function __construct() { // } /** * Handle the event. * * @param AddLogs $event * @return void */ public function handle(AddLogs $event) { Adminlog::create([ 'user_id' => $event->user_id, 'data' => $event->data, ]); } }
Все наш event готов к использованию.
Вызов Event в приложении Laravel 5
Давайте вернемся к контроллеру UserController и поправим методы lock и unlock – запустим наше событие. Это можно сделать при помощи метода fire фасада Event.
Метод блокировки:
public function lock($id) { $user = User::find($id); if($user){ $user->blocked = 1; $user->save(); //вызываем event Event::fire(new AddLogs(Auth::user()->id,'ID: '.$user->id.' Status: 1')); return redirect()->back()->with('message','saved'); } return redirect()->back()->with('errMessage','error'); }
И метод разблокировки:
public function unlock($id) { $user = User::find($id); if($user){ $user->blocked = 0; $user->save(); //вызываем event Event::fire(new AddLogs(Auth::user()->id,'ID: '.$user->id.' Status: 0')); return redirect()->back()->with('message','saved'); } return redirect()->back()->with('errMessage','error'); }
Проверяем и вуаля: мы пишем логи при помощи события и слушателя.
Вывод.
Как видите все очень просто. Создается event (событие), которому соответствуют один или несколько listener (слушателей). Затем в приложении мы вызываем событие, при вызове события управление передается его слушателям, в которых и реализованы правила обработки данного события.
А для чего ещё могут использоваться события? Кроме логирования. И возможно ли поставить событие на все методы контроллера, к примеру, после добавления новой строки в бд чтобы вызывалось событие?
Вы можете события создавать для чего угодно. И затем его вызвать. Вот документация по событиям https://laravel.com/docs/5.2/events или вот видео https://laracasts.com/lessons/laravel-5-events
А в чем вообще смысл этих событий? Почему в методах лок и анлок просто не выполнять sql запрос в базу или не вызывать какой-либо метод с прописанной в нем необходимой догикой?
Лучше ли это использовать вместо обычного запроса в базу данных, путем создания соответствующего экземпляра модели Log, например, где будет заноситься лог в базу?
Откуда в слушателе берётся класс Adminlog?
Adminlog — это модель, а в таблице adminlog хранятся логи. Adminlog::create — мы создаем запись в нашей таблице с логами.
Мрак, так ничего и не заработало.
Странно. По-моему в Laravel мрака нет, все просто. А ошибка какая?