如何构建一个 Doctrine 查询来查找缺少特定类型的 OneToMany 关系的元素?
How to build a Doctrine query which would find elements with missing particular type of OneToMany relation?
假设我们有两个处于 OneToMany 关系中的实体,例如:
User 实体与 Rewards 实体和每个 实体有 OneToMany 关系奖励可能有不同的类型:例如'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 关系的性质,我不能简单地将条件从等于更改为不等于,也不能使用关系为空的条件(如下例所示),因为它不会 return 有效结果,因为用户可能与另一种类型的奖励有关。
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。
谢谢!
我设法使用以下 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;
}
有没有办法实现与查询生成器等效的东西?
我也找到了一个与查询生成器等效的解决方案:
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;
}
假设我们有两个处于 OneToMany 关系中的实体,例如:
User 实体与 Rewards 实体和每个 实体有 OneToMany 关系奖励可能有不同的类型:例如'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 关系的性质,我不能简单地将条件从等于更改为不等于,也不能使用关系为空的条件(如下例所示),因为它不会 return 有效结果,因为用户可能与另一种类型的奖励有关。
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。
谢谢!
我设法使用以下 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;
}
有没有办法实现与查询生成器等效的东西?
我也找到了一个与查询生成器等效的解决方案:
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;
}