Encje doctrinowe i dziedziczenie – Doctrine discriminator

To już ostatnia część serii wpisó o Doctrinie. W pierwszej poznałeś podstawy encji, w drugiej szczegóły dotyczące relacji. Teraz przyszedł czas na coś bardziej zaawansowanego, czyli dziedziczenie. Doctrine to ORM, czyli sposób odwzorowania obiektowej architektury systemu informatycznego na bazę danych. Czym jest doctrine discriminator? Skoro obiekty to i dziedziczenie, ale jak wykonać dziedziczenie na tabelach? Czy to jest w ogóle możliwe? Tak! Tylko jak? Już wszystko tłumaczę.

Doctrine discriminator

Discriminatory to właśnie sposób na odwzorowanie dziedziczenia encji. Najlepiej wytłumaczyć to na przykładzie. W aplikacji jest możliwość komentowania zarówno wydarzeń, jak i prelegentów/szkoleniowców. Można zrealizować to na kilka sposobów.

  1. Stworzenie tabeli z komentarzami z dwoma kolumnami opcjonalnymi albo do wydarzenia, albo do prowadzącego.
  2. Stworzenie jednej kolumny z relacją do wydarzenia/prowadzącego i kolumny z typem komentarza.
  3. Trzymanie komentarzy w osobnych tabelach.
  4. Discrimintor na komentarzu.

Myślę, że pierwsze trzy rozwiązania są jasne. Jeśli nie to daj znać, a rozwinę je. Jednak teraz skupię się na ostatnim, bo myślę, że jest najmniej znane.

W Symfony discriminatorach tworzymy normalnie klasy dziedziczące (EventComment, LecturerComment) i klasę abstrakcyjną (AbstractComment). Jeśli chodzi o zawartość tych encji, dzielimy je tak jak w przypadku zwykłego dziedziczenia, czyli elementy wspólne umieszczamy w klasie abstrakcyjnej a różnice w klasach dziedziczących. Aby teraz z takiej struktury stworzyły się tabele i działało mapowanie klas doctrinowych, wystarczy skonfigurować discriminator w adnotacjach lub plikach yml. Tak jak w poprzedniej części serii tutaj umieszczę przykłady konfiguracji w adnotacjach.

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="comment")
 *
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({
 * "event" = "OpinionBundle\Entity\Comment\EventComment",
 * "lecturer" = "OpinionBundle\Entity\Comment\LecturerComment",
 * })
 * @ORM\InheritanceType("SINGLE_TABLE")
 */
abstract class AbstractComment
/**
 * @ORM\Table(name="event_comment")
 * @ORM\Entity()
 */
class EventComment extends AbstractComment

Zaczynając od klasy nadrzędnej. Tak jak w każdej encji określamy nazwę tabeli, na którą będzie się mapować. DicriminatorColumn to kolumna, po której Doctrine będzie decydował, z którą klasą podrzędną ma do czynienia, jest ona jakby relacją do klas podrzędnych. W DiscriminatorMap spisujemy wszystkie możliwości, czyli wszystkie klasy dziedziczące, a string, do którego przypisujemy klasę, będzie kodem klasy dziedziczącej i wartością wpisywaną do kolumny type – DiscriminatorColumn.

Na koniec zostawiłam InheritanceType. W moim przypadku przyjmuje wartość SINGLE_TABLE, to tryb gdzie wszystkie dane mamy w jednej tabeli i wszystkie klasy dziedziczące mapują się na jedną tabelę główną. W moim przypadku to najlepsze rozwiązanie, bo jedyną różnicą, jaka występuje między klasami dziedziczącymi to relacja do wydarzenia/prowadzącego. Dlatego tworzenie osobnej tabeli tylko po to, aby trzymać tam idk relacji, byłoby przerostem formy nad treścią.

Jest jeszcze opcja JOINED. Oznacza ona, że klasy mapują się na kilka tabel. Każda ma swoją i tabele klasy abstrakcyjnej – główną.

Uwaga!

Jeśli klasa, z której dziedziczysz, jest zwykłą klasa, nie abstrakcyjną, musisz zawrzeć ją jako opcje w DiscriminatorMap, bo inaczej dostaniecie przepiękny fail:

[Doctrine\ORM\Mapping\MappingException]
Entity ‘OpinionBundle\Entity\AbstractComment’ has to be part of the discriminator map of ‘OpinionBundle\Entity\AbstractComment’ to be properly mapped in the inheritance hierarchy. Alternatively you can make ‘OpinionBundle\Entity\Comment’ an abstract class to avoid this exception from occurring.

Aby ominąć ten wymóg i nie zawierać klasy nadrzędnej jako opcji discriminatora należy klasę nadrzędną zamienić na klasę abstrakcyjną. Dokładnie tak jak mówi błąd.

Rozmieszczenie plików

Bardzo ważną rzeczą, o której nie wspomina dokumentacja, a szkoda, jest temat nazwnictwa.  Chyba że ja po prostu tego nie znalazłam. Jak nazywać i gdzie umieszczać klasy orm.yml dla encji zagnieżdżonych w folderach np. w ścieżce Bundle\Entity\ . Tak jak u mnie  OpinionBundle\Entity\Comment\EventComment.php. Trochę mi zajeło, aby przekopać stacka i strony pokrewna, ale udało się. Mój drogi/Moja droga, jeśli chcesz zorganizować sobie sensownie encje w folderach i wybierasz definicje mapowania yml, to pamiętaj!

Plik definiujący mapowanie encji OpinionBundle\Entity\Comment\EventComment.php. znajdziesz w ścieżce OpinionBundle\Resources\config\doctrine\Comment.EventComment.orm.yml

Jeśli decydujesz się na adnotacje, to nie masz tego problemu, bo definicje zawiera sama encja. Gdy już to wiem to nie zmienam zdania i dalej preferują podejście yml. Jednak podczas szukania rozwiązania przeszło mi kilka razy przez głowę, że trzeba było wybrać adnotacje. A co Ty wolisz? Jak konfigurujesz swój projekt? Może spotkałeś się już z tym problemem i znasz jakieś ciekawostki na ten temat doctrine discriminator ? Pisz śmiało w komentarzu!

Podobne posty

Jestem programistką, która lubi mieć ręce pełne roboty. Do życia potrzebuje komputera z internetem i kubka gorącej kawy. Więcej na stronie o mnie.

Comments

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here