Laravel observer for pivot table(数据透视表的 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();
}
一切都按预期工作.但现在我有一个数据透视表.relation 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
更新时如何触发观察者?
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
}
或者覆盖 detach
和 attachNew
函数以获得类似的结果.
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;
}
如果您想深入挖掘并想了解幕后发生的事情,请分析 IlluminateDatabaseEloquentConcernsHasRelationship
特征 - 特别是 belongsToMany
关系函数和 BelongsToMany
类本身.
If you want to dig deeper and want to understand what's going on under the hood then analyze the IlluminateDatabaseEloquentConcernsHasRelationship
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;
}
}
现在将 trait 添加到你的类中.
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 观察者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:数据透视表的 Laravel 观察者
基础教程推荐
- 如何在 PHP 中的请求之间持久化对象 2022-01-01
- Libpuzzle 索引数百万张图片? 2022-01-01
- 在 PHP 中强制下载文件 - 在 Joomla 框架内 2022-01-01
- mysqli_insert_id 是否有可能在高流量应用程序中返回 2021-01-01
- XAMPP 服务器不加载 CSS 文件 2022-01-01
- 超薄框架REST服务两次获得输出 2022-01-01
- 在 Woocommerce 中根据运输方式和付款方式添加费用 2021-01-01
- WooCommerce 中选定产品类别的自定义产品价格后缀 2021-01-01
- 通过 PHP SoapClient 请求发送原始 XML 2021-01-01
- 在多维数组中查找最大值 2021-01-01