Laravel模型事件的多个侦听器

时间:2016-11-29 14:07:17

标签: php laravel

我有一个名为RecordsUserActivity的特性,当用户创建,更新或删除使用此特征的内容时,它基本上在活动表中创建记录。这是代码:

trait RecordsUserActivity
{
    protected static function boot()
    {
        parent::boot();
        foreach (static::getModelEvents() as $event) {
            static::$event(function ($model) use ($event) {
                $model->addActivity($event);
            });
        }
    }

    protected function addActivity($event)
    {
        $newActivity = [
            'subject_id' => $this->id,
            'subject_type' => get_class($this),
            'action' => $event,
            'user_id' => (Auth::id()) ?? null,
        ];

        if ($event == 'updated') {
            $newActivity['data'] = json_encode($this->getDirty());
        }

        UserActivity::create($newActivity);
    }

    protected static function getModelEvents()
    {
        if (isset(static::$recordEvents)) {
            return static::$recordEvents;
        }

        return ['created', 'deleted', 'updated'];
    }
}

然后我有另一个特性记录了使用它的模型中的属性变化。这是代码:

trait RecordsPropertyChangelog
{
    protected static function boot()
    {
        parent::boot();

        static::updated(function ($model){
            $model->addPropertiesChangelog();
        });
    }

    protected function addPropertiesChangelog()
    {
        $dirty = $this->getDirty();
        foreach ($dirty as $field => $newData) {
            $oldData = $this->getOriginal($field);
            $this->addPropertyChangelog($field,$oldData,$newData);
        }
    }

    protected function addPropertyChangelog($fieldName,$oldValue,$newValue)
    {
        PropertyChangelog::create([
            'resource_id' => $this->id,
            'resource_type' => get_class($this),
            'property' => $fieldName,
            'from_value' => $oldValue,
            'to_value' => $newValue,
            'data' => '{}',
        ]);
    }

}

当我在模型中包含两个特征并进行更新时,会出现问题,两个更新的模型事件都会发生某种冲突。有没有办法解决这个问题,还是应该找到另一种解决方案?

2 个答案:

答案 0 :(得分:3)

如果您尝试在同一型号上同时使用这两种特性,应该收到错误,说明Trait method boot has not been applied, because there are collisions with other trait methods...

无论如何,您不希望自己的特征定义boot()方法。如果你有一个需要挂钩Model方法的特征,Laravel的boot有一个特殊的约定。基本上,在您的特征中,以boot{traitName}的格式定义方法。另外,在两种方法中删除对parent::boot()的调用。基础boot()上的Model方法将在Model启动时调用符合此格式的方法。

所以,你的特质应该是这样的:

trait RecordsUserActivity
{
    protected static function bootRecordsUserActivity()
    {
        foreach (static::getModelEvents() as $event) {
            static::$event(function ($model) use ($event) {
                $model->addActivity($event);
            });
        }
    }

    //...
}

trait RecordsPropertyChangelog
{
    protected static function bootRecordsPropertyChangelog()
    {
        static::updated(function ($model) {
            $model->addPropertiesChangelog();
        });
    }

    //...
}

答案 1 :(得分:1)

根据laravel文档,您应该使用Laravel Model Observers来管理模型事件:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Listen to the User created event.
     *
     * @param  User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Listen to the User deleting event.
     *
     * @param  User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

要注册观察者,请在要观察的模型上使用observe方法。您可以在其中一个服务提供商的引导方法中注册观察者。在这个例子中,我们将在AppServiceProvider中注册观察者:

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

希望这有帮助!