Doctrine a klasy potomne

W dzisiejszym wpisie chce się z Wami podzielić moimi ostatnimi potyczkami z używaniem klas potomnych w Doctrinie gdy potrzebowałem zrobić bardziej zaawansowane zapytania w DQL.

Na pierwszy ogień przypadek operowania na właściwościach dostępnych tylko w klasie potomnej. Załóżmy, że mamy taką sytuacje – encje Word, przechowującej słówka do wyuczenia w ramach jakiegoś kursu internetowego. Mamy też jej klasę potomną DifficultWord, która posiada dodatkowe właściwości jak np. referencje do mema ułatwiającego zapamiętanie trudnego słowa. Encja Word jest również w relacji z uczestnikiem kursu. Gdybyśmy chcieli uzyskać informacje dla jakiego uczestnika przypisany jej konkretny mem z trudnym słówkiem to nie możemy operować tutaj encją Word (pułapka szczelinowa). W takim przypadku musimy złożyć zapytanie operując na jej klasie potomnej.

$qb = $this->createQueryBuilder('md');
$qb->join('Entity\DifficultWord', 'd', Join::WITH, 'md.difficultWord = d.id');
$qb->andWhere('d.mem = :mem');
$qb->setParameter('mem', $mem);

Całość sprowadza się tutaj do wykonania złączenia tabeli uczestników z ich trudnymi słówkami (join) i wyfiltrowanie właściwego mema (where).

Znacznie trudniejszy przypadek dotyczył operowaniem encją rodzica, ale w taki sposób jakby był klasą potomną. Trzymając się przytoczonego przykładu załóżmy, że chcemy pobrać listę słów, których nauczył się uczestnik kursu. Musimy tutaj pominąć trudne słowa. Jednocześnie pamiętamy, że przecież trudne słowa są podzbiorem słów jako takich. Stąd warunek taki jak poniżej się nie sprawdzi.

$qb->andWhere($qb->expr()->isInstanceOf('w', Word::class));

Po długich godzinach przeszukiwania stackoverflow znalazłem rozwiązanie działające z nowszymi wersjami Doctrina. Opiera się ono na wykorzystaniu dyskriminatora danej encji zamiast typu klasy (gdzie word jest właśnie nazwą dyskriminatora pożądanej encji, zgodnie z adnotacją DiscriminatorMap).

$qb->andWhere('w INSTANCE OF :type');
$qb->setParameter('type', 'word');

…i pomyśleć, że to tylko zwykłe zapytania SELECT.