QueryBuilderまたはEloquentを使用した追加条件との結合


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

Raw式を使用できることはわかっていますが、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を使用する必要がありますか?

回答:


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を使用して結合ですべてのタイプの条件を生成することは不可能だと思います。
2013年

1
実際に気づいたのは?ON句には追加されません。ONのこれらの値はパラメーター化されておらず、SQLインジェクションから保護されていません。解決策を見つけようとして質問を投稿しました。
プログラムハンマー2013

3
これは、この応答で無視されたor句を含む元の質問には答えませんでした。
ionescho 2015年

完璧なソリューション。値を使用するときは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();

その後、クエリを返します

$ resultsを返します。


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::rawDB::selectメソッドの間)には違いがあります。

を使用してDB::select、単にドロップするだけで、やりたいことができます。?プリペアドステートメントの場合と同じようプレースホルダーに(実際にはそれが実行されます)。

小さな例:

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

2番目のパラメーターは、クエリ内のプレースホルダーを左から右に置き換えるために使用される値の配列です。


これは、パラメーターが自動的にエスケープされる(SQLインジェクションから保存する)ことを意味しますか?
2013年

2
はい、これはPDOプリペアドステートメントの単なるラッパーです。
ジェイソンルイス

左結合のON句でその手法を使用する方法はありますか?ここで私の質問を参照してください。leftJoinクロージャ内で実行しようとしましたが、機能$join->on('winners.year','=',DB::select('?',array('2013'));しませんでした。
プログラムハンマー2013

それは実際には雄弁に動作するように設計されている方法ではありません。DB :: raw()を実行して、自分で作成することもできます
mydoglixu 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.