I'm using Symfony 2 with Doctrine, and I've got two entities joined in a many to many association. Let's say I have two entities: User and Group, and the related tables on db are users, groups and users_groups.
I'd like to get the top 10 most populated groups in DQL, but I don't know the syntax to perform queries on the join table (users_groups). I already looked on the Doctrine manual but I didn't found the solution, I guess I still have a lot to learn about DQL.
In plain sql that would be:
select distinct group_id, count(*) as cnt from users_groups group by group_id order by cnt desc limit 10
Can you please help me to translate this to DQL?
Update (classes):
/**
* 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 ...*/
}
It's not easy without seeing the actual classes, but by guessing you have a many-to-many bidirectional relationship:
$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:
You don't have to use a bidirectional relationship, you can query the other way around:
$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;";
For those who want to build the query with Doctrine's QueryBuilder
instead of using DQL directly take this solution.
Please note that my problem wasn't to get the top user groups, but technically the problem is pretty similar to mine. I work with posts (like articles/blog posts) and tags that are added to posts. I needed to determine a list of related posts (identified by same tags). That list had to be sorted by relevance (the more same tags another post has the more relevant it is).
This is the method of my PostRepository
class:
/**
* 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;
}
Thank you @Tom Imrei for you solution. Also the answer #26549597 was very helpful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With