Laravel模型事件 – 我对他们要去的地方有点困惑
所以我看到的方式是,一个好的Laravel应用程序应该是模型和事件驱动的。
我有一个叫做Article
的模型。 我希望在下列事件发生时发送电子邮件提醒:
- 当文章被创build
- 当文章更新
- 当文章被删除
该文档说我可以使用模型事件,并在App\Providers\EventServiceProvider
的boot()
函数中注册它们。
但这让我感到困惑,因为…
- 当我添加
Comment
或Author
需要全套模型事件的更多模型时会发生什么?EventServiceProvider
的单一boot()
函数会是绝对巨大的吗? - Laravel的“其他”活动的目的是什么? 为什么我需要使用它们,如果真实的话,我的事件只会响应模型CRUD操作?
我是来自CodeIgniter的Laravel的初学者,所以试图把我的头围绕在Laravel的方式上。 谢谢你的build议!
最近我遇到了同样的问题,在我的一个Laravel 5项目中,我不得不logging所有的模型事件。 我决定使用Traits
。 我创build了ModelEventLogger
Trait,并简单地用于需要logging的所有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
模型。
<?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()
方法中注册Event Handler for Article
。
public function boot(DispatcherContract $events) { parent::boot($events); $events->subscribe('App\Handlers\Events\ArticleEventHandler'); }
现在在app/Handlers/Events
目录下创buildClass ArticleEventHandler
,
<?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\ArticleEventHandler@created'); $events->listen('article.updated', 'App\Handlers\Events\ArticleEventHandler@updated'); $events->listen('article.deleted', 'App\Handlers\Events\ArticleEventHandler@deleted'); } }
正如您从不同用户的不同答案中看到的,处理模型事件的方式不止一种。 也有自定义事件,可以在事件文件夹中创build,可以在Handler文件夹中处理,并可以从不同的地方分派。 我希望它有帮助。
在你的情况下,你也可以使用下面的方法:
// 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\ArticleEvents@articleCreated', ], 'article.updated' => [ 'App\Handlers\Events\ArticleEvents@articleUpdated', ], 'article.deleted' => [ 'App\Handlers\Events\ArticleEvents@articleDeleted', ], ];
另外请确保您已经在App\Handlers\Events
文件夹/目录中创build了处理App\Handlers\Events
来处理该事件。 例如, 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... }
我发现这是最干净的方式来做你想做的事情。
1.-为模型创build观察者(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.-创build一个新的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()); }); } }
您可以selectObserver方法来处理模型事件。 例如,这里是我的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) {} }
现在,如果我要创build一个产品模型,它的Observer将如下所示:
<?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
目录中创build了一个observers.php
文件,并将其包含在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
。
如果您需要在创buildlogging后发送电子邮件,那么使用Laravel的“其他”事件将会更清洁,因为您将有一个专门的课程来处理这个问题,并在您希望的时候从控制器。
“其他”事件将具有更多的目的,因为您的应用程序变得越自动化,想到在某些时候您需要的所有日常cronjob。 除了“其他”活动之外,再没有比这更清晰的方式来处理了。
你已经将这个问题标记为Laravel 5,所以我build议不要使用模型事件,因为最终会在模型中出现大量额外的代码,这可能会使事情难以pipe理。 相反,我的build议是使用命令总线和事件。
以下是这些function的文档:
http://laravel.com/docs/5.0/bus
http://laravel.com/docs/5.0/events
我的build议是使用以下模式。
- 你创build一个表单提交给你的控制器。
- 您的控制器将生成的请求中的数据分派给一个命令。
- 你的命令做了繁重的工作 – 即在数据库中创build一个条目。
- 然后你的命令触发一个可以被事件处理程序拾取的事件。
- 您的事件处理程序可以发送电子邮件或更新其他内容。
为什么我喜欢这种模式有几个原因:从概念上讲,你的命令处理现在正在发生的事情,事件处理刚发生的事情。 而且,你可以很容易地把命令和事件处理程序放到一个队列中稍后处理 – 这对于发送电子邮件来说是非常好的,因为你不希望这样做,因为它们会把HTTP请求放慢一点点。 您还可以为单个事件分配多个事件处理程序,这对分离问题非常有用。
在这里提供任何实际的代码很难,因为你的问题更多的是关于Laravel的概念,所以我build议你观看一下这些video,以便了解这个模式的工作原理:
这一个描述命令总线:
https://laracasts.com/lessons/laravel-5-events
这一个描述事件如何工作:
1)您可以使用以下引导方法为每个新模型(ArticleEventSubscriber,CommentEventSubscriber)创build事件侦听器:
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文档(主)和这个其他的答案
2)模型事件只是由Eloquent默认提供的事件。
您可以在事件中拥有多个侦听器。 所以当一篇文章被更新时,你可能会有一个发送邮件的监听器,但是你可以有一个完全不同的监听器来做一些完全不同的事情 – 它们都会被执行。