A JOIN Dengan Ketentuan Tambahan Menggunakan Query Builder atau Eloquent

94

Saya mencoba menambahkan kondisi menggunakan kueri JOIN dengan Laravel Query Builder.

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

Saya tahu saya dapat menggunakan Ekspresi Mentah tetapi kemudian akan ada titik injeksi SQL. Saya telah mencoba yang berikut ini dengan Query Builder tetapi kueri yang dihasilkan (dan jelas, hasil kueri) tidak seperti yang saya inginkan:

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

Ini adalah kueri yang dihasilkan oleh 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

Seperti yang Anda lihat, output kueri tidak memiliki struktur (terutama di bawah cakupan JOIN). Apakah mungkin untuk menambahkan ketentuan tambahan di bawah JOIN?

Bagaimana saya bisa membangun kueri yang sama menggunakan Laravel's Query Builder (jika mungkin) Apakah lebih baik menggunakan Eloquent, atau harus tetap dengan DB :: select?

dede
sumber

Jawaban:

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

Tidak begitu yakin apakah klausa antara dapat ditambahkan ke gabungan di laravel.

Catatan:

  • DB::raw() menginstruksikan Laravel untuk tidak mengembalikan tanda kutip.
  • Dengan melewatkan metode closure untuk bergabung, Anda dapat menambahkan lebih banyak kondisi bergabung ke dalamnya, on()akan menambah ANDkondisi dan orOn()akan menambahkan ORkondisi.
Abishek
sumber
Terima kasih atas jawabannya. Sayangnya, query yang dihasilkan sedikit berbeda karena kondisi bergabung sekarang memiliki and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?bukan AND ( r.arrival between ? and ? OR r.departure between ? and ? )(silakan perhatikan ORklausa). Ini menambahkan baris tambahan ke hasil yang seharusnya tidak ada. Saya kira tidak mungkin untuk menghasilkan semua jenis kondisi dalam bergabung menggunakan QB.
dede
1
Sebenarnya saya perhatikan bahwa? tidak ditambahkan ke klausa ON. Nilai tersebut di ON tidak berparameter dan tidak dilindungi dari injeksi SQL. Saya telah memposting pertanyaan mencoba mencari tahu solusinya.
prograhammer
3
ini tidak menjawab pertanyaan asli yang memiliki klausa atau yang diabaikan dalam jawaban ini.
ionescho
Solusi sempurna. Ingatlah untuk menggunakan DB :: raw saat menggunakan nilai, jika tidak Laravel (setidaknya di 5.6.29) menambahkan tanda kutip belakang dan "mematahkan" sasaran.
Adrian Hernandez-Lopez
35

Anda dapat mereplikasi tanda kurung tersebut di gabungan kiri:

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

adalah

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

Anda kemudian harus mengatur binding nanti menggunakan "setBindings" seperti yang dijelaskan dalam posting SO ini: Bagaimana cara mengikat parameter ke kueri DB mentah di Laravel yang digunakan pada model?

Ini tidak cantik tapi berhasil.

rickywiens
sumber
32

Jika Anda memiliki beberapa parameter, Anda dapat melakukan ini.

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

lalu kembalikan kueri Anda

mengembalikan $ hasil;

vroldan
sumber
23

Contoh query sql seperti ini

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

Laravel bergabung dengan berbagai kondisi

->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);
    });
})
Majbah Habib
sumber
11

Saya menggunakan laravel5.2 dan kami dapat menambahkan gabungan dengan opsi yang berbeda, Anda dapat memodifikasi sesuai kebutuhan Anda.

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();
Abid Ali
sumber
3

Ada perbedaan antara kueri mentah dan pemilihan standar (antara DB::rawdanDB::select metode ).

Anda dapat melakukan apa yang Anda inginkan dengan menggunakan DB::selectdan cukup memasukkan file? placeholder seperti yang Anda lakukan dengan pernyataan yang disiapkan (sebenarnya itulah yang dilakukannya).

Contoh kecil:

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

Parameter kedua adalah larik nilai yang akan digunakan untuk menggantikan placeholder dalam kueri dari kiri ke kanan.

Jason Lewis
sumber
Apakah itu berarti bahwa parameter secara otomatis di-escape (simpan dari injeksi SQL)?
dede
2
Ya, itu hanya pembungkus untuk pernyataan yang disiapkan PDO.
Jason Lewis
Apakah ada cara untuk menggunakan teknik itu dalam klausa ON di gabungan kiri? Lihat pertanyaan saya di sini . Saya mencoba di dalam penutupan leftJoin untuk melakukan $join->on('winners.year','=',DB::select('?',array('2013'));tetapi tidak berhasil.
prograhammer
itu tidak terlalu fasih dirancang untuk beroperasi. Anda sebaiknya melakukan DB :: raw () dan membuatnya sendiri
mydoglixu