Доктрина ORM и фабрики со стратегией абстрактных классов

Итак, я наткнулся на это препятствие, когда мне нужно создать абстрактный класс и фабрику для создания объектов более конкретных классов, которые расширяют абстрактный класс и реализуют более конкретные методы объекта.

Проще говоря, у меня есть класс SocialMediaAbstract. Расширяющиеся классы - это Facebook, Instagram, и они реализуют SocialMediaInterface. Facebook, Instagram и т. Д. Все сохраняются в базе данных с идентификатором, именем и несколькими другими свойствами, которые используются среди расширяющихся классов, следовательно, абстрактный класс.

Поскольку я хочу иметь возможность запрашивать несколько вещей из объектов SocialMedia, а каждая платформа социальных сетей имеет свои собственные API-интерфейсы, я создал интерфейс и создал различные классы, чтобы все они могли иметь свои собственные реализации этих методов.

Теперь проблема, конечно, связана с моим абстрактным классом и Doctrine. Doctrine сообщает об этом на своем веб-сайте, посвященном наследованию:

Сопоставленный суперкласс не может быть сущностью, он не поддерживает запросы [...]

Теперь, если бы у меня был SocialMediaFactory и я добавил идентификатор, я бы хотел вернуть соответствующий объект, например, класса Facebook или Instagram. Я не хочу точно знать, что это за SocialMedia, когда я их собираю. Это проблема доктрины, по крайней мере, я так думаю.

Я что-то упускаю, возможен ли заводской шаблон? Или мне действительно стоит просто удалить абстрактный класс и создать фабрику, которая выполняет поиск в каждой таблице реализующего класса SocialMediaInterface, что кажется крайне неэффективным и незаметным, когда приложение становится больше.

Мы будем благодарны за любую информацию или указатели, поскольку я уверен, что эта проблема должна возникать чаще. Я пробовал гуглить и искать в самом Stackoverflow, но не смог получить никаких соответствующих вопросов или ответов.

Заранее большое спасибо.

РЕДАКТИРОВАТЬ: Я столкнулся с такой интересной возможностью: Наследование таблицы классов. Это означало бы добавить:

 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({"facebook" = "Facebook", "instagram" = "Instagram"})

к моему коду. Я возлагал большие надежды, но, к сожалению, валидатор выдал мне эту ошибку:

[Doctrine \ ORM \ Mapping \ MappingException] Не поддерживается определение информации о наследовании для сопоставленного суперкласса Portal \ SocialMedia \ Entity \ SocialMediaAbstract.

Суперклассы преобразователя стыда не поддерживаются.

РЕДАКТИРОВАТЬ 2 / ЗАКЛЮЧЕНИЕ: Я решил пойти с наследованием таблицы классов (точно так же, как предложенный ниже ответ). Удаление абстракции из класса позволило по-прежнему использовать мою фабрику.

Однако сейчас я использую конкретный класс как абстрактный, что кажется неправильным. Я задокументировал в docblock, что никакие объекты не должны создаваться из этого класса.

Одно небольшое примечание: Entity Manager Doctrine более или менее уже предоставляет Factory:

$socialMedia = $entityManager->find('Portal\SocialMedia\Entity\SocialMedia', 2);

Это возвращает объект Instagram. Я по-прежнему предлагаю вам построить над ним свою собственную фабрику для удобства обслуживания в дальнейшем, поскольку сущность SocialMedia может измениться позже.


person Ventus    schedule 27.07.2015    source источник
comment
удалите аннотацию @MappedSuperclass, если хотите class table inheritance. Он вам больше не нужен, это для другого шаблона наследования, см. Мой ответ для объяснения :)   -  person SaSchu    schedule 28.07.2015


Ответы (1)


Прошло некоторое время с тех пор, как я работал с доктриной, но, если я правильно помню, сопоставленные суперклассы доктрины являются реализацией шаблона наследования конкретной таблицы Мартина Фаулера.

В упомянутом здесь примере Player - это отображаемый суперкласс, атрибуты которого распределяются между всеми наследующими сущностями / моделями. Дело в том, что игрок не может быть создан и, следовательно, не имеет собственного идентификатора. Вместо этого каждая наследующая модель имеет собственный идентификатор, который не зависит друг от друга.

Я думаю, что вы ищете шаблон либо наследование одной таблицы, либо наследование таблицы классов (см. типы наследования доктрины).


Наследование одной таблицы реализовано в типе наследования доктрины "SINGLE_TABLE", где у вас есть одна таблица для всех сущностей. . Они используют одни и те же атрибуты и пул идентификаторов, что означает, что вы можете «добавить» идентификатор, получить объект и проверить тип (Facebook, Instagram и т. Д.).

Обратной стороной является то, что если вы добавили в какой-либо из объектов атрибут, который может быть NULL, вы можете столкнуться с проблемами, если другие объекты не имеют этого атрибута или не нуждаются в нем. Это будет означать, что вы должны установить для данного атрибута фиктивное значение в других объектах, чтобы сохранить их в таблице базы данных.


Наследование таблицы классов решает эту проблему, сохраняя каждую сущность в отдельной таблице, сохраняя при этом возможность совместного использования пул идентификаторов, поскольку доктрина заботится о том, чтобы общие атрибуты сохранялись в таблице базового класса, в то время как все атрибуты, относящиеся к сущности, сохранялись в таблице сущности. Затем таблицы соединяются по идентификатору, отсюда и тип наследования "JOINED" в доктрине.


Вывод:

Используйте наследование одной таблицы, если классы очень похожи и отличаются только определение или реализация функции, но с теми же атрибутами.

Используйте наследование таблиц классов, если классы имеют отдельные атрибуты, которые было бы проблематично хранить в одной таблице.

Используйте наследование конкретной таблицы, если классы на самом деле не связаны друг с другом, а имеют лишь небольшую долю. общих атрибутов. Но это также можно реализовать с помощью особенностей PHP, что, на мой взгляд, проще и гибче в использовании, чем отображаемый суперкласс doctrine. В трейтах PHP вы также можете использовать аннотации доктрины, потому что интерпретатор PHP правильно назначит аннотации классам, в которых вы используете трейты.

Вы по-прежнему сможете использовать свой SocialMediaFactory с шаблоном наследования одной таблицы или таблицы классов.

person SaSchu    schedule 27.07.2015
comment
Я только что решил перейти к наследованию таблицы классов, удалив абстрактную часть, когда вернулся, и вы предложили это. Тем не менее, спасибо. Проблема заключалась в том, что по-пуристски я использую конкретный класс как абстрактный. Я предполагаю, что мне придется задокументировать в файле, что он не должен быть создан. - person Ventus; 28.07.2015