Доктрина ORM и фабрики със стратегия за абстрактни класове

Така че се натъкнах на това препятствие, при което трябва да създам абстрактен клас и фабрика, за да създам обекти от по-специфични класове, които разширяват абстрактния клас и прилагат по-специфични обектни методи.

Просто казано, имам клас SocialMediaAbstract. Разширяващите класове са Facebook, Instagram и имплементират SocialMediaInterface. Facebook, Instagram и т.н. всички се записват в db, с идентификатор, име и още няколко свойства, които се използват сред разширяващите се класове, следователно един абстрактен клас.

Тъй като искам да мога да правя заявки за няколко неща от обектите на социалните медии и всяка социална медийна платформа има свои собствени API за нея, направих интерфейса и създадох различните класове, така че всички те да могат да имат свои собствени реализации на тези методи.

Сега проблемът, разбира се, е с моя абстрактен клас и доктрина. Doctrine казва това на техния уебсайт по отношение на наследството:

Картографиран суперклас не може да бъде обект, не може да се прави заявка [...]

Сега, ако имах SocialMediaFactory и хвърлих ID, бих искал да получа обратно съответния обект от, например, клас Facebook или Instagram. Не искам да знам коя точно социална медия е, когато ги събирам. Това е проблем с доктрината, поне така мисля.

Пропускам ли нещо, фабричният модел все още ли е възможен? Или наистина трябва просто да премахна абстрактния клас и да създам фабрика, която търси във всяка таблица на клас за внедряване на SocialMediaInterface, което изглежда изключително неефективно и неподлежащо на поддръжка, когато приложението стане по-голямо.

Всякакви идеи или насоки ще бъдат оценени, тъй като съм сигурен, че този проблем трябва да се е появявал по-често. Опитах да потърся в Google и в самия Stackoverflow, но не можах да получа подходящи въпроси или отговори.

Благодаря много предварително.

РЕДАКТИРАНЕ: Попаднах на тази интересна възможност: Наследяване на класова таблица. Това би означавало добавяне на:

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

към моя код. Имах големи надежди, но за съжаление валидаторът ми даде тази грешка:

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

Суперкласовете на shame mapper не се поддържат.

РЕДАКТИРАНЕ 2/ЗАКЛЮЧЕНИЕ: Реших да използвам наследяване на Class Table (точно както предлага отговорът по-долу). Премахването на резюмето от класа направи възможно все още да използвам моята фабрика.

Сега обаче използвам конкретен клас като абстрактен клас, което се чувства погрешно. Документирал съм в 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, може да се натъкнете на проблеми, ако другите обекти нямат този атрибут или не се нуждаят от него. Това би означавало, че трябва да зададете дадения атрибут на фиктивна стойност в другите обекти, за да ги запишете в таблицата на базата данни.


Наследяването на класова таблица преодолява този проблем, като запазва всеки обект в собствена таблица, като същевременно може да споделя пула от идентификатори, тъй като доктрината се грижи общите атрибути да бъдат записани в таблицата на базовия клас, докато всички атрибути, специфични за даден обект, са записани в таблицата на обекта. След това таблиците се обединяват от id, оттук и типът на наследяване "JOINED" в доктрината.


Заключение:

Използвайте наследяване на една таблица, ако класовете са много сходни и се различават само в дефиниране или изпълнение на функция, но имат същите атрибути.

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

Използвайте наследяване на конкретна таблица, ако класовете не са наистина свързани един с друг, а споделят само малко количество на общи атрибути. Но това може да се приложи и чрез чертите на PHP, което според мен е по-лесен и по-гъвкав за използване от картографирания супер клас на доктрината. В PHP характеристика можете също да използвате анотациите на доктрината, тъй като PHP интерпретаторът правилно ще присвои анотациите към класовете, в които използвате чертите.

Все още трябва да можете да използвате своя SocialMediaFactory с шаблон за наследяване на единична таблица или таблица на клас.

person SaSchu    schedule 27.07.2015
comment
Току-що бях решил да използвам наследяването на Class Table, като премахнах абстрактната част, когато се върнах и вие предложихте това. Все пак благодаря. Проблемът беше, че по пуристичен начин използвам конкретен клас като абстрактен клас. Предполагам, че ще трябва да документирам във файла, че не трябва да се инстанцира. - person Ventus; 28.07.2015