Laravel观察员的数据透视表

Jam*_*mie 2 php pivot observers laravel

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

ObserverServiceProvider.php

public function boot()
{
    Relation::observe(RelationObserver::class);
}
Run Code Online (Sandbox Code Playgroud)

RelationObserver.php

public function updated(Relation $relation)
{
    $this->cache->tags(Relation::class)->flush();
}
Run Code Online (Sandbox Code Playgroud)

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

public function update(Request $request, Relation $relation)
{
     $relation->update($request->all()));
     return back();
}
Run Code Online (Sandbox Code Playgroud)

一切都按预期工作.但现在我有了一个数据透视表.一个关系 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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

Kid*_*ang 9

当我搜索这个主题时,它是第一个结果。然而,对于较新的 Laravel 版本,您只需为此创建一个“Pivot”模型类。

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\Pivot;

class PostTag extends Pivot
{
    protected $table = 'post_tag';

    public $timestamps = null;
}
Run Code Online (Sandbox Code Playgroud)

对于相关型号

public function tags(): BelongsToMany
{
    return $this->belongsToMany(Tag::class)->using(PostTag::class);
}

Run Code Online (Sandbox Code Playgroud)

并且您必须按照Laravel 文档EventServiceProvider 中所述声明您的观察者

PostTag::observe(PostTagObserver::class);
Run Code Online (Sandbox Code Playgroud)

参考:观察 Laravel 中的数据透视表


Art*_*äpp 8

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


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
    );
}
Run Code Online (Sandbox Code Playgroud)

或者复制整个函数,重命名并使其返回BelongsToManySpecial类.或者省略所有变量,或者简单地返回new BelongsToManyProducts类并解决所有BelongsToMany varialbes __construct...我认为你有了这个想法.

使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
}
Run Code Online (Sandbox Code Playgroud)

或者覆盖类似结果的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;
}
Run Code Online (Sandbox Code Playgroud)

如果你想深入挖掘并想要了解幕后发生的事情,那么分析这个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
        );
    }

}
Run Code Online (Sandbox Code Playgroud)

创建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;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在将特征添加到您的班级.


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

$functionName = 'on' . $this->foreignKey . 'Sync';

if(method_exists($this->parent), $functionName) {
    $this->parent->$functionName($changes);
}
Run Code Online (Sandbox Code Playgroud)

4 - 创建服务

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


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