【问题标题】:Doing a COUNT(*) GROUP BY based on a ManyToMany relation using DQL?使用 DQL 基于多对多关系执行 COUNT(*) GROUP BY?
【发布时间】:2018-10-25 20:42:30
【问题描述】:

我有两个实体 PersonSkill,其中一个 Person 可能有多种技能。

/**
 * @ORM\Entity(repositoryClass="App\Repository\PersonRepository")
 */
class Person
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Skill", inversedBy="people")
     */
    private $skills = [];

    // other fields and getters/setters
}

技能

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\SkillRepository")
 */
class Skill
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Person", mappedBy="skills")
     */
    private $people = [];

    // other fields and getters/setters
}

我有一个表单,可以按技能过滤人员,每个技能都是一个复选框,我想要拥有该技能的人数以及复选框的标签。

结果是:

我使用以下本机查询让它工作:

SELECT s.id, COUNT(*) AS c
FROM skill s
JOIN person_skill ps   /* table required by the M2M relation between person and skill */
ON s.id = ps.skill_id
GROUP BY s.id

如您所见,我需要在 ManyToMany 表上进行 JOIN 才能获得这些计数。

如何使用 Doctrine 的 DQL 而不是使用本机查询来做到这一点?

【问题讨论】:

    标签: php doctrine dql


    【解决方案1】:

    实际上,当用关系映射实体时,Doctrine 使用了一个名为 ArrayCollection 的自定义对象。

    它带有很多方法,例如filter()和count()。

    如果需要,您可以向技能实体添加一个方法,该方法将使用 ArrayCollection (people) 的 count 方法。

    为确保正确使用 ArrayCollection,您必须像这样更改 Skill 类:

    class Skill
    {
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\ManyToMany(targetEntity="App\Entity\Person", mappedBy="skills")
         */
        private $people; //<-- Removed the default array definition
    
        public function __construct()
        {
            $this->people = new ArrayCollection(); //Add this line in your constructor
        }
    
        public function countPeople()
        {
            return $this->people->count(); //Will return the number of people joined to the skill
        }
    
        // other fields and getters/setters
    }
    

    【讨论】:

    • ArrayCollection 上的 -&gt;filter 需要先加载整个表。它不会扩展:-)。还是谢谢
    • 我了解可扩展性问题。我阅读了更多关于关联的内容,并想与您分享Extra Lazy Loading。启用后,您可以在不加载集合的情况下使用计数功能(无需过滤器)。虽然,这取决于您打算在应用程序中使用该关联的程度。祝你有美好的一天!
    • 感谢您的信息 :) 看起来没有我的解决方案那么 hacky!干杯
    【解决方案2】:

    好的,我找到了解决办法:

    $rows = $this->_em->createQueryBuilder('s')
                      ->select('s.id, COUNT(p.id) AS c')
                      ->from(Skill::class, 's')
                      ->join('s.people', 'p')
                      ->groupBy('s.id')
                      ->getQuery()
                      ->getArrayResult();
    

    它会生成以下查询:

    SELECT s0_.id AS id_0, COUNT(p1_.id) AS sclr_1 
    FROM skill s0_ 
    INNER JOIN person_skill p2_
    ON s0_.id = p2_.skill_id 
    INNER JOIN person p1_ 
    ON p1_.id = p2_.person_id 
    GROUP BY s0_.id
    

    【讨论】:

      猜你喜欢
      • 2020-09-08
      • 1970-01-01
      • 1970-01-01
      • 2015-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-03
      • 2013-11-02
      相关资源
      最近更新 更多