我有一个相当复杂的Doctrine QueryBuilder对象,就是这样的存储库(实际上,它甚至更复杂,这是一个简化的版本):
/** @var \DateTimeInterface $openAfter */
/** @var \DateTimeInterface $openBefore */
$qb->where(
$qb->expr()->andX(
$qb->expr()->isNotNull('beginAt'),
$qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->gte('beginAt', ':openAfter'),
$qb->expr()->lte('beginAt', ':openBefore')
),
$qb->expr()->andX(
$qb->expr()->lt('beginAt', ':openAfter'),
$qb->expr()->orX(
$qb->expr()->isNull('finishedAt'),
$qb->expr()->gte('finishedAt', ':openAfter')
)
)
)
)
);
$qb->setParameter('openAfter', $openAfter);
$qb->setParameter('openBefore', $openBefore);
但是现在我想允许$openBefore
是可选的,因此间隔可以在右侧打开。在简单的QueryBuilder对象中,当andWhere()
不为null时,我将插入一个可选的$openBefore
,但是此QueryBulder太复杂了,无法采用这种方式。
因此,就目前而言,我设定了一个很晚的日期(因为没有new \DateTime('eternity')
:
if (null === $openBefore) {
$openBefore = new \DateTime('9999-12-31 23:59:59');
}
那感觉不对(或者只是假设一个遥远的未来,这变得古怪)。
所以我想要的基本上是,如果$qb->expr()->lte('beginAt', ':openBefore')
为null,则$openBefore
始终为true。但是实际上,它总是错误的,我认为这是正确的行为。
一种解决方案是编写处理$openBefore
的表达式变体,并将其存储到这样的变量中:
/** @var \DateTimeInterface $openAfter */
/** @var \DateTimeInterface|null $openBefore */
if (null === $openBefore) {
$beginAtIntervalExpr = $qb->expr()->gte('beginAt', ':openAfter');
}
else {
$beginAtIntervalExpr = $qb->expr()->andX(
$qb->expr()->gte('beginAt', ':openAfter'),
$qb->expr()->lte('beginAt', ':openBefore')
);
}
$qb->where(
$qb->expr()->andX(
$qb->expr()->isNotNull('beginAt'),
$qb->expr()->orX(
$beginAtIntervalExpr,
$qb->expr()->andX(
$qb->expr()->lt('beginAt', ':openAfter'),
$qb->expr()->orX(
$qb->expr()->isNull('finishedAt'),
$qb->expr()->gte('finishedAt', ':openAfter')
)
)
)
)
);
$qb->setParameter('openAfter', $openAfter);
if (null !== $openBefore) {
$qb->setParameter('openBefore', $openBefore);
}
这很简单,难以理解,并且随着更多这些可选参数的出现而变得更糟。那么$openAfter
也应该是可选的怎么办?在我的应用程序中就是这种情况(以及使用那些前卫的DateTime对象解决该问题的原因。
所以问题是:在不编写相同QueryBuilder表达式的多个变体的情况下,使比较成为可选的正确而又可靠的方法是什么?