События модели Laravel - я немного смущен тем, где они предназначены
Таким образом, я вижу, что хорошее приложение Laravel должно быть очень ориентировано на модели и события.
У меня есть модель под названием Article
. Я хочу отправить уведомления по электронной почте, когда произойдут следующие события:
- Когда создается статья.
- Когда обновляется статья
- Когда статья удалена
Документы говорят, что я могу использовать события модели и зарегистрировать их в функции boot()
App\Providers\EventServiceProvider
.
Но это меня путает, потому что...
- Что происходит, когда я добавляю дополнительные модели, такие как
Comment
или Author
, которые нуждаются в полных наборах всех своих собственных событий модели? Будет ли единственная функция boot()
EventServiceProvider
просто огромной?
- Какова цель Laravel 'other' Events? Почему я должен когда-либо использовать их, если реально мои события будут реагировать только на действия Model CRUD?
Я начинающий в Laravel, пришедший из CodeIgniter, поэтому пытаюсь обернуть голову вокруг правильного способа Laravel делать вещи. Спасибо за ваш совет!
Ответы
Ответ 1
Недавно я столкнулся с той же проблемой в одном из моих проектов Laravel 5, где мне пришлось регистрировать все события модели. Я решил использовать Traits
. Я создал ModelEventLogger
Trait и просто использовался во всех классах Model, которые необходимо было зарегистрировать. Я собираюсь изменить его в соответствии с вашими потребностями, которые приведены ниже.
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
/**
* Class ModelEventThrower
* @package App\Traits
*
* Automatically throw Add, Update, Delete events of Model.
*/
trait ModelEventThrower {
/**
* Automatically boot with Model, and register Events handler.
*/
protected static function bootModelEventThrower()
{
foreach (static::getModelEvents() as $eventName) {
static::$eventName(function (Model $model) use ($eventName) {
try {
$reflect = new \ReflectionClass($model);
Event::fire(strtolower($reflect->getShortName()).'.'.$eventName, $model);
} catch (\Exception $e) {
return true;
}
});
}
}
/**
* Set the default events to be recorded if the $recordEvents
* property does not exist on the model.
*
* @return array
*/
protected static function getModelEvents()
{
if (isset(static::$recordEvents)) {
return static::$recordEvents;
}
return [
'created',
'updated',
'deleted',
];
}
}
Теперь вы можете использовать этот признак в любой модели, для которой хотите вывести события. В вашем случае в Article
Model.
<?php namespace App;
use App\Traits\ModelEventThrower;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
use ModelEventThrower;
//Just in case you want specific events to be fired for Article model
//uncomment following line of code
// protected static $recordEvents = ['created'];
}
Теперь в вашем app/Providers/EventServiceProvider.php
, в boot()
методе register обработчик событий для Article
.
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App\Handlers\Events\ArticleEventHandler');
}
Теперь создайте класс ArticleEventHandler
в каталоге app/Handlers/Events
, как показано ниже,
<?php namespace App\Handlers\Events;
use App\Article;
class ArticleEventHandler{
/**
* Create the event handler.
*
* @return \App\Handlers\Events\ArticleEventHandler
*/
public function __construct()
{
//
}
/**
* Handle article.created event
*/
public function created(Article $article)
{
//Implement logic
}
/**
* Handle article.updated event
*/
public function updated(Article $article)
{
//Implement logic
}
/**
* Handle article.deleted event
*/
public function deleted(Article $article)
{
//Implement logic
}
/**
* @param $events
*/
public function subscribe($events)
{
$events->listen('article.created',
'App\Handlers\Events\[email protected]');
$events->listen('article.updated',
'App\Handlers\Events\[email protected]');
$events->listen('article.deleted',
'App\Handlers\Events\[email protected]');
}
}
Как вы можете видеть из разных ответов, у разных пользователей существует более одного способа обработки событий модели. Существуют также пользовательские события, которые могут быть созданы в папке "События" и могут обрабатываться в папке "Обработчик" и могут отправляться из разных мест. Надеюсь, это поможет.
Ответ 2
В вашем случае вы также можете использовать следующий подход:
// Put this code in your Article Model
public static function boot() {
parent::boot();
static::created(function($article) {
Event::fire('article.created', $article);
});
static::updated(function($article) {
Event::fire('article.updated', $article);
});
static::deleted(function($article) {
Event::fire('article.deleted', $article);
});
}
Кроме того, вам необходимо зарегистрировать слушателей в App\Providers\EventServiceProvider
:
protected $listen = [
'article.created' => [
'App\Handlers\Events\[email protected]',
],
'article.updated' => [
'App\Handlers\Events\[email protected]',
],
'article.deleted' => [
'App\Handlers\Events\[email protected]',
],
];
Также убедитесь, что вы создали обработчики в каталоге App\Handlers\Events
folder/directory для обработки этого события. Например, обработчик article.created
может быть таким:
<?php namespace App\Handlers\Events;
use App\Article;
use App\Services\Email\Mailer; // This one I use to email as a service class
class ArticleEvents {
protected $mailer = null;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function articleCreated(Article $article)
{
// Implement mailer or use laravel mailer directly
$this->mailer->notifyArticleCreated($article);
}
// Other Handlers/Methods...
}
Ответ 3
Я нашел это самым чистым способом сделать то, что вы хотите.
1.- Создайте наблюдателя для модели (ArticleObserver)
use App\Article;
class ArticleObserver{
public function __construct(Article $articles){
$this->articles = $articles
}
public function created(Article $article){
// Do anything you want to do, $article is the newly created article
}
}
2.- Создайте новый ServiceProvider (ObserversServiceProvider), не забудьте добавить его в config/app.php
use App\Observers\ArticleObserver;
use App\Article;
use Illuminate\Support\ServiceProvider;
class ObserversServiceProvider extends ServiceProvider
{
public function boot()
{
Article::observe($this->app->make(ArticleObserver::class));
}
public function register()
{
$this->app->bindShared(ArticleObserver::class, function()
{
return new ArticleObserver(new Article());
});
}
}
Ответ 4
Вы можете выбрать подход Observer для работы с событиями модели. Например, вот мой BaseObserver
:
<?php
namespace App\Observers;
use Illuminate\Database\Eloquent\Model as Eloquent;
class BaseObserver {
public function saving(Eloquent $model) {}
public function saved(Eloquent $model) {}
public function updating(Eloquent $model) {}
public function updated(Eloquent $model) {}
public function creating(Eloquent $model) {}
public function created(Eloquent $model) {}
public function deleting(Eloquent $model) {}
public function deleted(Eloquent $model) {}
public function restoring(Eloquent $model) {}
public function restored(Eloquent $model) {}
}
Теперь, если я создаю модель продукта, ее наблюдатель будет выглядеть так:
<?php
namespace App\Observers;
use App\Observers\BaseObserver;
class ProductObserver extends BaseObserver {
public function creating(Eloquent $model)
{
$model->author_id = Sentry::getUser()->id;
}
public function created(Eloquent $model)
{
if(Input::hasFile('logo')) Image::make(Input::file('logo')->getRealPath())->save(public_path() ."/gfx/product/logo_{$model->id}.png");
}
public function updating(Eloquent $model)
{
$model->author_id = Sentry::getUser()->id;
}
public function updated(Eloquent $model)
{
if(Input::has('payment_types')) $model->paymentTypes()->attach(Input::get('payment_types'));
//Upload logo
$this->created($model);
}
}
Что касается слушателей, я создаю файл observers.php
внутри Observers
dir, и я включаю его из AppServiceProvider
. Вот фрагмент из файла observers.php
:
<?php
\App\Models\Support\Ticket::observe(new \App\Observers\Support\TicketObserver);
\App\Models\Support\TicketReply::observe(new \App\Observers\Support\TicketReplyObserver);
Все это относится к Model Events
.
Если вам нужно отправить электронное письмо после создания записи, было бы более чистым использовать "другие" события Laravel, так как у вас будет выделенный класс, чтобы справиться с этим и уволить его, когда вы из контроллера.
"Другие" события будут иметь гораздо больший смысл, так как чем более автоматизировано ваше приложение, подумайте обо всех ежедневных cronjobs, которые вам понадобятся в какой-то момент. Не будет более чистого способа справиться с этим, кроме других событий.
Ответ 5
Вы отметили этот вопрос как Laravel 5, поэтому я бы предложил не использовать модельные события, так как у вас будет много дополнительного кода в ваших моделях, что может затруднить управление в будущем. Вместо этого моя рекомендация заключалась бы в использовании командной шины и событий.
Здесь представлены документы для этих функций:
http://laravel.com/docs/5.0/bus
http://laravel.com/docs/5.0/events
Моя рекомендация заключалась бы в использовании следующего шаблона.
- Вы создаете форму, которая отправляется на ваш контроллер.
- Контроллер отправляет данные из запроса, сгенерированного командой.
- Ваша команда делает тяжелую работу - т.е. создает запись в базе данных.
- Затем ваша команда запускает событие, которое может быть обработано обработчиком событий.
- Ваш обработчик событий делает что-то вроде отправки электронной почты или обновления чего-то другого.
Есть несколько причин, почему мне нравится этот шаблон: Концептуально ваши команды обрабатывают вещи, которые происходят прямо сейчас, и события обрабатывают вещи, которые только что произошли. Кроме того, вы можете легко поместить обработчики команд и событий в очередь для последующей обработки - это отлично подходит для отправки электронных писем, поскольку вы, как правило, не хотите делать это в режиме реального времени, поскольку они замедляют HTTP-запрос по справедливому биту. Вы также можете иметь несколько обработчиков событий для одного события, которое отлично подходит для разделения проблем.
Трудно представить какой-либо фактический код здесь, как ваш вопрос, более подробно о концепциях Laravel, поэтому я бы рекомендовал просмотреть эти видеоролики, чтобы вы получили представление о том, как работает этот шаблон:
Здесь описывается командная шина:
https://laracasts.com/lessons/laravel-5-events
В этом разделе описывается, как работают события:
https://laracasts.com/lessons/laravel-5-commands
Ответ 6
1) Вы можете создать прослушиватель событий для каждой новой модели (ArticleEventSubscriber, CommentEventSubscriber) при загрузке:
EventServiceProvider.php
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App\Listeners\ArticleEventListener');
$events->subscribe('App\Listeners\CommentEventListener');
}
или вы также можете использовать свойство $subscribe
protected $subscribe = [
'App\Listeners\ArticleEventListener',
'App\Listeners\CommentEventListener',
];
Существует множество способов прослушивания и обработки событий. Взгляните на текущую основную документацию, чтобы узнать больше способов (например, закрытие соединений): Laravel Docs (master) и этот другой ответ
2) События модели - это просто события, предоставленные по умолчанию Eloquent.
https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php#L1171
https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php#L1273
Ответ 7
У вас может быть несколько слушателей на событии. Таким образом, у вас может быть слушатель, который отправляет электронное письмо при обновлении статьи, но у вас может быть совершенно другой слушатель, который делает что-то совершенно другое - они оба исполняются.