首页 文章

Laravel观察员的数据透视表

提问于
浏览
1

我有一个有更新方法的观察者:

ObserverServiceProvider.php

public function boot()
{
    Relation::observe(RelationObserver::class);
}

RelationObserver.php

public function updated(Relation $relation)
{
    $this->cache->tags(Relation::class)->flush();
}

所以当我更新控制器中的关系时:

public function update(Request $request, Relation $relation)
{
     $relation->update($request->all()));
     return back();
}

一切都按预期工作 . 但现在我有了一个数据透视表 . relation belongsToMany 产品 .

所以现在我的控制器方法如下所示:

public function update(Request $request, Relation $relation)
{
    if(empty($request->products)) {
        $relation->products()->detach();
    } else {
        $relation->products()->sync(collect($request->products)->pluck('id'));
    }

    $relation->update($request->all());

    return back();
}

问题是如果我添加或删除产品,观察者不再被触发 .

pivot table 更新时如何触发观察者?

谢谢

1 回答

  • 5

    如您所知,Laravel在调用 sync() 时实际上并不检索模型或在任何模型上调用save / update,因此默认情况下不会创建任何事件 . 但我想出了一些替代解决方案来解决你的问题 .


    1 - 为sync()方法添加一些额外的功能:

    如果深入了解 belongsToMany 功能,您将看到它尝试猜测某些变量名称并返回BelongsToMany对象 . 最简单的方法是让你的关系函数自己简单地返回一个自定义 BelongsToMany 对象:

    public function products() {
    
        // Product::class is by default the 1. argument in ->belongsToMany calll
        $instance = $this->newRelatedInstance(Product::class);
    
        return new BelongsToManySpecial(
            $instance->newQuery(),
            $this,
            $this->joiningTable(Product::class), // By default the 2. argument
            $this->getForeignKey(), // By default the 3. argument
            $instance->getForeignKey(), // By default the 4. argument
            null // By default the 5. argument
        );
    }
    

    或者,复制整个函数,重命名并使其返回 BelongsToManySpecial 类 . 或者省略所有变量,或者简单地返回 new BelongsToManyProducts 类并解析 __construct 中的所有BelongsToMany变量......我想你明白了 .

    使BelongsToManySpecial类扩展原始的BelongsToMany类,并将同步函数写入BelongsToManySpecial类 .

    public function sync($ids, $detaching = true) {
    
        // Call the parent class for default functionality
        $changes = parent::sync($ids, $detaching);
    
        // $changes = [ 'attached' => [...], 'detached' => [...], 'updated' => [...] ]
        // Add your functionality
        // Here you have access to everything the BelongsToMany function has access and also know what changes the sync function made.
    
        // Return the original response
        return $changes
    }
    

    或者覆盖 detachattachNew 函数以获得类似的结果 .

    protected function attachNew(array $records, array $current, $touch = true) {
        $result = parent::attachNew($records, $current, $touch);
    
        // Your functionality
    
        return $result;
    }
    
    public function detach($ids = null, $touch = true)
        $result = parent::detach($ids, $touch);
    
        // Your functionality
    
        return $result;
    }
    

    如果你想深入挖掘并想要了解幕后发生的事情,那么分析 Illuminate\Database\Eloquent\Concerns\HasRelationship 特征 - 特别是 belongsToMany 关系函数和 BelongsToMany 类本身 .


    2 - 创建一个名为 BelongsToManySyncEvents 的特征,它不会比返回特殊的BelongsToMany类做更多的事情

    trait BelongsToManySyncEvents {
    
        public function belongsToMany($related, $table = null, $foreignKey = null, $relatedKey = null, $relation = null) {
    
            if (is_null($relation)) {
                $relation = $this->guessBelongsToManyRelation();
            }
    
            $instance = $this->newRelatedInstance($related);
            $foreignKey = $foreignKey ?: $this->getForeignKey();
            $relatedKey = $relatedKey ?: $instance->getForeignKey();
    
            if (is_null($table)) {
                $table = $this->joiningTable($related);
            }
    
            return new BelongsToManyWithSyncEvents(
                $instance->newQuery(), $this, $table, $foreignKey, $relatedKey, $relation
            );
        }
    
    }
    

    创建 BelongsToManyWithSyncEvents 类:

    class BelongsToManyWithSyncEvents extends BelongsToMany {
    
        public function sync($ids, $detaching = true) {
    
            $changes = parent::sync($ids, $detaching);
    
            // Do your own magic. For example using these variables if needed:
            // $this->get() - returns an array of objects given with the sync method
            // $this->parent - Object they got attached to
            // Maybe call some function on the parent if it exists?
    
            return $changes;
        }
    
    }
    

    现在将特征添加到您的 class .


    3 - 结合以前的解决方案,并将此功能添加到BaseModel类等中的每个模型中 . 例如,如果定义了它们,请检查并调用某些方法...

    $functionName = 'on' . $this->foreignKey . 'Sync';
    
    if(method_exists($this->parent), $functionName) {
        $this->parent->$functionName($changes);
    }
    

    4 - 创建服务

    在该服务内部创建一个必须始终调用的函数,而不是默认的 sync() . 也许称之为attachAndDetachProducts(...)并添加您的事件或功能


    由于我没有关于你的课程和关系的那么多信息,你可以选择比我提供的更好的课程名称 . 但是,如果您现在的用例只是清除缓存,那么我认为您可以使用一些提供的解决方案 .

相关问题