使用查询生成器或口才的具有附加条件的JOIN


93

我正在尝试使用Laravel查询生成器的JOIN查询添加条件。

<?php

$results = DB::select('
       SELECT DISTINCT 
          *
          FROM 
             rooms 
                LEFT JOIN bookings  
                   ON rooms.id = bookings.room_type_id
                  AND (  bookings.arrival between ? and ?
                      OR bookings.departure between ? and ? )
          WHERE
                bookings.room_type_id IS NULL
          LIMIT 20',
    array('2012-05-01', '2012-05-10', '2012-05-01', '2012-05-10')
);

我知道我可以使用原始表达式,但随后会有SQL注入点。我已经使用查询生成器尝试了以下操作,但是生成的查询(显然是查询结果)不是我想要的:

$results = DB::table('rooms')
    ->distinct()
    ->leftJoin('bookings', function ($join) {
        $join->on('rooms.id', '=', 'bookings.room_type_id');
    })
    ->whereBetween('arrival', array('2012-05-01', '2012-05-10'))
    ->whereBetween('departure', array('2012-05-01', '2012-05-10'))
    ->where('bookings.room_type_id', '=', null)
    ->get();

这是Laravel生成的查询:

select distinct * from `room_type_info`
    left join `bookings` 
on `room_type_info`.`id` = `bookings`.`room_type_id` 
where `arrival` between ? and ? 
    and `departure` between ? and ? 
    and `bookings`.`room_type_id` is null

如您所见,查询输出没有结构(特别是在JOIN范围内)。是否可以在JOIN下添加其他条件?

如何使用Laravel的查询生成器构建相同的查询(如果可能),最好是使用Eloquent,还是应该保留在DB :: select中?

Answers:


139
$results = DB::table('rooms')
                     ->distinct()
                     ->leftJoin('bookings', function($join)
                         {
                             $join->on('rooms.id', '=', 'bookings.room_type_id');
                             $join->on('arrival','>=',DB::raw("'2012-05-01'"));
                             $join->on('arrival','<=',DB::raw("'2012-05-10'"));
                             $join->on('departure','>=',DB::raw("'2012-05-01'"));
                             $join->on('departure','<=',DB::raw("'2012-05-10'"));
                         })
                     ->where('bookings.room_type_id', '=', NULL)
                     ->get();

不太确定是否可以在laravel中将between子句添加到联接中。

笔记:

  • DB::raw() 指示Laravel不要回引号。
  • 通过将闭包传递给联接方法,您可以为其添加更多的联接条件,on()添加AND条件和orOn()添加OR条件。

谢谢你的回答。不幸的是,生成的查询略有不同,因为现在and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?改为具有连接条件AND ( r.arrival between ? and ? OR r.departure between ? and ? )(请注意该OR子句)。这将添加其他行,以致不应存在的行。我想不可能使用QB在联接中生成所有类型的条件。
德德

1
其实我注意到了吗?未添加到ON子句中。ON中的那些值未参数化,也不受SQL注入的保护。我发布了一个问题,试图找出解决方案。
prograhammer

3
这没有回答原始问题,该问题的原语中有一个or子句在此响应中被忽略。
ionescho

完美的解决方案。只需记住在使用值时使用DB :: raw,否则Laravel(至少在5.6.29中)添加反引号并“破坏”目标。
阿德里安·埃尔南德斯-洛佩兹

35

您可以在左联接中复制这些括号:

LEFT JOIN bookings  
               ON rooms.id = bookings.room_type_id
              AND (  bookings.arrival between ? and ?
                  OR bookings.departure between ? and ? )

->leftJoin('bookings', function($join){
    $join->on('rooms.id', '=', 'bookings.room_type_id');
    $join->on(DB::raw('(  bookings.arrival between ? and ? OR bookings.departure between ? and ? )'), DB::raw(''), DB::raw(''));
})

然后,您将不得不稍后按照本SO文章中所述使用“ setBindings”设置绑定: 如何将参数绑定到模型上使用的Laravel中的原始DB查询?

它不是很漂亮,但是可以。


32

如果您有一些参数,则可以执行此操作。

    $results = DB::table('rooms')
    ->distinct()
    ->leftJoin('bookings', function($join) use ($param1, $param2)
    {
        $join->on('rooms.id', '=', 'bookings.room_type_id');
        $join->on('arrival','=',DB::raw("'".$param1."'"));
        $join->on('arrival','=',DB::raw("'".$param2."'"));

    })
    ->where('bookings.room_type_id', '=', NULL)
    ->get();

然后返回您的查询

返回$结果;


23

像这样的sql查询示例

LEFT JOIN bookings  
    ON rooms.id = bookings.room_type_id
    AND (bookings.arrival = ?
        OR bookings.departure = ?)

Laravel加入多个条件

->leftJoin('bookings', function($join) use ($param1, $param2) {
    $join->on('rooms.id', '=', 'bookings.room_type_id');
    $join->on(function($query) use ($param1, $param2) {
        $query->on('bookings.arrival', '=', $param1);
        $query->orOn('departure', '=',$param2);
    });
})

11

我正在使用laravel5.2,我们可以添加具有不同选项的联接,您可以根据需要进行修改。

Option 1:    
    DB::table('users')
            ->join('contacts', function ($join) {
                $join->on('users.id', '=', 'contacts.user_id')->orOn(...);//you add more joins here
            })// and you add more joins here
        ->get();

Option 2:
    $users = DB::table('users')
        ->join('contacts', 'users.id', '=', 'contacts.user_id')
        ->join('orders', 'users.id', '=', 'orders.user_id')// you may add more joins
        ->select('users.*', 'contacts.phone', 'orders.price')
        ->get();

option 3:
    $users = DB::table('users')
        ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
        ->leftJoin('...', '...', '...', '...')// you may add more joins
        ->get();

3

原始查询和标准选择之间有区别(DB::raw和之间DB::select方法)。

您可以使用aDB::select进行所需的?操作,就像在准备好的语句中一样简单地放入占位符(实际上就是它在做什么)。

一个小例子:

$results = DB::select('SELECT * FROM user WHERE username=?', ['jason']);

第二个参数是一个值数组,将用于从左到右替换查询中的占位符。


这是否意味着参数会自动转义(从SQL注入中保存)?
dede

2
是的,它只是PDO准备好的语句的包装。
杰森·刘易斯

有什么方法可以在左连接的ON子句中使用该技术?在这里看到我的问题。我尝试在我的leftJoin闭包内部进行操作,$join->on('winners.year','=',DB::select('?',array('2013'));但没有成功。
prograhammer

这并不是说操作的雄辩能力。您也可以只做一个DB :: raw()自己动手
mydoglixu 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.