【问题标题】:A JOIN With Additional Conditions Using Query Builder or Eloquent使用 Query Builder 或 Eloquent 进行附加条件的 JOIN
【发布时间】:2013-05-26 18:50:42
【问题描述】:

我正在尝试使用 Laravel Query Builder 的 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')
);

我知道我可以使用Raw Expressions,但是会有 SQL 注入点。我已经使用 Query Builder 尝试了以下操作,但生成的查询(显然,查询结果)不是我想要的:

$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 的 Query Builder 构建相同的查询(如果可能)是使用 Eloquent 更好,还是应该使用 DB::select?

【问题讨论】:

    标签: laravel laravel-4 laravel-query-builder


    【解决方案1】:
    $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 子句添加到 join 中。

    注意事项:

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

    【讨论】:

    • 感谢您的回答。不幸的是,生成的查询略有不同,因为连接条件现在有 and arrival &gt;= ? and arrival &lt;= ? and departure &gt;= ? and departure &lt;= ? 而不是 AND ( r.arrival between ? and ? OR r.departure between ? and ? )(请注意 OR 子句)。这会将额外的行添加到不应该存在的结果中。我猜不可能使用 QB 在 join 中生成所有类型的条件。
    • 其实我注意到 ?未添加到 ON 子句中。 ON 中的那些值没有被参数化,也没有受到 SQL 注入的保护。我已经发布了question 试图找出解决方案。
    • 这没有回答原始问题,该问题在此响应中忽略了 or 子句。
    • 完美解决方案。请记住在使用值时使用 DB::raw,否则 Laravel(至少在 5.6.29 中)添加反引号并“破坏”目标。
    【解决方案2】:

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

    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(''));
    })
    

    您必须稍后使用“setBindings”设置绑定,如此 SO 帖子中所述: How to bind parameters to a raw DB query in Laravel that's used on a model?

    它不漂亮,但它有效。

    【讨论】:

      【解决方案3】:

      如果你有一些参数,你可以这样做。

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

      然后返回您的查询

      返回 $results;

      【讨论】:

      • 在加入条件的情况下,您可以在左连接中使用 $join->where() 而不是 DB::raw
      【解决方案4】:

      这样的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);
          });
      })
      

      【讨论】:

        【解决方案5】:

        我使用的是 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();
        

        【讨论】:

          【解决方案6】:

          原始查询和标准选择之间存在差异(DB::rawDB::select 方法之间)。

          您可以使用DB::select 做任何您想做的事情,只需放入? 占位符,就像您使用准备好的语句一样(实际上就是它正在做的事情)。

          一个小例子:

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

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

          【讨论】:

          • 这是否意味着参数会自动转义(从 SQL 注入中保存)?
          • 是的,它只是 PDO 准备语句的包装器。
          • 有没有办法在左连接的 ON 子句中使用该技术?请参阅我的问题here。我尝试在 leftJoin 闭包中执行 $join-&gt;on('winners.year','=',DB::select('?',array('2013'));,但没有成功。
          • 这并不是 eloquent 的设计初衷。您不妨只做一个 DB::raw() 并自己制作
          【解决方案7】:

          我的五美分方案LEFT JOIN ON (.. or ..) and (.. or ..) and ..

              ->join('checks','checks.id','check_id')
              ->leftJoin('schema_risks', function (JoinClause $join) use($order_type_id, $check_group_id,  $filial_id){
                  $join->on(function($join){
                      $join->on('schema_risks.check_method_id','=', 'check_id')
                          ->orWhereNull('schema_risks.check_method_id')
                          ;
                  })
                  ->on(function($join) use ($order_type_id) {
                      $join->where('schema_risks.order_type_id', $order_type_id)
                          ->orWhereNull('schema_risks.order_type_id')
                          ;
                  })
                  ->on(function($join) use ($check_group_id) {
                      $join->where('schema_risks.check_group_id', $check_group_id)
                          ->orWhereNull('schema_risks.check_group_id')
                          ;
                  })
                  ->on(function($join) use($filial_id){
                      $join->whereNull('schema_risks.filial_id');
                      if ($filial_id){
                          $join->orWhere('schema_risks.filial_id', $filial_id);
                      }
                  })            
                  ->on(function($join){
                      $join->whereNull('schema_risks.check_risk_level_id')
                          ->orWhere('schema_risks.check_risk_level_id', '>' , CheckRiskLevel::CRL_NORMALLLY );
                  })
                  ;
              })
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-12-07
            • 2020-02-20
            • 2016-05-26
            • 1970-01-01
            • 2022-01-20
            • 2019-08-11
            • 2015-01-12
            相关资源
            最近更新 更多