Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [Symfony][Doctrine] Zapytania przy relacjach ManyToMany
marekc12
post 9.08.2011, 14:48:32
Post #1





Grupa: Zarejestrowani
Postów: 37
Pomógł: 0
Dołączył: 1.08.2008

Ostrzeżenie: (0%)
-----


Witam,

pytanie dotyczy Symfony 2.

Wiele książek (tabela book) może być przypisanych do wielu gatunków (tabela genre). Wykorzystałem relację Many-To-Many, Bidirectional.

Wyciągam 20 książek, takich które należą do konkretnego gatunku (po prostu chcę wyświetlić np. tylko książki fantasty), zapytaniem:
  1. $query = $this->getDoctrine()->getRepository('ProjektPageBundle:Book')->createQueryBuilder('b');
  2. $query = $query->join('b.genres', 'g')->where('g.id = gId')->setParameter('gId', $gatunek);
  3. $query = $query->setFirstResult($offset)->setMaxResults(20)->getQuery();
  4. $books = $query->getResult();


wysyłam zmienną $books zawierającą 20 książek do templatki, a w niej wyświetlam je. Przy każdej książce wyświetlam informacje o niej z tabeli book oraz nazwy gatunków do jakich należy dana książka (może być ich kilka). Oto kawałek tamplatki:

  1. {% for book in books %}
  2. <h2>{{ book.title }}, {{ book.titlepl }}</h2>
  3. <p>
  4. Kraj wydania: {{ book.country }}<br />
  5. Gatunki:
  6. {% for genre in book.genres %}
  7. {{ genre.name }}
  8. {% endfor %}
  9. </p>
  10. {% endfor %}


Niby wszystko działa, ale martwi mnie to, że w momencie wyświetlania listy gatunków do których każda książka należy wykonuje się zapytanie do bazy i gdy wyświetlam te 20 książek to dochodzi 20 zapytań. Co oznacza, że pewnie niezbyt dobrze to robię. Jak to powinienem zrobić?

Mam nadzieję, że post jest zrozumiały.

Ten post edytował marekc12 9.08.2011, 14:51:07
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 4)
Crozin
post 9.08.2011, 15:52:05
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Powinieneś jeszcze w zapytaniu zaznaczyć, że chcesz pobrać informacje dot. gatunku: Doctrine\ORM\QueryBuilder::addSelect.
Go to the top of the page
+Quote Post
marekc12
post 9.08.2011, 16:18:47
Post #3





Grupa: Zarejestrowani
Postów: 37
Pomógł: 0
Dołączył: 1.08.2008

Ostrzeżenie: (0%)
-----


  1. ->addSelect('g')


po wciśnięciu tego w zapytanie rzeczywiście dodatkowe zapytania się nie wykonują i gatunki są pobierane od razu razem z książkami, ale teraz niestety widok wyświetla mi przy każdej książce tylko jedną nazwę gatunku ( no bo gatunki są przecież teraz pobierane razem z książkami z tym warunkiem ->where('g.id = gId')-> ). Nie mam pomysłu jak to rozwiązać...

Ten post edytował marekc12 9.08.2011, 16:28:08
Go to the top of the page
+Quote Post
Crozin
post 9.08.2011, 17:17:19
Post #4





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Chcesz pobrać książki należące do danego gatunku, więc skorzystaj z MEMBER OF:
  1. SELECT b, g
  2. FROM ProjektPageBundle:Book b
  3. JOIN b.genres g
  4. WHERE :genre MEMBER OF b.genres
Gdzie parametr :genre to obiekt / id danego gatunku.
Go to the top of the page
+Quote Post
marekc12
post 10.08.2011, 22:42:09
Post #5





Grupa: Zarejestrowani
Postów: 37
Pomógł: 0
Dołączył: 1.08.2008

Ostrzeżenie: (0%)
-----


Próbowałem robić coś podobnego wcześniej, Twój sposób rzeczywiście dobrze działa, ale z tym, że teraz nie da się ustawić ilości pobieranych książek, ponieważ, gdy dam ->setMaxResults(20)-> to np. książkę która zawiera 3 gatunki policzy trzykrotnie i w ten sposób pobierze mniej książek niż 20.

Chodzi mi po prostu o podzielenie wyników na wiele stron po 20 wyników.

EDIT:

Wymyśliłem to tak:

  1. SELECT b, g
  2. FROM ProjektPageBundle:Book b
  3. JOIN b.genres g
  4. WHERE b.id IN ( SELECT b2.id FROM ProjektPageBundle:Book b2 WHERE :gId MEMBER OF b2.genres LIMIT 0, 20 )'


być może tak by zadziałało, problemem pozostaje to, że o ile się orientuję to limity są obsługiwane tylko przez ->SetMaxResults(20)->, w każdym razie mam błąd przy słowie LIMIT.

EDIT2:
Chyba trzeba to zapytanie po prostu zapisać w DQB, ale mimo, że znalazłem parę przykładów to dalej nie wiem jak to zrobić.

EDIT 3:

Udało mi się to zamienić na DQB:

  1. $em = $this->getDoctrine()->getEntityManager();
  2.  
  3. $qb = $em->createQueryBuilder();
  4. $qb2 = $em->createQueryBuilder();
  5.  
  6. $qb2->addSelect('b2.id')->from('ProjektPageBundle:Book','b2')->andWhere(':gId MEMBER OF b2.genres');
  7.  
  8. $qb->addSelect('b')->addSelect('g');
  9. $qb->from('ProjektPageBundle:Book','b');
  10. $qb->join('b.genres','g');
  11. $qb->andWhere($qb->expr()->in('b.id', $qb2->getDQL()))->setParameter('gId', array($filter));
  12.  
  13. $query = $qb->getQuery();
  14. $books = $query->getResult();


Ale to nic nie zmienia, ponieważ jeżeli dam setMaxResults to działa on tylko na ostateczną treśc zapytania, czyli na całe zapytanie.

W każdym razie chyba jedynym rozwiązaniem będzie rozdzielenie tego na 2 oddzielnie wykonujące się zapytania. Pierwsze pobierze 20 numerów id książek i da drugiemu żeby wiedziało które książki pobrać.

Dzięki za pomoc, pozdrawiam!

Ten post edytował marekc12 10.08.2011, 22:43:39
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 14.08.2025 - 10:26