简体   繁体   English

尝试使用 doctrine 查询生成器的 select 左连接为 null

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

I'm trying to select all Rooms that are not occupied on given Reservation dates.我正在尝试 select 所有在给定预订日期未被占用的房间。
I've created such code:我创建了这样的代码:

$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
                )
            ); 

I have 2 reservations on 2 rooms, where room id equals reservation id:我在 2 个房间有 2 个预订,其中房间 ID 等于预订 ID:

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

Also $start = '2000-01-02' and $stop = '2000-01-05' and status of both reservations is Reservation::STATUS_NEW另外$start = '2000-01-02'$stop = '2000-01-05'并且两个预订的状态都是Reservation::STATUS_NEW
I would expect that given these dates, this query would return me room 1., as room 2 has reservation colliding with them but the result is empty.我希望给定这些日期,此查询将返回房间 1。因为房间 2 的预订与它们发生冲突,但结果为空。
Removing the where clause results in both rooms being returned.删除where子句会导致两个房间都被返回。 In this case $queryBuilder->getQuery()->getArrayResult() returns both rooms and reservation number 2. as expected, but when I restore the where clause, the array is just empty.在这种情况下$queryBuilder->getQuery()->getArrayResult()返回房间和预订号 2。正如预期的那样,但是当我恢复where子句时,数组只是空的。

My another approach is through a subquery but it behaves exactly the same so please advise, what am I missing?我的另一种方法是通过子查询,但它的行为完全相同,所以请告知,我错过了什么?

$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
                )
            );

you would want to exclude rooms, where the reservations have overlap with the query date span.您可能希望排除预订与查询日期范围重叠的房间。

that is那是

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

which you already had correct.你已经正确了。 however, your null check is off, it should be:但是,您的 null 检查已关闭,它应该是:

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

so, r is obviously the reservation, and that one should be null ("missing").所以, r显然是保留,应该是 null(“缺失”)。

(I always find that using short aliases makes it really really hard to spot those kinds of errors, and it's so little actual time saved..., I also much prefer named parameters over numbered parameters) (我总是发现使用短别名真的很难发现这些错误,而且实际节省的时间很少......,我也更喜欢命名参数而不是编号参数)

update更新

Okay, I missed the main point being wrong here;o/好的,我错过了这里错误的要点;o/

your query matches all rooms with all reservations (not just the reservations of that room), and selects those, that "have" no reservation.您的查询匹配所有预订的房间(不仅仅是该房间的预订),并选择那些“没有”预订的房间。 Meaning: if there is a reservation for the date span, you get back NO rooms, and if there is no reservation for the date span, you get back ALL rooms.含义:如果在日期范围内有预订,则返回 NO 房间,如果在日期范围内没有预订,则返回所有房间。

There's two ways to make the querybuilder do the right thing, which depends on your mapping definition, but I will assume, that the Room entity has a field reservations that holds all reservations for that room.有两种方法可以让查询构建器做正确的事情,这取决于您的映射定义,但我会假设Room实体有一个字段reservations保存该房间的所有预订。

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

The difference being, that the "table" descriptor implies that the reservations are of the respective room and only joined with those.不同之处在于,“桌子”描述符意味着预订是各自房间的,并且只与那些房间相连。

Or being explicit:或者是明确的:

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

That should finally solve your problem.那应该最终解决您的问题。

The answer above placed me on the right track to understand my failure.上面的答案使我走上了正确的轨道,以了解我的失败。
My final code looks like this:我的最终代码如下所示:

$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
                )
            );

It was all about joining rm.reservations , not general App:Reservation as I did.这完全是关于加入rm.reservations ,而不是像我一样加入一般的App:Reservation Assocations are well defined inside Entity classes and Doctrine will add proper constraints if I point to field of the object instead of general class.如果我指向 object 的字段而不是一般的 class 的字段,则关联在实体类中定义得很好,并且 Doctrine 将添加适当的约束。
Example given by Jakumi would work ok for a ManyToOne assocation, in my case (ManyToMany) I would have to manually join the joining table between Reservation and Room. Jakumi 给出的示例适用于 ManyToOne 关联,在我的情况下(ManyToMany),我必须手动加入 Reservation 和 Room 之间的连接表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM