所以我认为一个好的Laravel应用程序应该是模型和事件驱动的。
我有一个模型叫Article
。我希望在发生以下事件时发送电子邮件提醒:
- 创建文章时
- 当条目被更新
- 当条目被删除
文档说我可以使用模型事件并在App'Providers'EventServiceProvider
的boot()
函数中注册它们。
但是这让我很困惑,因为…
- 当我添加进一步的模型,如
Comment
或Author
,需要全套的所有自己的模型事件发生了什么?EventServiceProvider
的单个boot()
函数会非常庞大吗? - Laravel的"其他"事件的目的是什么?如果我的事件实际上只响应Model CRUD操作,为什么还要使用它们呢?
我是Laravel的初学者,来自CodeIgniter,所以我试图用Laravel正确的方式做事。谢谢你的建议!
在您的情况下,您也可以使用以下方法:
// 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
文件夹/目录中创建了处理程序来处理该事件。例如,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...
}
最近我在一个Laravel 5项目中遇到了同样的问题,我必须记录所有的模型事件。我决定使用Traits
。我创建了ModelEventLogger
Trait,并在所有需要记录的模型类中简单地使用。我将根据您的需要进行更改,如下所示。
<?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',
];
}
}
现在你可以在任何你想要抛出事件的模型中使用这个trait。在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()
方法中为Article
注册事件处理程序。
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App'Handlers'Events'ArticleEventHandler');
}
现在在app/Handlers/Events
目录下创建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');
}
}
从不同用户的不同回答中可以看出,处理Model Events的方法不止一种。也有自定义事件,可以在events文件夹中创建,可以在Handler文件夹中处理,可以从不同的地方调度。我希望这对你有帮助。
我发现这是做你想做的事情最干净的方法。
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。-创建一个新的服务提供商(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());
});
}
}
您可以选择Observer方法来处理Model Events。例如,下面是我的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
目录中创建了一个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
的。
如果你需要在记录创建后发送电子邮件,使用Laravel 'other' Events会更简洁,因为你将有一个专门的类来处理这个问题,并在你希望的时候从控制器触发它。
当你的应用程序变得更加自动化时,"其他"事件将有更多的目的,想想在某些时候你将需要的所有日常cronjob。没有比"其他"事件更干净的处理方式了。
您已经将这个问题标记为Laravel 5,所以我建议不要使用模型事件,因为您最终会在模型中添加大量额外的代码,这可能会使将来难以管理。相反,我的建议是使用命令总线和事件。
以下是这些特性的文档:
http://laravel.com/docs/5.0/bus http://laravel.com/docs/5.0/events我的建议是使用以下模式:
- 你创建一个表单提交到你的控制器
- 控制器从生成的请求中调度数据到命令。
- 你的命令做了繁重的工作——即在数据库中创建一个条目。然后你的命令触发一个事件,该事件可以被事件处理程序拾取。你的事件处理程序做一些事情,比如发送电子邮件或更新其他东西。
由于你的问题更多的是关于Laravel的概念,所以很难在这里提供任何实际的代码,所以我建议你观看这些视频,这样你就能很好地了解这个模式是如何工作的:
这个描述了命令总线:
https://laracasts.com/lessons/laravel-5-events这个描述了事件是如何工作的:
https://laracasts.com/lessons/laravel-5-commands
一个事件可以有多个监听器。因此,您可能有一个侦听器,它在文章更新时发送电子邮件,但您可以有一个完全不同的侦听器,它做的事情完全不同——它们都将被执行。
1)您可以为每个新模型(ArticleEventSubscriber, commentteventsubscriber)创建一个事件侦听器:
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(主文档)和这个答案
2)模型事件只是Eloquent默认提供的事件。https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php L1171
https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php#L1273
我可能会在战斗之后,但是如果您不想要扩展类或创建特征的所有麻烦,您可能想要尝试一下这个文件探索解决方案。
Laravel 5。解X
请注意,您选择获取模型的文件夹应该只包含使此解决方案工作的模型
不要忘记添加use File
应用程序/供应商/AppServiceProvider.php
<?php
namespace App'Providers;
use Illuminate'Support'ServiceProvider;
use File;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$model_location = base_path() . '/app'; // Change to wherever your models are located at
$files = File::files( $model_location );
foreach( $files as $data ) {
$model_name = "App''" . pathinfo($data)['filename'];
$model_name::creating(function($model) {
// ...
});
$model_name::created(function($model) {
// ...
});
$model_name::updating(function($model) {
// ...
});
$model_name::updated(function($model) {
// ...
});
$model_name::deleting(function($model) {
// ...
});
$model_name::deleted(function($model) {
// ...
});
$model_name::saving(function($model) {
// ...
});
$model_name::saved(function($model) {
// ...
});
}
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}
希望它能帮助你编写尽可能少的代码!
Laravel 6,最短解
BaseSubscriber 类
namespace App'Listeners;
use Illuminate'Events'Dispatcher;
use Illuminate'Support'Str;
/**
* Class BaseSubscriber
* @package App'Listeners
*/
abstract class BaseSubscriber
{
/**
* Returns the first part of an event name (before the first dot)
* Can be a class namespace
* @return string
*/
protected abstract function getEventSubject(): string;
/**
* Register the listeners for the subscriber.
* @param Dispatcher $events
*/
public function subscribe($events)
{
$currentNamespace = get_class($this);
$eventSubject = strtolower(class_basename($this->getEventSubject()));
foreach (get_class_methods($this) as $method) {
if (Str::startsWith($method, 'handle')) {
$suffix = strtolower(Str::after($method, 'handle'));
$events->listen("$eventSubject.$suffix", "$currentNamespace@$method");
}
}
}
}
OrderEventSubscriber 类。Order模型事件的处理程序
use App'Models'Order;
/**
* Class OrderEventSubscriber
* @package App'Listeners
*/
class OrderEventSubscriber extends BaseSubscriber
{
/**
* @return string
*/
protected function getEventSubject(): string
{
return Order::class; // Or just 'order'
}
/**
* @param Order $order
*/
public function handleSaved(Order $order)
{
// Handle 'saved' event
}
/**
* @param Order $order
*/
public function handleCreating(Order $order)
{
// Handle 'creating' event
}
}
ModelEvents 特征。它去你的模型,在我的情况下- App'Model'Order
namespace App'Traits;
use Illuminate'Database'Eloquent'Model;
/**
* Trait ModelEvents
* @package App'Traits
*/
trait ModelEvents
{
/**
* Register model events
*/
protected static function bootModelEvents()
{
foreach (static::registerModelEvents() as $eventName) {
static::$eventName(function (Model $model) use ($eventName) {
event(strtolower(class_basename(static::class)) . ".$eventName", $model);
});
}
}
/**
* Returns an array of default registered model events
* @return array
*/
protected static function registerModelEvents(): array
{
return [
'created',
'updated',
'deleted',
];
}
}
在服务提供者中注册订阅者,例如AppServiceProvider
/**
* @param Dispatcher $events
*/
public function boot(Dispatcher $events)
{
$events->subscribe(OrderEventSubscriber::class);
}
如何将ModelEvents特性添加到您的模型中,调整您想要注册的事件而不是默认事件:
protected static function registerModelEvents(): array
{
return [
'creating',
'saved',
];
}
完成了!