Laravel belongsToMany on same table: 如何创建对称关系?
Laravel belongsToMany on same table: how do I create a symmetrical relation?
我正在使用 Laravel nova 构建管理面板。我有一个 Items table,它本身有一个 belongsToMany,使用(当然)一个 pivot table.
这本身是有效的,但是当我指定项目 A 与项目 B 相关时,不会存储该关系的倒数,因此导致在项目 B 上找不到。
我需要它,因为这种关系是对称的,意味着如果 A 与 B 相关,那么 B 也与 A 相关。
存储和检索关系的最佳方式是什么?
这是我当前定义关系的代码,它以一种方式工作。
class Item extends Model
{
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
所以:假设我有一个 table,其中包含项目 A、B 和 C,并且关系在主元 table
中定义为这样
item_id - related_id
A - B
A - C
B - C
所以基本上,当我做 B-> 相关时,我希望它 return [A, C]。现在只会return C,而C根本没有任何相关项
这里有两种可能的解决方案,一种是同时插入逆关系,导致
item_id - related_id
A - B
B - A
A - C
C - A
B - C
C - B
这会产生正确的结果,但是如何自动强制 laravel 保存倒置关系,并确保正确完成删除等操作?
另一个选项是 'merging' 两个相关的 belongsToMany 调用。
class Item extends Model
{
public function related(): BelongsToMany
{
// return merged $this->relatedFrom and $this->relatedTo
}
public function relatedFrom(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'related_id', 'item_id');
}
public function relatedTo(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
这也会导致各种问题。
那么什么是好的方法呢?
我选择了向枢轴添加反向关系的方法 table,使用观察者和枢轴模型
通常情况下,当有 belongsToMany 关系时,不会创建模型。在这种情况下我需要一个,所以我手动创建了一个。
Pivot 模型,注意它扩展了 Pivot 而不是 Model,在启动方法中添加了观察者,以及我设置为 false 的 $timestamps:
namespace App\Models;
use App\Observers\RelationObserver;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ItemRelated extends Pivot
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'item_related';
public $timestamps = false;
public static function boot(): void
{
parent::boot();
parent::observe(new RelationObserver);
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'item_id',
'related_id',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'item_id' => 'integer',
'related_id' => 'integer',
];
}
观察者只需要一个created和deleted方法,插入和删除分别是插入和删除后的反向关系:
namespace App\Observers;
use App\Models\ItemRelated;
class RelationObserver
{
public function created(ItemRelated $itemRelated): void
{
if (ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->doesntExist()) {
$itemRelatedReverse = new ItemRelated();
$itemRelatedReverse->item_id = $itemRelated->related_id;
$itemRelatedReverse->related_id = $itemRelated->item_id;
$itemRelatedReverse->save();
}
}
public function deleted(ItemRelated $itemRelated): void
{
ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->delete();
}
}
最后,我需要通过链接 using 方法告诉项目 table 上的关系使用主元 table:
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id')
->using(ItemRelated::class);
}
我正在使用 Laravel nova 构建管理面板。我有一个 Items table,它本身有一个 belongsToMany,使用(当然)一个 pivot table.
这本身是有效的,但是当我指定项目 A 与项目 B 相关时,不会存储该关系的倒数,因此导致在项目 B 上找不到。
我需要它,因为这种关系是对称的,意味着如果 A 与 B 相关,那么 B 也与 A 相关。
存储和检索关系的最佳方式是什么?
这是我当前定义关系的代码,它以一种方式工作。
class Item extends Model
{
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
所以:假设我有一个 table,其中包含项目 A、B 和 C,并且关系在主元 table
中定义为这样item_id - related_id
A - B
A - C
B - C
所以基本上,当我做 B-> 相关时,我希望它 return [A, C]。现在只会return C,而C根本没有任何相关项
这里有两种可能的解决方案,一种是同时插入逆关系,导致
item_id - related_id
A - B
B - A
A - C
C - A
B - C
C - B
这会产生正确的结果,但是如何自动强制 laravel 保存倒置关系,并确保正确完成删除等操作?
另一个选项是 'merging' 两个相关的 belongsToMany 调用。
class Item extends Model
{
public function related(): BelongsToMany
{
// return merged $this->relatedFrom and $this->relatedTo
}
public function relatedFrom(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'related_id', 'item_id');
}
public function relatedTo(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id');
}
}
这也会导致各种问题。
那么什么是好的方法呢?
我选择了向枢轴添加反向关系的方法 table,使用观察者和枢轴模型
通常情况下,当有 belongsToMany 关系时,不会创建模型。在这种情况下我需要一个,所以我手动创建了一个。
Pivot 模型,注意它扩展了 Pivot 而不是 Model,在启动方法中添加了观察者,以及我设置为 false 的 $timestamps:
namespace App\Models;
use App\Observers\RelationObserver;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ItemRelated extends Pivot
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'item_related';
public $timestamps = false;
public static function boot(): void
{
parent::boot();
parent::observe(new RelationObserver);
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'item_id',
'related_id',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'item_id' => 'integer',
'related_id' => 'integer',
];
}
观察者只需要一个created和deleted方法,插入和删除分别是插入和删除后的反向关系:
namespace App\Observers;
use App\Models\ItemRelated;
class RelationObserver
{
public function created(ItemRelated $itemRelated): void
{
if (ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->doesntExist()) {
$itemRelatedReverse = new ItemRelated();
$itemRelatedReverse->item_id = $itemRelated->related_id;
$itemRelatedReverse->related_id = $itemRelated->item_id;
$itemRelatedReverse->save();
}
}
public function deleted(ItemRelated $itemRelated): void
{
ItemRelated::where([
['item_id', '=', $itemRelated->related_id],
['related_id', '=', $itemRelated->item_id],
])->delete();
}
}
最后,我需要通过链接 using 方法告诉项目 table 上的关系使用主元 table:
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id')
->using(ItemRelated::class);
}