Laravel 枢轴的观察者 table
Laravel observer for pivot table
我有一个具有更新方法的观察者:
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();
}
一切都按预期进行。但现在我有了一个支点 table。 关系 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
更新时触发观察者?
谢谢
如您所知,Laravel 在调用 sync()
时实际上不会检索模型,也不会在任何模型上调用 save/update,因此默认情况下不会创建任何事件。但我为您的问题想出了一些替代解决方案。
1 - 向 sync() 方法添加一些额外功能:
如果您深入研究 belongsToMany
功能,您会发现它会尝试猜测一些变量名称,并且 return 是一个 BelongsToMany 对象。最简单的方法是使您的关系函数简单地 return 自定义 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
);
}
或者复制整个函数,重命名并使其成为 return BelongsToManySpecial
class。或者省略所有变量,也许只是 return new BelongsToManyProducts
class 并解析 __construct
中的所有 BelongsToMany 变量...我想你明白了。
使 BelongsToManySpecial class 扩展原来的 BelongsToMany class 并向 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
函数以获得类似的结果。
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
class本身。
2 - 创建一个名为 BelongsToManySyncEvents
的特征,它的作用不超过 return 你的特殊 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:
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 class 等中拥有的每个模型。例如,让他们检查并调用一些方法,以防它被定义...
$functionName = 'on' . $this->foreignKey . 'Sync';
if(method_exists($this->parent), $functionName) {
$this->parent->$functionName($changes);
}
4 - 创建服务
在该服务中创建一个您必须始终调用的函数,而不是默认 sync()
。也许称它为 attachAndDetachProducts(...) 并添加您的事件或功能
因为我没有那么多关于您的 class 和关系的信息,您可以选择比我提供的更好的 class 名字。但是,如果您现在的用例只是清除缓存,那么我认为您可以使用提供的一些解决方案。
只需添加:
public $afterCommit = true;
在观察者的开头class..它将等到交易完成,然后执行你的同步,然后应该可以正常工作..
请检查Laravel's documentation for that。
似乎这个解决方案刚刚添加到 Laravel 8。
当我搜索这个主题时,它是第一个结果。
但是,对于较新的 Laravel 版本,您可以为此制作一个“Pivot”模型 class。
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PostTag extends Pivot
{
protected $table = 'post_tag';
public $timestamps = null;
}
相关型号
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->using(PostTag::class);
}
你必须在 EventServiceProvider
中声明你的观察者
如Laravel Docs
所述
PostTag::observe(PostTagObserver::class);
我有一个具有更新方法的观察者:
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();
}
一切都按预期进行。但现在我有了一个支点 table。 关系 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
更新时触发观察者?
谢谢
如您所知,Laravel 在调用 sync()
时实际上不会检索模型,也不会在任何模型上调用 save/update,因此默认情况下不会创建任何事件。但我为您的问题想出了一些替代解决方案。
1 - 向 sync() 方法添加一些额外功能:
如果您深入研究 belongsToMany
功能,您会发现它会尝试猜测一些变量名称,并且 return 是一个 BelongsToMany 对象。最简单的方法是使您的关系函数简单地 return 自定义 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
);
}
或者复制整个函数,重命名并使其成为 return BelongsToManySpecial
class。或者省略所有变量,也许只是 return new BelongsToManyProducts
class 并解析 __construct
中的所有 BelongsToMany 变量...我想你明白了。
使 BelongsToManySpecial class 扩展原来的 BelongsToMany class 并向 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
函数以获得类似的结果。
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
class本身。
2 - 创建一个名为 BelongsToManySyncEvents
的特征,它的作用不超过 return 你的特殊 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:
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 class 等中拥有的每个模型。例如,让他们检查并调用一些方法,以防它被定义...
$functionName = 'on' . $this->foreignKey . 'Sync';
if(method_exists($this->parent), $functionName) {
$this->parent->$functionName($changes);
}
4 - 创建服务
在该服务中创建一个您必须始终调用的函数,而不是默认 sync()
。也许称它为 attachAndDetachProducts(...) 并添加您的事件或功能
因为我没有那么多关于您的 class 和关系的信息,您可以选择比我提供的更好的 class 名字。但是,如果您现在的用例只是清除缓存,那么我认为您可以使用提供的一些解决方案。
只需添加:
public $afterCommit = true;
在观察者的开头class..它将等到交易完成,然后执行你的同步,然后应该可以正常工作..
请检查Laravel's documentation for that。
似乎这个解决方案刚刚添加到 Laravel 8。
当我搜索这个主题时,它是第一个结果。 但是,对于较新的 Laravel 版本,您可以为此制作一个“Pivot”模型 class。
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PostTag extends Pivot
{
protected $table = 'post_tag';
public $timestamps = null;
}
相关型号
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->using(PostTag::class);
}
你必须在 EventServiceProvider
中声明你的观察者
如Laravel Docs
PostTag::observe(PostTagObserver::class);