У меня есть Animal
модель, основанная на animal
таблице.
Эта таблица содержит type
поле, которое может содержать такие значения, как cat или dog .
Я хотел бы иметь возможность создавать объекты, такие как:
class Animal extends Model { }
class Dog extends Animal { }
class Cat extends Animal { }
Тем не менее, возможность получить животное, как это:
$animal = Animal::find($id);
Но где $animal
будет экземпляр Dog
или в Cat
зависимости от type
поля, которое я могу проверить с помощью instance of
или которое будет работать с подсказками типа. Причина в том, что 90% кода является общим, но один может лаять, а другой - мяу.
Я знаю, что могу сделать Dog::find($id)
, но это не то, что я хочу: я могу определить тип объекта только после того, как он был выбран. Я мог бы также получить Animal, и затем запустить find()
на нужном объекте, но это делает два вызова базы данных, которые я, очевидно, не хочу.
Я попытался найти способ «вручную» создать экземпляр модели Eloquent, такой как Dog from Animal, но не смог найти ни одного соответствующего метода. Любая идея или метод, который я пропустил, пожалуйста?
*_type
именем для определения модели подтипа. В моем случае у меня действительно есть только одна таблица, так что, хотя это хорошая функция, в моем случае это не так.Ответы:
Вы можете использовать Полиморфные Отношения в Laravel, как описано в Официальных Документах Laravel . Вот как вы можете это сделать.
Определите отношения в модели как дано
Здесь вам понадобятся два столбца в
animals
таблице, первый из которых,animable_type
а другой -animable_id
для определения типа модели, присоединенной к нему во время выполнения.Вы можете получить модель собаки или кошки, как указано,
После этого вы можете проверить
$anim
класс объекта с помощьюinstanceof
.Этот подход поможет вам в дальнейшем расширении, если вы добавите в приложение еще один тип животных (например, лиса или лев). Это будет работать без изменения вашей кодовой базы. Это правильный способ достижения ваших требований. Однако нет альтернативного подхода для достижения полиморфизма и совместной загрузки без использования полиморфных отношений. Если вы не используете полиморфные отношения , вы получите более одного вызова базы данных. Однако, если у вас есть один столбец, который различает модальный тип, возможно, у вас неправильная структурированная схема. Я предлагаю вам улучшить это, если вы хотите упростить его и для будущего развития.
Переписать внутреннюю модель
newInstance()
иnewFromBuilder()
это не очень хороший / рекомендуемый способ, и вам придется переделать ее, как только вы получите обновление из фреймворка.источник
Я думаю, что вы можете переопределить
newInstance
метод вAnimal
модели, проверить тип по атрибутам и затем запустить соответствующую модель.Вам также нужно переопределить
newFromBuilder
метод.источник
type
в базе данных?Если вы действительно хотите это сделать, вы можете использовать следующий подход внутри вашей модели Animal.
источник
Как указывалось в комментариях ФП: проект базы данных уже установлен, и поэтому Laravel Полиморфные отношения » похоже, здесь не вариант.
я нравится ответ Криса Нила, потому что мне пришлось недавно сделать нечто подобное (написать свой собственный драйвер базы данных для поддержки Eloquent для файлов dbase / DBF) и получить большой опыт работы с внутренними компонентами Laravel Eloquent ORM.
Я добавил свой личный вкус, чтобы сделать код более динамичным, сохраняя явное отображение для каждой модели.
Поддерживаемые функции, которые я быстро протестировал:
Animal::find(1)
работает как задано в вашем вопросеAnimal::all()
работает такжеAnimal::where(['type' => 'dog'])->get()
вернусьAnimalDog
-объекты как коллекциюAnimal
-модели в случае, если сопоставление не настроено (или в БД появилось новое сопоставление)Недостатки:
newInstance()
и внутреннеnewFromBuilder()
(копировать и вставлять). Это означает, что если будет какое-либо обновление инфраструктуры для функций-членов, вам нужно будет принять код вручную.Я надеюсь, что это поможет, и я готов ответить на любые предложения, вопросы и дополнительные варианты использования в вашем сценарии. Вот примеры использования и примеры для этого:
И это пример того, как его можно использовать, и ниже соответствующие результаты для него:
что приводит к следующему:
И если вы хотите, чтобы вы использовали,
MorphTrait
здесь, конечно, полный код для этого:источник
Я думаю, я знаю, что вы ищете. Рассмотрим это элегантное решение, которое использует области запросов Laravel, см. Https://laravel.com/docs/6.x/eloquent#query-scopes для получения дополнительной информации:
Создайте родительский класс, который содержит разделяемую логику:
Создайте дочерний (или множественный) с глобальной областью запроса и
saving
обработчиком событий:(то же самое относится и к другому классу
Cat
, просто замените константу)Глобальная область запроса действует как модификация запроса по умолчанию, так что
Dog
класс всегда будет искать записи сtype='dog'
.Скажем, у нас есть 3 записи:
Теперь вызов
Dog::find(1)
приведет к томуnull
, что область запроса по умолчанию не найдет тот,id:1
который являетсяCat
. ВызовAnimal::find(1)
иCat::find(1)
оба будут работать, хотя только последний из них даст вам реальный объект Cat.Приятной особенностью этой настройки является то, что вы можете использовать классы выше для создания отношений, таких как:
И это отношение автоматически только даст вам всех животных с
type='dog'
(в формеDog
классов). Область запроса применяется автоматически.Кроме того, вызов
Dog::create($properties)
автоматически установитtype
значение'dog'
из-заsaving
перехвата события (см. Https://laravel.com/docs/6.x/eloquent#events ).Обратите внимание, что для вызова
Animal::create($properties)
неtype
задано значение по умолчанию, поэтому здесь вам нужно установить его вручную (что и следовало ожидать).источник
Хотя вы используете Laravel, в этом случае, я думаю, вы не должны придерживаться ярлыков Laravel.
Эта проблема, которую вы пытаетесь решить, является классической проблемой, которую многие другие языки / фреймворки решают с помощью шаблона метода Factory ( https://en.wikipedia.org/wiki/Factory_method_pattern ).
Если вы хотите, чтобы ваш код был проще для понимания и без скрытых уловок, вы должны использовать известный шаблон вместо скрытых / магических уловок под капотом.
источник
Самый простой способ - создать метод в классе Animal.
Разрешающая модель
Это вернет экземпляр класса Animal, Dog или Cat в зависимости от типа модели.
источник