#laravel #laravel-7
#laravel #laravel-7
Вопрос:
Я создаю панель администратора с использованием Laravel nova. У меня есть таблица элементов, в которой есть число, принадлежащее самому себе, с использованием (конечно) сводной таблицы.
Это само по себе работает, но когда я указываю, что элемент 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');
}
}
Итак: представьте, что у меня есть таблица с элементами A, B и C, и отношения определены как таковые в сводной таблице
item_id - related_id
A - B
A - C
B - C
Итак, в принципе, когда я делаю B-> related, я хочу, чтобы он возвращал [A, C]. Теперь он будет возвращать только C, а у C вообще нет связанных элементов
Здесь есть два возможных решения, одно из которых заключается в том, чтобы также вставить обратное отношение, что приведет к
item_id - related_id
A - B
B - A
A - C
C - A
B - C
C - B
Это даст правильные результаты, но как автоматически заставить laravel сохранить также перевернутое отношение и убедиться, что удаления и т.д. Выполнены правильно?
Другим вариантом было бы «объединение» двух связанных вызовов 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');
}
}
Это также вызвало бы всевозможные проблемы.
Итак, какой здесь был бы хороший подход?
Комментарии:
1. Я добавил код, который я в настоящее время использую для определения отношения. Здесь это не так уж важно.
2. Я добавил способ, которым я решил это, в качестве ответа, другие подходы, конечно, приветствуются 🙂
Ответ №1:
Я выбрал метод добавления обратного отношения к сводной таблице, используя наблюдателя и сводную модель
Обычно модель не создается при наличии отношения belongsToMany. В этом случае мне нужно было одно, поэтому я создал его вручную.
Модель Pivot, обратите внимание, что она расширяет Pivot вместо Model, наблюдатель добавляется в метод загрузки, а $ timestamps, для которого я установил значение false:
namespace AppModels;
use AppObserversRelationObserver;
use IlluminateDatabaseEloquentRelationsBelongsTo;
use IlluminateDatabaseEloquentRelationsPivot;
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 AppObservers;
use AppModelsItemRelated;
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:
public function related(): BelongsToMany
{
return $this->belongsToMany(Item::class, 'item_related', 'item_id', 'related_id')
->using(ItemRelated::class);
}