2012-04-16 6 views
7

Ich benutze Symfony 2 mit Doctrine, und ich habe zwei Entitäten in einer Verknüpfung von vielen zu vielen verbunden. Angenommen, ich habe zwei Entitäten: Benutzer und Gruppe, und die zugehörigen Tabellen in db sind Benutzer, Gruppen und Benutzergruppen.DQL viele zu viele und zählen

Ich würde gerne die Top 10 am meisten besetzten Gruppen in DQL, aber ich weiß nicht die Syntax, um Abfragen für die Join-Tabelle (users_groups) durchzuführen. Ich habe bereits das Doktrin-Handbuch gelesen, aber ich habe die Lösung nicht gefunden. Ich denke, ich muss noch eine Menge über DQL lernen.

Im Klar SQL, das wäre:

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

Können Sie mir diese dql zu übersetzen bitte helfen?

Update (Klassen):

/** 
* 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 ...*/ 
} 

Antwort

6

Es ohne zu sehen, die tatsächlichen Klassen nicht einfach ist, aber Sie erraten haben eine many-to-many bidirektionale Beziehung:

$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:

Sie nicht müssen eine bidirektionale Beziehung verwenden, können Sie qu Ery umgekehrt:

$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

Danke, das Problem ist, dass Gruppenentität keine Verknüpfung namens Benutzer hat. So bekomme ich die Ausnahme: [Semantischer Fehler] Zeile 0, Spalte 72 in der Nähe von 'GROUP BY g.id': Fehler: Klassen Entity \ E_Group hat keine Assoziation namens Benutzer. Ich habe die Frage mit den tatsächlichen Klassen aktualisiert. Soll ich der Entität E_Group das Attribut $ users hinzufügen, um die bidirektionale Viele-zu-Viele-Beziehung herzustellen? –

+0

vielen dank. Ich hatte sowieso eine bidirektionale Beziehung. Das war nur eine Dummy-Abfrage, um zu verstehen, wie es funktioniert. Danke noch einmal –

0

Für diejenigen, die die Abfrage mit Lehre der QueryBuilder statt mit DQL direkt nehmen diese Lösung aufbauen wollen.

Bitte beachten Sie, dass mein Problem nicht war, die oberen Benutzergruppen zu bekommen, aber technisch ist das Problem ziemlich ähnlich zu meinem. Ich arbeite mit Posts (wie Artikel/Blog-Posts) und Tags, die Posts hinzugefügt werden. Ich musste eine Liste verwandter Beiträge ermitteln (die durch dieselben Tags identifiziert wurden). Diese Liste musste nach Relevanz sortiert werden (je mehr gleiche Tags ein anderer Beitrag hat, desto relevanter ist er).

Dies ist die Methode meiner PostRepository Klasse:

/** 
* 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; 
} 

Danke @ Tom Imrei für Sie Lösung. Auch die Antwort #26549597 war sehr hilfreich.