uczę się Symfony2 od jakichś 3 tygodni. Do tej pory jakoś znajdywałem rozwiązania zagadek, które napotykałem. Tym razem potrzebuję pomocy.
Jest projekt aplikacji do tłumaczenia tekstów. Celem jest wystawienie aplikacji, dzięki której tłumacze będą mogli tłumaczyć teksty z aplikacji lub dokumentacji. Są 4 tabele, które zawierają teksty do tłumaczenia. Definicje bazy danych:
BDK\TranslatorBundle\Entity\TranslatorText: type: entity table: translator.tbl_txt_text fields: txt_id: id: true type: integer generator: strategy: AUTO txt_name: type: text notnull: true unique: true txt_description: type: text notnull: true oneToMany: body: targetEntity: TextBody mappedBy: txt_id indexBy: ted_txt_id manyToOne: txt_tap_id: targetEntity: TranslatorApp inversedBy: text_body joinColumn: name: txt_tap_id referencedColumnName: tap_id onDelete: CASCADE lifecycleCallbacks: { }
BDK\TranslatorBundle\Entity\TranslatorLang: type: entity table: translator.tbl_lng_language id: lng_id: type: integer generator: strategy: AUTO fields: lng_name: { type: string, length: 100 } lng_shortcut: { type: string, length: 10 } lng_img: { type: text, nullable: true } oneToMany: text: targetEntity: TextBody mappedBy: lng_id indexBy: ted_lng_id lifecycleCallbacks: { }
BDK\TranslatorBundle\Entity\TextBody: type: entity table: translator.tbl_ted_text_body fields: ted_id: id: true type: integer generator: strategy: AUTO ted_description: type: text notnull: true ted_body: type: text notnull: true manyToOne: ted_txt_id: targetEntity: TranslatorText inversedBy: body joinColumn: name: ted_txt_id referencedColumnName: txt_id onDelete: CASCADE ted_lng_id: targetEntity: TranslatorLang inversedBy: text joinColumn: name: ted_lng_id referencedColumnName: lng_id onDelete: CASCADE lifecycleCallbacks: { }
BDK\TranslatorBundle\Entity\TranslatorApp: type: entity table: translator.tbl_tap_application fields: tap_id: type: integer id: true generator: strategy: AUTO tap_name: type: text length: null tap_prefix: type: text length: null tap_desc: type: text length: null oneToMany: text_body: targetEntity: TranslatorText mappedBy: tap_id indexBy: txt_tap_id lifecycleCallbacks: { }
Wszystko jest pięknie, dopóki nie chcę zmienić standardowo wygenerowanych zapytań do obsługi listy tekstów.
Zapytanie, którym normalnie coś takiego bym zrobił wygląda następująco:
-- Lista wszystkich tekstów SELECT * FROM (SELECT * FROM translator.tbl_txt_text, translator.tbl_lng_language) AS a LEFT JOIN translator.tbl_ted_text_body ON (ted_txt_id=a.txt_id AND ted_lng_id=a.lng_id)
...nie jestem w stanie przełożyć go na DQL albo utworzyć za pomocą klasy QueryBilder.
Kiedy tylko próbuję połączyć tabele, które nie mają połączenia "wprost" podnosi się krzyk, że:
[Semantical Error] line 0, col 106 near 'b': Error: Identification Variable BDK\TranslatorBundle\Entity\TextBody used in join path expression but was not defined before.
Próbowałem połączyć na początek dwie tablice, ale... się nie udało. Kod, który powoduje problem:
$qb = $em->createQueryBuilder(); $qb->add('select', 'a') ->add('select', 'b') ->from('BDK\TranslatorBundle\Entity\TranslatorLang', 'a') ->leftJoin('BDK\TranslatorBundle\Entity\TextBody', 'b'); $query = $qb->getQuery(); $tmp = $query->getResult();
Oczywiście, mogę zrobić to w ten sposób:
$sql = 'SELECT * FROM (SELECT * FROM translator.tbl_txt_text, translator.tbl_lng_language) AS a ' .'LEFT JOIN translator.tbl_ted_text_body ON (ted_txt_id=a.txt_id AND ted_lng_id=a.lng_id)'; $conn = $this->getDoctrine()->getConnection(); $stmt = $conn->executeQuery($sql); $list_of_texts = $stmt->fetchAll();
Potem dotarłem, do informacji, że DQL najwyraźniej nie obsługuje takich rzeczy. Może jednak znasz rozwiązanie, które pozwoli mi na użycie standardowych rozwiązań w zgodzie z duchem Doctrine2. Chodzi oczywiście o użycie np. pagera KnpPaginatorBundle czy innych temu podobnych. Może użycie jakiegoś widoku? Na razie skończyły mi się pomysły na eleganckie rozwiązanie.
Dlaczego interesuje mnie to zapytanie? Bardzo łatwo wyszukać co jest do tłumaczenia, a to ważne dla przyszłego użytkownika.