Symfony & Propel podwójny join do jednej tabeli
Artykuł ten jest poradą jak rozwiązać problem podwójnego łączenia z jedna tablicą. Propel w wersji 1.2 i 1.3 nie obsługuje tego mechanizmu. Być może będzie go obsługiwał w wersji 2.0
Po co nam takie łączenia tabel?
Cóż, weźmy przykład z życia wzięty: lista artykułów powiązanych z konkretnym artykułem. Struktura tabeli related_articles wygląda następująco:
kolumny: (id, article_id, related_id). article_id i related_id są kluczami obcymi do tabeli zawierającej artykuły.
Problem
Wywołując w propelu doSelectAll lub używając generatora admina w symfony, propel się sypie nie mogąc znaleźć odpowiedniej tabeli. Cóż, możliwości propela nie przeskoczymy, wiec musimy ten problem obejść.
Rozwiązanie
Spędziłem kilka godzin na różnych kombinacjach. W końcu doszedłem do prowizorycznego rozwiązania, które rozwiązało mój problem. Otóż, utworzyłem widok (ang.view) zawierający wszystkie elementy tabeli Articles
![]()
CREATE VIEW Article2 as SELECT * FROM Article;
Prawdopodobnie można też wykorzystać engine merge. Jednak widoki wydają mi się bardziej przenośnym rozwiązaniem. Następnie dodajemy nową tablę w schemacie bazy (schema.yml lub schema.xml). Zmieniamy wartość foreignTable dla kolumny related_id na 'Article2'. Przebudowujemy model i w ten sposób przechytrzyliśmy propela
Kilka udoskonaleń pliku schema.xml/schema.yml
Aby czasem propel nie utworzył nam tabeli zamiast widoku ustawiamy parametr tabeli skipSQL na true. Powinniśmy także ustawić opcje readOnly na true.
Podsumowanie
To jest bardzo krótki artykuł, a właściwie tylko porada. Mam jednak nadzieje, że komuś zaoszczędzi kilku godzin nerwów i szukania, co jest nie tak.
#1
Często takie patent używam w SF, pewne operacje lepiej przeżucić na widok i nie mieszac do tego Propela. Zastanawiam sie czy Twojego problemu nie da sie rozwiazac metoda Join. Jak znajde chwilke to sprawdze.
#2
Właśnie z tym przerzuceniem operacji na widok jest problem. Poza tym to nie ładne. Mozna oczywiscie rozbic takie coś na wiele zapytań. Joinami właśnie się nie da z tego względu, że propel nie obsługuje lączenia 2 razy tej samej tabeli :)
#3
Autor pewnie ma na myśli coś w tym stylu: "SELECT * FROM list INNER JOIN article ON list.article_id = article.id INNER JOIN article ON list.related_id = article.id" niestety takie coś nie działa, dlatego trzeba zastosować coś takiego: "SELECT * FROM list INNER JOIN article ON list.article_id = article.id INNER JOIN article AS related ON list.related_id = related.id" niestety propel nie umie dodawać JOINów zmieniając nazwy tabel
#4
Po chwili szukania i czytania okazało się że jednak propel umie robić aliasy dla tabel przy JOINach. Criteria->addAlias('innaNazwa', nazwaPeer::TABLE_NAME) przygotowuje zmianę nazwy, a w Criteria->addJoin używa się nazwaPeer::alias('innaNazwa' nazwaPeer::KOLUMNA). Pozostało mi to jeszcze pożenić z sfPropelCustomJoinHelper i może będzie to miało ręce i nogi ;-).

