使用学说查询生成器尝试 select 左连接为空的地方

trying to select where left join is null using doctrine query builder

我正在尝试 select 所有在给定预订日期未入住的房间。
我创建了这样的代码:

$queryBuilder->select('rm')
            ->from('App:Room', 'rm')
            ->leftJoin('App:Reservation', 'r', 'WITH', $queryBuilder->expr()->andX(
                $queryBuilder->expr()->lt('r.start', '?1'),
                $queryBuilder->expr()->gt('r.stop', '?2'),
                $queryBuilder->expr()->neq('r.status', '?3')
                )
            )->where(
                $queryBuilder->expr()->andX(
                    $queryBuilder->expr()->isNull('r')
                )
            )->setParameters(
                array(
                    1 => $stop,
                    2 => $start,
                    3 => Reservation::STATUS_EXPIRED
                )
            ); 

我有 2 个房间的 2 个预订,其中房间 ID 等于预订 ID:

  1. 2000-01-01 - 2000-01-02
  2. 2000-01-01 - 2000-01-03

还有 $start = '2000-01-02'$stop = '2000-01-05',两个预订的状态都是 Reservation::STATUS_NEW
我希望根据这些日期,此查询将 return 我的房间 1.,因为房间 2 的预订与他们发生冲突,但结果是空的。
删除 where 子句会导致两个房间都被 returned。在这种情况下 $queryBuilder->getQuery()->getArrayResult() return 房间和预订号 2. 正如预期的那样,但是当我恢复 where 子句时,数组只是空的。

我的另一种方法是通过子查询,但它的行为完全相同,所以请指教,我错过了什么?

$queryBuilder = $this->getEntityManager()->createQueryBuilder();
        $all = $this->getEntityManager()->createQueryBuilder();

        $all->select('rma.id')
            ->from('App:Room', 'rma')
            ->innerJoin('App:Reservation', 'r')
            ->where(
                $all->expr()->andX(
                    $all->expr()->lt('r.start', '?1'),
                    $all->expr()->gt('r.stop', '?2'),
                    $all->expr()->neq('r.status', '?3')
                )
            );

        $queryBuilder->select('rm')
            ->from('App:Room', 'rm')
            ->where(
                $queryBuilder->expr()->neq(
                    'rm.id',
                    $queryBuilder->expr()->all(
                        $all->getDQL()
                    )
                )
            )->setParameters(
                array(
                    1 => $stop,
                    2 => $start,
                    3 => Reservation::STATUS_EXPIRED
                )
            );

您可能希望排除预订与查询日期跨度重叠的房间。

$queryBuilder->expr()->lt('r.start', '?1'), // 1 being stop
$queryBuilder->expr()->gt('r.stop', '?2'),  // 2 being start

你已经猜对了。但是,您的空检查已关闭,它应该是:

$queryBuilder->expr()->isNull('r') // <-- you had rm here

所以,r显然是保留,那个应该是空的("missing")。

(我总是发现使用短别名真的很难发现这些类型的错误,而且实际节省的时间太少了......,我也更喜欢命名参数而不是编号参数)

更新

好吧,我错过了这里的要点;o/

您的查询匹配所有 all 预订的房间(不仅仅是那个房间的预订),并选择那些 "have" 没有预订的房间。含义:如果该日期范围内有预订,您将无法获得任何房间,如果该日期范围内没有预订,您将获得所有房间。

有两种方法可以使查询生成器做正确的事情,这取决于您的映射定义,但我假设,Room 实体有一个字段 reservations 保存所有保留那个房间。

$qb->from('Room', 'rm')
   ->leftJoin('rm.reservations', 'r', 'WITH', ...)

不同之处在于,"table" 描述符 暗示 预订是针对相应房间的,并且仅与这些房间合并。

或明确:

$qb->from('Room', 'rm')
   ->leftJoin('Reservation', 'r', 'WITH', $qb->expr()->andX('r.room=rm', ...))

这应该最终解决了您的问题。

上面的回答让我明白了我的失败。
我的最终代码如下所示:

$queryBuilder->select('rm')
            ->from('App:Room', 'rm')
            ->leftJoin('rm.reservations', 'r', 'WITH', $queryBuilder->expr()->andX(
                $queryBuilder->expr()->lt('r.start', '?1'),
                $queryBuilder->expr()->gt('r.stop', '?2'),
                $queryBuilder->expr()->neq('r.status', '?3')
                )
            )->where(
                $queryBuilder->expr()->andX(
                    $queryBuilder->expr()->isNull('r')
                )
            )->setParameters(
                array(
                    1 => $stop,
                    2 => $start,
                    3 => Reservation::STATUS_EXPIRED
                )
            );

一切都是为了加入 rm.reservations,而不是像我那样笼统 App:Reservation。关联在实体 classes 中定义明确,如果我指向对象的字段而不是一般 class.
,则 Doctrine 将添加适当的约束 Jakumi 给出的示例对于 ManyToOne 关联可以正常工作,在我的情况下(ManyToMany)我将不得不手动加入 Reservation 和 Room 之间的加入 table。