Laravel模型事件-我对他们打算去哪里有点困惑


Laravel Model Events - I'm a bit confused about where they're meant to go

所以我认为一个好的Laravel应用程序应该是模型和事件驱动的。

我有一个模型叫Article。我希望在发生以下事件时发送电子邮件提醒:

  • 创建文章时
  • 当条目被更新
  • 当条目被删除

文档说我可以使用模型事件并在App'Providers'EventServiceProviderboot()函数中注册它们。

但是这让我很困惑,因为…

  • 当我添加进一步的模型,如CommentAuthor,需要全套的所有自己的模型事件发生了什么?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

我的建议是使用以下模式:

  • 你创建一个表单提交到你的控制器
  • 控制器从生成的请求中调度数据到命令。
  • 你的命令做了繁重的工作——即在数据库中创建一个条目。然后你的命令触发一个事件,该事件可以被事件处理程序拾取。你的事件处理程序做一些事情,比如发送电子邮件或更新其他东西。
我喜欢这种模式有几个原因:从概念上讲,命令处理正在发生的事情,事件处理刚刚发生的事情。此外,您还可以轻松地将命令和事件处理程序放到队列中,稍后再进行处理——这对于发送电子邮件非常有用,因为您往往不希望实时发送电子邮件,因为它们会大大降低HTTP请求的速度。您还可以为单个事件设置多个事件处理程序,这对于分离关注点非常有用。

由于你的问题更多的是关于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',
        ];
    }

完成了!