DQL多对多,数不胜数

时间:2012-04-16 14:09:48

标签: php symfony dql

我正在使用带有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 ...*/
}

3 个答案:

答案 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来确保不清除空组。