如何使用Laravel为每个Model实例注册一个默认的Observer


How to register a default Observer for every Model instance with Laravel

Laravel 5.1

我试图为每个扩展我的抽象模型的模型注册一个模型观测器(他们正在扩展Illuminate''Database''Eloquent''model)。

问题是我的GenericModelObserver无法监听继承AbstractModel的模型引发的事件。

让我展示一下到目前为止我做了什么。

创建了一个服务提供者,并将其放在config/app.php 中提供者阵列的最后一个位置

<?php
// app/Providers/ObserverServiceProvider.php
namespace App'Providers;
use App'Models'Quotation;
use App'Models'AbstractModel;
use App'Observers'QuotationObserver;
use App'Observers'GenericModelObserver;
use Illuminate'Support'ServiceProvider;
class ObserverServiceProvider extends ServiceProvider
{
    public function boot()
    {
        AbstractModel::observe(GenericModelObserver::class);
        Quotation::observe(QuotationObserver::class);
    }
    public function register()
    {
    }
}

然后我有了简单的GenericModelObserver

<?php
// app/Observers/GenericModelObserver.php
namespace App'Observers;
use App'Models'AbstractModel;
class GenericModelObserver
{
    public function saving(AbstractModel $model)
    {
        return $model->valid();
    }
}

抽象模型

<?php
// app/Models/AbstractModel.php
namespace App'Models;
use Illuminate'Database'Eloquent'Model;
class AbstractModel extends Model
{
    // ...
}

我的报价模型

<?php
// app/Models/Quotation.php
namespace App'Models;
class Quotation extends AbstractModel
{
    // ...
}

保存报价时,GenericModelObserver无法监听保存事件或任何其他事件。

这同样适用于没有特定模型观测器的其他模型。

这是正确的策略吗?我不想通过引导方法将观察者绑定到每个模型。

不要扩展模型,而是编写自己的特性,作为观察者
下面我写了一些基本特征:

<?php
namespace App'YourPackage'Traits;
use Illuminate'Database'Eloquent'Model;
trait Observable
{
    public static function bootObservable()
    {
        static::updating(function (Model $model) {
            dd('updating');
        });
    }
}

并通过在模型类中键入use Observable;来使用它。

为了便于学习,请注意traits是如何引导的:您必须将boot[TraitClassName]方法放入trait中,才能正确引导它
千万不要在你的特质里写boot方法,这很危险!

<?php
namespace App'Models;
use Illuminate'Database'Eloquent'Model;
use App'Observers'TeamObserver;
class Team extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();
        self::observe(TeamObserver::class);
    }
}

为什么不简单地扩展父类,比如BaseObserver

我的缓存系统中有类似的东西

<?php namespace App'Observers;
class BaseObserver {
    public function saving($model)
    {
        //do your thing here that apply to all observers, like caching
    }
}

然后在您的Observers

<?php namespace App'Observers;
class Quotation extends BaseObserver{
   //you can override any of the methods if you wish
}

AppServiceProvider中的boot方法更新为以下内容:

public function boot()
{
    # Register all model observers
    $filesInFolder = 'File::files(app_path('/Observers'));
    foreach($filesInFolder as $path) {
        $observerClassName = pathinfo($path)['filename'];
        $className = str_replace('Observer', '', $observerClassName);
        $observerClassName = 'App''Observers''' . $observerClassName;
        $className = 'App''' . $className;
        $className::observe($observerClassName);
    }
}

模型应遵循以下格式:

App'User

观察员应遵循以下格式:

App'Observers'UserObserver

当模型位于"models"文件夹中时:将此$className = 'App''' . $className;换成此$className = 'App''Models''' . $className;

在父模型中,您可以执行类似的操作

/**
 * If true will attach the observers of the parent class
 * @var bool
 */
protected $shouldAttachParentObservers = true;
public static function boot()
{
    $instance = new static;
    $instance->attachParentObservers();
    parent::boot();
}

public function attachParentObservers() {
    $parentClass = get_parent_class($this);
    if(!empty($parentClass) && $this->shouldAttachParentObservers) {
        $eventObservers = [];
        foreach ($this->getObservableEvents() as $event) {
            $eventObservers[$event] = ($this::$dispatcher->getListeners("eloquent.{$event}: {$parentClass}"));
            foreach ($eventObservers[$event] as $observer) {
                $eventName = "eloquent.{$event}: {$this::getClassName()}";
                $this::$dispatcher->listen($eventName, $observer);
            }
        }
    }
}
/**
 * You may use different way to find the class name
 */
public static function getClassName() {
    return static::class;
}