2012-04-16 15 views
7

Używam Symfony 2 z Doctrine, i mam dwie jednostki połączone w wiele do wielu stowarzyszeń. Załóżmy, że mam dwie jednostki: użytkownik i grupę, a powiązane tabele na db to użytkownicy, grupy i grupy users_groups.DQL wiele do wielu i liczyć

Chciałbym uzyskać 10 najbardziej zaludnionych grup w DQL, ale nie znam składni do wykonywania zapytań w tabeli join (users_groups). Już zapoznałem się z podręcznikiem do Doctrine, ale nie znalazłem rozwiązania, myślę, że wciąż muszę się dużo nauczyć o DQL.

w prostym SQL, które byłyby:

select distinct group_id, count(*) as cnt from users_groups group by group_id order by cnt desc limit 10 

Czy możesz mi pomóc przetłumaczyć to DQL?

Aktualizuj (klas):

/** 
* Entity\E_User 
* 
* @ORM\Table(name="users") 
* @ORM\Entity 
*/ 
class E_User 
{ 
    /** 
    * @ORM\ManyToMany(targetEntity="E_Group", cascade={"persist"}) 
    * @ORM\JoinTable(name="users_groups", 
    *  joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="cascade")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id", onDelete="cascade")} 
    *) 
    */ 

    protected $groups; 

    /** 
    * @var integer $id 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @var string $name 
    * 
    * @ORM\Column(name="name", type="string", length=255) 
    */ 
    private $name; 

    /* ... other attributes & getters and setters ...*/ 
} 


/** 
* Entity\E_Group 
* 
* @ORM\Table(name="groups") 
* @ORM\Entity 
*/ 
class E_Group 
{ 
    /** 
    * @var integer $id 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @var string $name 
    * 
    * @ORM\Column(name="text", type="string", length=255) 
    */ 
    private $name; 

    /* ... other attributes & getters and setters ...*/ 
} 

Odpowiedz

6

To nie jest łatwe, nie widząc rzeczywistych klas, ale zgadując masz wiele-do-wielu dwukierunkowa zależność:

$dql = "SELECT g.id, count(u.id) as cnt FROM Entity\Group g " . 
    "JOIN g.users u GROUP BY g.id ORDER BY cnt DESC LIMIT 10;"; 
$query = $em->createQuery($dql); 
$popularGroups = $query->getArrayResult(); 

UPDATE:

Nie musisz mieć do używać relacji dwukierunkowej, możesz qu ERY na odwrót:

$dql = "SELECT g.id, count(u.id) as cnt FROM Entity\User u " . 
    "JOIN u.groups g GROUP BY g.id ORDER BY cnt DESC LIMIT 10;"; 
+0

dzięki, problem polega na tym, że encja Grupy nie ma powiązania zwanego użytkownikiem. Dostaję wyjątek: [Błąd semantyczny] linia 0, col 72 w pobliżu GROUP BY g.id ': Błąd: Klasa Entity \ E_Group nie ma powiązania o nazwie użytkownicy. Uaktualniłem pytanie do faktycznych klas. Czy mam dodać atrybut $ users do encji E_Group, aby utworzyć dwukierunkową relację wiele do wielu? –

+0

wielkie dzięki. Ostatecznie skończyłem z dwukierunkową relacją. To było tylko fałszywe zapytanie, aby zrozumieć, jak to działa. dzięki jeszcze raz –

0

Dla tych, którzy chcą budować zapytania z doktryny QueryBuilder zamiast korzystania DQL bezpośrednio brać tego rozwiązania.

Proszę zauważyć, że moim problemem nie było uzyskanie najlepszych grup użytkowników, ale technicznie problem jest podobny do mojego. Pracuję z postami (takimi jak artykuły/posty na blogu) i tagami dodawanymi do postów. Potrzebowałem ustalić listę pokrewnych postów (identyfikowanych przez te same tagi). Ta lista musiała być sortowana według trafności (im więcej tagów ma ten sam znacznik).

Jest to metoda mojego PostRepository klasy:

/** 
* Finds all related posts sorted by relavance 
* (from most important to least important) using 
* the tags of the given post entity. 
* 
* @param Post $post 
* 
* @return POST[] 
*/ 
public function findRelatedPosts(Post $post) { 
    // build query 
    $q = $this->createQueryBuilder('p') 
     ->addSelect('count(t.id) as relevance') 
     ->innerJoin('p.tags', 't') 
     ->where('t.id IN (:tags)') 
     ->setParameter('tags', $post->getTags()) 
     ->andWhere('p.id != :post') 
     ->setParameter('post', $post->getId()) 
     ->addGroupBy('p.id') 
     ->addOrderBy('relevance', 'DESC') 
     ->getQuery(); 

    // execute query and retrieve database result 
    $r = $q->execute(); 

    // result contains arrays, each array contains 
    // the actual post and the relevance value 
    // --> let's extract the post entities and 
    // forget about the relevance, because the 
    // posts are already sorted by relevance 
    $r = array_map(function ($entry) { 
     // first index is the post, second one 
     // is the relevance, just return the post 
     return reset($entry); 
    }, $r); 

    // array of posts 
    return $r; 
} 

Dziękuję @Tom Imrei dla ciebie rozwiązanie. Również odpowiedź #26549597 była bardzo pomocna.

Powiązane problemy