Doctrine Query Builder 嵌套的 orX 和 andX 条件与连接
Doctrine Query Builder nested orX and andX conditions with join
我有两个实体 User
和 CalendarEvent
。我的用户隶属于一个工厂,calendarEvent 可用于了解用户是否 "loaned" 属于他所属工厂的不同工厂...
我的两个实体是这样的:
用户:
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var string
* @ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* @var string
* @ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\CalendarEvent", mappedBy="user")
*/
private $events;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Factory", inversedBy="users")
* @ORM\JoinColumn(name="factory_id", referencedColumnName="id")
*/
private $factory;
}
日历事件:
class CalendarEvent
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \DateTime
* @ORM\Column(type="datetime", name="event_start")
*/
private $startDate;
/**
* @var \DateTime
* @ORM\Column(type="datetime", name="event_end")
*/
private $endDate;
/**
* @var bool
* @ORM\Column(name="isLoan", type="boolean")
*/
private $isLoan = TRUE;
/**
* @ORM\ManyToOne(targetEntity="Factory")
* @ORM\JoinColumn(name="factory_id", referencedColumnName="id", nullable = true)
*/
private $factory;
/**
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="events")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
我想做的是在 UserRepository
中创建一个存储库函数,当我在入口参数中使用工厂触发请求时,它让我知道实际上是工厂的人...
为了实现这一点,我尝试指定一个条件。该条件应如下所示:
select * from user where (
(user.factory == $idfactory AND not loaned_to_other_factory) OR
(user.factory != $idfactory AND loaned_to_my_factory)
)
要知道一个用户是否被借给了我的工厂,必须验证以下条件:
Does a record exist in CalendarEvent where startDate < NOW and endDate > NOW and loaned == true and factory == $factory
我正在尝试使用我的存储库使用和/或条件,但我一直坚持该查询很长时间,我什至不习惯使用存储库,因为 findBy 方法在大多数情况下都可以正常工作案例...
我试过了,但是这个变量 $notLoanedToOther
有一个错误 Expression of type 'Doctrine\ORM\QueryBuilder' not allowed in this context.
:
public function findByFactory($idFactory)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->leftJoin('u.events', 'e');
$sub = $this->createQueryBuilder("e");
$sub->select("e");
$sub->from("CalendarEvent","e");
$sub->where('e.user = u.id');
$sub->andWhere('e.startDate < :now');
$sub->andWhere('e.endDate > :now');
$notLoanedToOther = $qb->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())));
$sub = $this->createQueryBuilder("e");
$sub->select("e");
$sub->from("CalendarEvent","e");
$sub->where('e.user = u.id');
$sub->where('e.factory = :idf');
$sub->andWhere('e.startDate < :now');
$sub->andWhere('e.endDate > :now');
$loanedToMyFactory = $qb->where($qb->expr()->exists($sub->getDQL()));
$condition1 = $qb->expr()->andX(
$qb->expr()->eq('u.factory', ':idf'),
$notLoanedToOther
);
$condition2 = $qb->expr()->andX(
$qb->expr()->neq('u.factory', ':idf'),
$loanedToMyFactory
);
$qb ->where($qb->expr()->orX($condition1, $condition2));
$qb ->setParameter('idf', $idFactory);
return $qb->getQuery()->getResult();
}
我真的希望我说得有道理,有人可以给我一些提示来实现这一目标。提前致谢
我猜你可以将 exists 查询翻译成 not in
$notLoanedToOther = $this->createQueryBuilder("e")
->select("e.user")
->from("CalendarEvent","e")
->andWhere('e.startDate < :now')
->andWhere('e.endDate > :now')
->getDQL();
$loanedToMyFactory = $this->createQueryBuilder("e")
->select("e.user")
->from("CalendarEvent","e")
->where('e.factory = :idf')
->andWhere('e.startDate < :now')
->andWhere('e.endDate > :now')
->getDQL();
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where(
$qb->expr()->not(
$qb->expr()->in(
'u.id',
$notLoanedToOther
)
)
)
->andWhere(
$qb->expr()->in(
'u.id',
$loanedToMyFactory
)
)
->where(
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('u.factory', ':idf'),
$qb->expr()->not(
$qb->expr()->in(
'u.id',
$notLoanedToOther
)
)
),
$qb->expr()->andX(
$qb->expr()->neq('u.factory', ':idf'),
$qb->expr()->in(
'u.id',
$loanedToMyFactory
)
)
)
)
->setParameter('idf', $idFactory)
->setParameter('now', $someDate)
;
Doing a WHERE .. IN subquery in Doctrine 2
根据您在 SQL
中指定的条件,使用带连接的简单 DQL 查询而不是复杂的 not in 子句怎么样?
select * from user where (
(user.factory == $idfactory AND not loaned_to_other_factory) OR
(user.factory != $idfactory AND loaned_to_my_factory)
)
你可以用 DQL 编写它的等价物
SELECT u
FROM User u
LEFT JOIN u.events nl WITH e.startDate < :now AND e.endDate > :now
LEFT JOIN u.events l WITH e.startDate < :now AND e.endDate > :now AND e.factory = :idf
WHERE (
u.factory = :idf AND nl.id IS NULL
) OR (
u.factory != :idf AND l.id IS NOT NULL
)
使用附加条款与用户加入事件实体两次,一个用于借出,另一个用于未加载,然后应用您的条件,我添加了 IS NULL
和 IS NOT NULL
过滤器以满足您的存在和不存在条件
我有两个实体 User
和 CalendarEvent
。我的用户隶属于一个工厂,calendarEvent 可用于了解用户是否 "loaned" 属于他所属工厂的不同工厂...
我的两个实体是这样的:
用户:
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var string
* @ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* @var string
* @ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\CalendarEvent", mappedBy="user")
*/
private $events;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Factory", inversedBy="users")
* @ORM\JoinColumn(name="factory_id", referencedColumnName="id")
*/
private $factory;
}
日历事件:
class CalendarEvent
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var \DateTime
* @ORM\Column(type="datetime", name="event_start")
*/
private $startDate;
/**
* @var \DateTime
* @ORM\Column(type="datetime", name="event_end")
*/
private $endDate;
/**
* @var bool
* @ORM\Column(name="isLoan", type="boolean")
*/
private $isLoan = TRUE;
/**
* @ORM\ManyToOne(targetEntity="Factory")
* @ORM\JoinColumn(name="factory_id", referencedColumnName="id", nullable = true)
*/
private $factory;
/**
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="events")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
我想做的是在 UserRepository
中创建一个存储库函数,当我在入口参数中使用工厂触发请求时,它让我知道实际上是工厂的人...
为了实现这一点,我尝试指定一个条件。该条件应如下所示:
select * from user where (
(user.factory == $idfactory AND not loaned_to_other_factory) OR
(user.factory != $idfactory AND loaned_to_my_factory)
)
要知道一个用户是否被借给了我的工厂,必须验证以下条件:
Does a record exist in CalendarEvent where startDate < NOW and endDate > NOW and loaned == true and factory == $factory
我正在尝试使用我的存储库使用和/或条件,但我一直坚持该查询很长时间,我什至不习惯使用存储库,因为 findBy 方法在大多数情况下都可以正常工作案例...
我试过了,但是这个变量 $notLoanedToOther
有一个错误 Expression of type 'Doctrine\ORM\QueryBuilder' not allowed in this context.
:
public function findByFactory($idFactory)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->leftJoin('u.events', 'e');
$sub = $this->createQueryBuilder("e");
$sub->select("e");
$sub->from("CalendarEvent","e");
$sub->where('e.user = u.id');
$sub->andWhere('e.startDate < :now');
$sub->andWhere('e.endDate > :now');
$notLoanedToOther = $qb->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())));
$sub = $this->createQueryBuilder("e");
$sub->select("e");
$sub->from("CalendarEvent","e");
$sub->where('e.user = u.id');
$sub->where('e.factory = :idf');
$sub->andWhere('e.startDate < :now');
$sub->andWhere('e.endDate > :now');
$loanedToMyFactory = $qb->where($qb->expr()->exists($sub->getDQL()));
$condition1 = $qb->expr()->andX(
$qb->expr()->eq('u.factory', ':idf'),
$notLoanedToOther
);
$condition2 = $qb->expr()->andX(
$qb->expr()->neq('u.factory', ':idf'),
$loanedToMyFactory
);
$qb ->where($qb->expr()->orX($condition1, $condition2));
$qb ->setParameter('idf', $idFactory);
return $qb->getQuery()->getResult();
}
我真的希望我说得有道理,有人可以给我一些提示来实现这一目标。提前致谢
我猜你可以将 exists 查询翻译成 not in
$notLoanedToOther = $this->createQueryBuilder("e")
->select("e.user")
->from("CalendarEvent","e")
->andWhere('e.startDate < :now')
->andWhere('e.endDate > :now')
->getDQL();
$loanedToMyFactory = $this->createQueryBuilder("e")
->select("e.user")
->from("CalendarEvent","e")
->where('e.factory = :idf')
->andWhere('e.startDate < :now')
->andWhere('e.endDate > :now')
->getDQL();
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where(
$qb->expr()->not(
$qb->expr()->in(
'u.id',
$notLoanedToOther
)
)
)
->andWhere(
$qb->expr()->in(
'u.id',
$loanedToMyFactory
)
)
->where(
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('u.factory', ':idf'),
$qb->expr()->not(
$qb->expr()->in(
'u.id',
$notLoanedToOther
)
)
),
$qb->expr()->andX(
$qb->expr()->neq('u.factory', ':idf'),
$qb->expr()->in(
'u.id',
$loanedToMyFactory
)
)
)
)
->setParameter('idf', $idFactory)
->setParameter('now', $someDate)
;
Doing a WHERE .. IN subquery in Doctrine 2
根据您在 SQL
中指定的条件,使用带连接的简单 DQL 查询而不是复杂的 not in 子句怎么样?select * from user where ( (user.factory == $idfactory AND not loaned_to_other_factory) OR (user.factory != $idfactory AND loaned_to_my_factory) )
你可以用 DQL 编写它的等价物
SELECT u
FROM User u
LEFT JOIN u.events nl WITH e.startDate < :now AND e.endDate > :now
LEFT JOIN u.events l WITH e.startDate < :now AND e.endDate > :now AND e.factory = :idf
WHERE (
u.factory = :idf AND nl.id IS NULL
) OR (
u.factory != :idf AND l.id IS NOT NULL
)
使用附加条款与用户加入事件实体两次,一个用于借出,另一个用于未加载,然后应用您的条件,我添加了 IS NULL
和 IS NOT NULL
过滤器以满足您的存在和不存在条件