Laravel数据透视表观察器 [英] Laravel observer for pivot table

查看:81
本文介绍了Laravel数据透视表观察器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got a observer that has a update method:

ObserverServiceProvider.php

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

RelationObserver.php

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

因此,当我在控制器中更新关系时

So when I update a relation in my controller:

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

一切正常.但是现在我有了一个数据透视表. 关系 belongsToMany产品.

Everything is working as expected. But now I've got a pivot table. A relation belongsToMany products.

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

So now my controller method looks like this:

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();
}

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

The problem is that the observer is not triggered anymore if I only add or remove products.

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

How can I trigger the observer when the pivot table updates aswel?

谢谢

推荐答案

您已经知道,Laravel在调用sync()时实际上并没有检索模型,也没有调用任何模型上的save/update,因此不会创建任何事件默认情况下.但是我为您的问题提出了一些替代解决方案.

As you already know, Laravel doesn't actually retrieve the models nor call save/update on any of the models when calling sync() thus no event's are created by default. But I came up with some alternative solutions for your problem.

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

1 - To add some extra functionality to the sync() method:

如果您深入研究belongsToMany功能,您会发现它会尝试猜测某些变量名称并返回BelongsToMany对象.最简单的方法是使您的关系函数自己简单地返回一个自定义BelongsToMany对象:

If you dive deeper into the belongsToMany functionality you will see that it tries to guess some of the variable names and returns a BelongsToMany object. Easiest way would be to make your relationship function to simply return a custom BelongsToMany object yourself:

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变量...我想您已经明白了.

Or alternatively copy the whole function, rename it and make it return the BelongsToManySpecial class. Or omit all the variables and perhaps simply return new BelongsToManyProducts class and resolve all the BelongsToMany varialbes in the __construct... I think you got the idea.

使BelongsToManySpecial类扩展原始的BelongsToMany类,并向BelongsToManySpecial类编写一个同步函数.

Make the BelongsToManySpecial class extend the original BelongsToMany class and write a sync function to the BelongsToManySpecial class.

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函数以获得类似的结果.

Alternatively override the detach and attachNew functions for similar results.

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类本身.

If you want to dig deeper and want to understand what's going on under the hood then analyze the Illuminate\Database\Eloquent\Concerns\HasRelationship trait - specifically the belongsToMany relationship function and the BelongsToMany class itself.

2-创建一个称为BelongsToManySyncEvents的特征,除了返回您特殊的BelongsToMany类之外,其他功能不多

2 - Create a trait called BelongsToManySyncEvents which doesn't do much more than returns your special BelongsToMany class

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

}

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

Now add the trait to your class.

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

3 - Combine the previous solutions and add this functionality to every Model that you have in a BaseModel class etc. For examples make them check and call some method in case it is defined...

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

if(method_exists($this->parent), $functionName) {
    $this->parent->$functionName($changes);
}


4-创建服务


4 - Create a service

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

Inside that service create a function that you must always call instead of the default sync(). Perhaps call it something attachAndDetachProducts(...) and add your events or functionality

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

As I didn't have that much information about your classes and relationships you can probably choose better class names than I provided. But if your use case for now is simply to clear cache then I think you can make use of some of the provided solutions.

这篇关于Laravel数据透视表观察器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆