所以我认为一个好的Laravel应用程序应该是非常模型和事件驱动的。
我有一个名为Article
的模型。我希望在发生以下事件时发送电子邮件提醒:
The docs说我可以使用模型事件并在boot()
的{{1}}函数中注册它们。
但这让我感到困惑,因为......
App\Providers\EventServiceProvider
或Comment
等其他模型时会发生什么? Author
的单boot()
函数是否绝对是巨大的?我是Laravel的初学者,来自CodeIgniter,因此试图围绕正确的Laravel做事方式。谢谢你的建议!
答案 0 :(得分:35)
在您的情况下,您还可以使用以下方法:
// 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...
}
答案 1 :(得分:25)
最近我在我的一个Laravel 5项目中出现了同样的问题,我必须记录所有模型事件。我决定使用 self.viewControllers = [ ViewControllerA(), ViewControllerB(), ViewControllerC()]
for viewController in viewControllers {
if let vc = viewController as? UICollectionViewController, collectionView = vc.collectionView {
collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: bottomContentInset(), right: 0)
}
}
。我创建了Traits
Trait,只是在需要记录的所有Model类中使用。我将根据您的需要对其进行更改,具体如下。
ModelEventLogger
现在,您可以在要为其投放事件的任何模型中使用此特征。在你的情况下<?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()
的事件处理程序。
Article
现在在 public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App\Handlers\Events\ArticleEventHandler');
}
目录下创建类ArticleEventHandler
,如下所示,
app/Handlers/Events
从不同的用户可以看出,不同的用户可以通过多种方式处理模型事件。还有自定义事件可以在事件文件夹中创建,可以在Handler文件夹中处理,可以从不同的地方分派。我希望它有所帮助。
答案 2 :(得分:9)
我发现这是做你想做的最干净的方法。
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());
});
}
}
答案 3 :(得分:3)
您已将此问题标记为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
这个描述了事件的运作方式:
答案 4 :(得分:2)
您可以选择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) {}
}
现在,如果我要创建一个产品模型,它的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.php
目录中创建了一个Observers
文件,并将其包含在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 :(得分:1)
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)和this other answer
2)模型事件只是Eloquent默认提供的事件。
答案 6 :(得分:0)
您可以在一个事件中拥有多个侦听器。所以你可能有一个监听器在文章更新时发送电子邮件,但是你可以有一个完全不同的监听器来做一些完全不同的事情 - 它们都会被执行。
答案 7 :(得分:0)
我可能会在战斗结束后来到,但如果你不想要扩展类或创建特征的大惊小怪,你可能想试试这个文件探索解决方案。
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()
{
//
}
}
希望它可以帮助您编写尽可能少的代码!
答案 8 :(得分:0)
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 类。 订购模型事件的处理程序
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',
];
}
完成!