【问题标题】:Laravel - Eloquent model not firing an eventLaravel - Eloquent 模型不触发事件
【发布时间】:2021-09-19 02:20:31
【问题描述】:

我正在使用https://github.com/JosephSilber/bouncer 包。我目前正在努力使其与 UUID 而不是典型的 int 标识符一起使用。从问题中我发现了如何做到这一点:https://github.com/JosephSilber/bouncer/issues/256.

问题是创建角色/能力最终会出错:

SQLSTATE[23000]:[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]无法将值 NULL 插入到列“id”、表“db.dbo.roles”中;列不允许空值。插入失败。 (SQL: insert into [roles] ([name], [title], [updated_at], [created_at]) 值 (Users, User, 2021-07-08 15:22:46.247, 2021-07-08 15:22 :46.247))

我的数据库中有 uuid 而不是 id,因此我在自定义角色/能力模型中创建了一个引导方法来捕获 creating 事件,如下所示:

trait BouncerUuidTrait
{
    public static function boot(){
        parent::boot();

        // NEVER REACHING HERE
        self::creating(function ($model) {
            $model->incrementing = false;
            $model->{$model->getKeyName()} = Str::uuid()->toString();
        });
    }
}

我的自定义角色类示例:

use Silber\Bouncer\Database\Role as BouncerRole;

class Role extends BouncerRole
{
    use BouncerUuidTrait;

    public $incrementing = false;

    protected $fillable = ['id', 'name', 'title'];
}

创建角色的逻辑:

        $role = Bouncer::role()->firstOrCreate([
            'name' => LDAPInterface::LDAP_GROUP_WCR_USERS,
            'title' => 'User',
        ]);

这是我从保镖包(带有 uuid)的迁移:

public function up()
    {
        Schema::create(Models::table('abilities'), function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('name');
            $table->string('title')->nullable();
            $table->bigInteger('entity_id')->unsigned()->nullable();
            $table->string('entity_type')->nullable();
            $table->boolean('only_owned')->default(false);
            $table->json('options')->nullable();
            $table->integer('scope')->nullable()->index();
            $table->timestamps();
        });

        Schema::create(Models::table('roles'), function (Blueprint $table) {
            $table->uuid('id')->primary();
            $table->string('name');
            $table->string('title')->nullable();
            $table->integer('level')->unsigned()->nullable();
            $table->integer('scope')->nullable()->index();
            $table->timestamps();

            $table->unique(
                ['name', 'scope'],
                'roles_name_unique'
            );
        });

        Schema::create(Models::table('assigned_roles'), function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->uuid('role_id')->index();
            $table->bigInteger('entity_id')->unsigned();
            $table->string('entity_type');
            $table->bigInteger('restricted_to_id')->unsigned()->nullable();
            $table->string('restricted_to_type')->nullable();
            $table->integer('scope')->nullable()->index();

            $table->index(
                ['entity_id', 'entity_type', 'scope'],
                'assigned_roles_entity_index'
            );

            $table->foreign('role_id')
                  ->references('id')->on(Models::table('roles'))
                  ->onUpdate('cascade')->onDelete('cascade');
        });

        Schema::create(Models::table('permissions'), function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->uuid('ability_id')->index();
            $table->uuid('entity_id')->nullable();
            $table->string('entity_type')->nullable();
            $table->boolean('forbidden')->default(false);
            $table->integer('scope')->nullable()->index();

            $table->index(
                ['entity_id', 'entity_type', 'scope'],
                'permissions_entity_index'
            );

            $table->foreign('ability_id')
                  ->references('id')->on(Models::table('abilities'))
                  ->onUpdate('cascade')->onDelete('cascade');
        });
    }

【问题讨论】:

  • 我相信这类似于删除事件中的this issue。您永远不会真正实例化模型,因此不会触发任何事件。
  • 尝试澄清一下:Bouncer::role()->firstOrCreate() 只是查询构建器(当点击“创建”部分时),您实际上永远不会获得模型实例。因此,没有触发 model 事件是有道理的。
  • 如果我是对的,这负责实例化模型:Bouncer::role()。那么应该如何创建模型来触发creating事件呢?
  • 不,访问关系仍然只是查询构建器。请注意,您正在静态使用该方法,而不是像 $r = new Role; $r->create() 那样实际上有一个模型实例。
  • $role = new Role(); $role->name = LDAPInterface::LDAP_GROUP_WCR_USERS; $role->title = 'User'; $role->create();此代码也不会触发

标签: php laravel eloquent


【解决方案1】:

为了触发模型事件,必须实例化模型。

基本模型示例

对于单个模型(没有关系),这就像选择一个创建语法而不是另一个一样简单:

不触发模型事件

Model::create([
  'field' => 'value
]);

触发模型事件

$model = new Model;
$model->field = 'value;
$model->save();

关系示例

对于关系,我们遵循类似的语法。

不触发模型事件

Model::related()->create(['field' => 'value']);

触发模型事件

$model = Model::find(1);

$related = new Related;
$related->field = 'value';

$model->related()->attach($related);

总结

您会注意到这两种类型之间的主要区别在于使用static 方法调用与首先实际创建new 模型。 create 和类似方法是方便的捷径,但您需要注意这些权衡。

【讨论】:

  • 我明白,但是像$role = new Role(); $role->title = 'User'; $role->save(); 这样的代码在我的情况下并没有改变任何东西。事件也不会在那里触发。
  • 您的听众应该是self 还是staticlaravel.com/docs/8.x/eloquent#events
  • 我看不出它会有什么不同,但让我们说自我,就像在其他示例中一样。
  • 您确定正在调用boot 方法吗?我似乎无法复制这个..
  • 是的,我确定启动方法已执行。我在捕捉事件之前通过添加随机 var_dump(__CLASS__) 进行检查。
猜你喜欢
  • 2017-05-08
  • 2016-09-11
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多