#php #laravel #eloquent
#php #laravel #красноречивый
Вопрос:
Допустим, в Laravel я хочу иметь миссию с разными MissionObjectives, каждая MissionObjective имеет свою собственную модель. У меня есть Mission, CheckboxObjective, Radioobjective и SpinnerObjective. Мне нужно связать цели с миссией, но я не хочу использовать разные методы для каждой цели, я хочу, чтобы они все были в одном методе. Я пробовал полиморфные отношения, но, похоже, они просто не работают для этого. Мне понадобилось бы что-то вроде отношения «Многие к одному», которого не существует.
Это то, что я хочу:
Класс миссии:
public function objectives() {
return *All objectives*;
}
Объективные классы:
public function mission() {
return $this->hasOne('AppModelsMission');
}
Комментарии:
1. Если каждая из ваших таблиц целей имеет одинаковую структуру таблицы (возможно, это не так, но если это так), вы могли бы создать таблицу для всех целей, а затем создать новый столбец, в котором указывается, к какому типу относится эта цель. Таким образом, вы могли бы просто использовать
hasMany
для одногоobjective model
. Опять же, это может быть неприменимо к вашему вопросу в зависимости от ваших табличных структур, но я подумал, что я бы выбросил его в качестве альтернативы на тот случай, если это может быть.2. Спасибо @SawyerClever это, вероятно, было бы хорошим решением во многих случаях, но каждая из целей имеет очень разные столбцы. RadioObjective даже имеет RadioObjectiveOption в качестве отношения hasMany. Если вы знаете способ интегрировать это, пожалуйста, дайте мне знать!
3. Я удивлен, что полиморфные отношения не будут работать для этого, поскольку это основной момент такого типа отношений; адаптация одной модели ко многим другим моделям с помощью того же метода отношений. А
many-to-one
это просто обратная величина aone-to-many
. Можете ли вы предоставить базовую схему базы данных таблиц, которые вы пытаетесь подключить? Я понимаю концепцию, к которой вы стремитесь, но визуализация (или, по крайней мере, аренда связанных столбцов в каждой из таблиц) во многом помогла бы решить эту проблему.4. Да, @TimLewis Я отредактирую свой пост со схемой таблиц.
5. Вам понадобится
objectives
метод в вашемMission
классе, который возвращает что-то вроде этого$this->hasMany([MainObjective::class, SecondaryObjective::class]);
, чтобы вы получили коллекцию, содержащую оба типа (или больше) — к сожалению, этого не существует. Я хотел бы увидеть решение для этого!
Ответ №1:
Я нашел 2 способа сделать это. Оба выполняют свою работу, ни один из них не идеален.
Обзор
-
Миссии, которые имеют несколько
*Objective
Mission
(1: n)*Objective
— один ко многимMission
hasMany*Objective
Objective
(Аннотация) принадлежитMission
*Objective
расширяетObjective
-
Миссий, которые имеют несколько,
Objective
которые, в свою очередь, каждая может быть своего рода*Objective
(промежуточный уровень)Mission
(1: n)Objective
— один ко многимObjective
(1: 1)*Objective
— один к одному (полиморфный)Mission
hasManyObjective
Objective
MorphTo*Objective
*Objective
расширяетObjective
Вы бы использовали 2 только в том случае, если вам нужен уровень itermediate, если вы хотите наследовать общие атрибуты для *Objective
from Objective
— что полностью имеет смысл, поскольку все они, по крайней мере, являются общими mission_id
.
Вы не сможете получить унаследованные атрибуты *Objective
из коробки, потому что модель для любого *Objective
привязана к определенной таблице, следовательно, она не может получить атрибуты унаследованного Objective
класса. Хотя для этого есть модуль: родительский
редактировать: Неважно, parental поддерживает только STI (наследование одной таблицы), что означает, что у нас может быть несколько моделей, ссылающихся на одну и ту же таблицу.
Решение 1
Таблицы
missions
id - int
*_objectives (e.g. main_objectives)
id - int
mission_id - int
Модели
class Mission extends Model
{
const OBJECTIVE_TYPES = [
MainObjective::class,
// ...
];
public function __get($name)
{
switch ($name) {
case 'objectives':
$objectives = new Collection();
foreach (static::OBJECTIVE_TYPES as $objectiveType) {
$objectives = $objectives->concat($this->hasMany($objectiveType)->get()->all());
}
return $objectives;
default:
return parent::__get($name);
}
}
}
abstract class Objective extends Model
{
public function mission() {
return $this->belongsTo(Mission::class, 'mission_id', 'id');
}
}
class MainObjective extends Objective
{
}
Пример
$mission = Mission::find(1);
foreach ($mission->objectives as $objective) {
echo 'Class: ' . get_class($objective) . PHP_EOL;
}
Вывод (В случае, если у нас есть 2 MainObjectives и 1 SecondaryObjective)
Class: AppModelsMainObjective
Class: AppModelsMainObjective
Class: AppModelsSecondaryObjective
Решение 2
Таблицы
missions
id - int
objectives
id - int
mission_id - int
objectiveable_type - string
objectiveable_id - int
*_objectives (e.g.: main_objectives)
id - int
objectiveable_id
ссылается на id
таблицу, используемую моделью в objectiveable_type
.
objectiveable_type
должно быть полное определение. Пример: AppModelsMainObjective
Модели
class Mission extends Model
{
public function __get($name)
{
if ($name === 'objectives') {
return new Collection(array_map(function ($objective) {
return $objective->objectiveable;
}, $this->hasMany(Objective::class)->get()->all()));
}
return parent::__get($name);
}
}
class Objective extends Model
{
public function objectiveable()
{
return $this->morphTo();
}
protected function _mission() {
return $this->belongsTo(Mission::class, 'mission_id', 'id');
}
public function __get($name)
{
switch ($name) {
case 'mission':
return $this->morphMany(Objective::class, 'objectiveable')->get()->first()->_mission;
default:
return parent::__get($name);
}
}
}
class MainObjective extends Objective
{
}
Пример
$mission = Mission::find(1);
foreach ($mission->objectives as $objective) {
echo 'Class: ' . get_class($objective) . PHP_EOL;
}
Вывод (В случае, если у нас есть 2 MainObjectives и 1 SecondaryObjective)
Class: AppModelsMainObjective
Class: AppModelsMainObjective
Class: AppModelsSecondaryObjective
Практически то же самое, что и в первом решении. Большая разница здесь заключается в атрибутах. вы не сможете получить доступ к атрибутам Objective
of MainObjective
без дальнейшего расширения этого.
@Laravel/Красноречивый
ПОЖАЛУЙСТА, сделайте это проще — у меня болит голова.
Комментарии:
1. Спасибо за ваш очень подробный ответ! Я скоро попробую это и свяжусь с вами!
2. Я использовал решение 1, оно отлично работает. Большое вам спасибо! Это действительно сэкономило мне много времени.