我正在使用带有Doctrine的Symfony 2,并且我有两个实体加入了多对多关联。 假设我有两个实体:User和Group,db上的相关表是users,groups和users_groups。
我想在DQL中获得人口最多的10个群组,但我不知道在连接表(users_groups)上执行查询的语法。我已经查看了Doctrine手册,但我没有找到解决方案,我想我还有很多东西可以学习DQL。
在简单的sql中:
select distinct group_id, count(*) as cnt from users_groups group by group_id order by cnt desc limit 10
你能帮我把它翻译成DQL吗?
更新(课程):
/**
* 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 ...*/
}
答案 0 :(得分:6)
如果没有看到实际的课程,这并不容易,但猜测你有多对多的双向关系:
$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();
更新:
您不必使用双向关系,您可以反过来查询:
$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;";
答案 1 :(得分:1)
对于那些想要使用Doctrine QueryBuilder
而不是直接使用DQL构建查询的人,请使用此解决方案。
请注意,我的问题不是获得顶级用户群,但从技术上讲,问题与我的问题非常相似。我使用帖子(如文章/博客文章)和添加到帖子的标签。我需要确定相关帖子的列表(由相同的标签标识)。该列表必须按相关性排序(相同的标签,其他帖子的相关性越高)。
这是我PostRepository
类的方法:
/**
* 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;
}
谢谢@Tom Imrei的解决方案。答案#26549597也非常有用。
答案 2 :(得分:0)
要改善汤姆的答案,可以使用DQL的HIDDEN
关键字。这样,结果就不会包含无用的cnt
列,并且不需要Arvid的array_map
解决方案(对于较大的查询,这可以显着加快结果的速度)。
OP的问题是获取排名前10位的组,而不仅仅是ID。
它看起来像这样:
$query = 'SELECT g, HIDDEN COUNT(u.id) AS cnt FROM Entity\Group g LEFT JOIN g.users u ORDER BY cnt DESC';
$groups = $em->createQuery($query)->setMaxResults(10)->getResult();
另外,请注意使用LEFT JOIN
来确保不清除空组。