【问题标题】:How to build a Doctrine query which would find elements with missing particular type of OneToMany relation?如何构建一个 Doctrine 查询来查找缺少特定类型的 OneToMany 关系的元素?
【发布时间】:2021-03-17 16:29:19
【问题描述】:

假设我们有两个处于 OneToMany 关系的实体,例如:

User 实体与 Rewards 实体具有 OneToMany 关系,并且每个 Reward 可能有不同的type:例如'forOnboarding'、'forReferring'、'forBeingReerred'。

User {
    /**
     * @var Reward[]
     *
     * @ORM\OneToMany(targetEntity="Reward", mappedBy="rewardee"}
     */
    private $rewards;
}


Reward {
    /*
     * @var User
     *
     * @ORM\ManyToOne(targetEntity="User", inversedBy="rewards")
     * @ORM\JoinColumn(nullable=false)
     */
    private $rewardee;

    /** @var string */
    private $type;
}

很容易找到与确切奖励类型相关的用户,例如:

    public function findUsersWithRewardType(string $rewardType): ?array
    {
        $qb = $this->createQueryBuilder('u')
            ->leftJoin('u.rewards', 'r')
            ->where('r.type = :reward_type')
            ->setParameter('reward_type', $rewardType);
        $query = $qb->getQuery();
        $result = $query->getResult();

        return $result;
    }

现在我想构建一个查询来查找所有缺乏特定类型奖励的用户。这有点棘手,所以任何帮助将不胜感激!

由于 OneToMany 关系的性质,我不能简单地将条件从等于更改为不等于,也不能使用关系为空的条件(如下面的示例所示),因为它不会返回有效结果因为用户可能与其他类型的奖励有关系。

    public function findUsersWithoutRewardType(string $rewardType): ?array
    {
        $qb = $this->createQueryBuilder('u');
        $qb
            ->leftJoin('u.rewards', 'r')
            ->where('r.type = :reward_type')
            ->setParameter('reward_type', $rewardType)
            ->andWhere('r is NULL');
        $query = $qb->getQuery();
        $result = $query->getResult();

        return $result;
    }

PS:即使所有用户都缺乏与奖励的关系,上述方法也不起作用。可能是因为属性 User->rewards 实际上是 ArrayCollection 并且从未设置为 null。

谢谢!

【问题讨论】:

    标签: symfony doctrine-orm doctrine symfony4


    【解决方案1】:

    我设法使用以下 DQL 找到了一个可行的解决方案:

        public function findUsersWithoutRewardType(string $rewardType): ?array
        {
            if (!in_array($rewardType, Reward::TYPE__OPTIONS)) {
                new \Exception(sprintf('Invalid type %s. Possible options are: %s.', $rewardType, implode(',', Reward::TYPE__OPTIONS)));
            }
    
            $query = $this
                ->getEntityManager()
                ->createQuery('SELECT u.username FROM App\Entity\User u WHERE u.enabled = true AND NOT EXISTS (SELECT  1 FROM App\Entity\Reward r WHERE u = r.rewardee AND r.type = :reward_type) ORDER BY u.createdAt DESC')
                ->setParameters(['reward_type' => $rewardType]);
            $users = $query->getResult();
    
            return $users;
        }
    

    有没有办法实现与查询生成器等效的功能?

    【讨论】:

      【解决方案2】:

      我也找到了一个与查询生成器等效的解决方案:

          public function findUsersWithoutRewardType(string $rewardType, $maxResults = 10): ?array
          {
              if (!in_array($rewardType, Reward::TYPE__OPTIONS)) {
                  new \Exception(sprintf('Invalid type %s. Possible options are: %s.', $rewardType, implode(',', Reward::TYPE__OPTIONS)));
              }
      
              $qb = $this->createQueryBuilder('u');
              $qb
                  ->where('u.enabled = true')
                  ->andWhere($qb->expr()->not($qb->expr()->exists($this->getEntityManager()->createQueryBuilder()->select('r')->from(Reward::class, 'r')->where('u = r.rewardee')->andWhere('r.type = :reward_type')->getDQL())))
                  ->orderBy('u.createdAt', 'DESC')
                  ->setParameter('reward_type', $rewardType)
                  ->setMaxResults($maxResults);
              $query = $qb->getQuery();
              $users = $query->getResult();
      
              return $users;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多