JOIN z dodatkowymi warunkami za pomocą Query Builder lub Eloquent


93

Próbuję dodać warunek za pomocą zapytania JOIN z 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')
);

Wiem, że mogę używać surowych wyrażeń, ale wtedy pojawią się punkty wstrzyknięcia SQL. Wypróbowałem poniższe z Query Builder, ale wygenerowane zapytanie (i oczywiście wyniki zapytania) nie są tym, co zamierzałem:

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

Oto zapytanie wygenerowane przez 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

Jak widać, dane wyjściowe zapytania nie mają struktury (szczególnie w zakresie JOIN). Czy można dodać dodatkowe warunki w JOIN?

Jak mogę zbudować to samo zapytanie używając Laravel's Query Builder (jeśli to możliwe). Czy lepiej jest użyć Eloquent, czy powinien pozostać przy DB :: select?

Odpowiedzi:


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

Nie do końca jestem pewien, czy klauzulę between można dodać do złączenia w laravel.

Uwagi:

  • DB::raw() instruuje Laravel, aby nie odkładał cytatów.
  • Przekazując zamknięcie metod łączenia, możesz dodać do niego więcej warunków łączenia, on()dodasz ANDwarunek i orOn()dodasz ORwarunek.

Dziękuję za odpowiedź. Niestety wygenerowane zapytanie jest nieco inne, ponieważ warunek sprzężenia ma teraz and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?zamiast tego AND ( r.arrival between ? and ? OR r.departure between ? and ? )(proszę zwrócić uwagę na ORklauzulę). Spowoduje to dodanie dodatkowych wierszy, których nie powinno tam być. Wydaje mi się, że nie jest możliwe wygenerowanie wszystkich typów warunków łączenia za pomocą QB.
— dede

1
Właściwie zauważyłem, że? nie jest dodawany do klauzul ON. Te wartości w ON nie są sparametryzowane i nie są chronione przed iniekcją SQL. Wysłałem pytanie, próbując znaleźć rozwiązanie.
— prograhammer

3
to nie odpowiadało na pierwotne pytanie, które zawierało klauzulę lub, która została zignorowana w tej odpowiedzi.
— ionescho

Idealne rozwiązanie. Pamiętaj tylko, aby używać DB :: raw podczas używania wartości, w przeciwnym razie Laravel (przynajmniej w 5.6.29) dodaje cudzysłowy i „łamie” cel.
— Adrian Hernandez-Lopez

35

Możesz powielić te nawiasy w lewym złączeniu:

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

jest

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

Będziesz wtedy musiał ustawić powiązania później za pomocą "setBindings", jak opisano w tym poście SO: Jak powiązać parametry z surowym zapytaniem DB w Laravel, które jest używane w modelu?

Nie jest ładna, ale działa.


32

Jeśli masz jakieś parametry, możesz to zrobić.

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

a następnie zwróć zapytanie

zwraca $ wyników;


23

Przykład zapytania sql, taki jak ten

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

Laravel dołącz z wieloma warunkami

->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

Używam laravel5.2 i możemy dodawać połączenia z różnymi opcjami, możesz je modyfikować zgodnie z wymaganiami.

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

Istnieje różnica między zapytaniami nieprzetworzonymi a standardowymi selekcjami (między metodami DB::rawi DB::select).

Możesz robić, co chcesz, używając a DB::selecti po prostu upuszczając ?symbol zastępczy, podobnie jak w przypadku przygotowanych instrukcji (tak naprawdę to robi).

Mały przykład:

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

Drugi parametr to tablica wartości, które zostaną użyte do zastąpienia symboli zastępczych w zapytaniu od lewej do prawej.


Czy to oznacza, że ​​parametry są automatycznie zmieniane (zapisywane z iniekcji SQL)?
— dede

2
Tak, to tylko opakowanie dla oświadczeń przygotowanych przez PDO.
— Jason Lewis

Czy istnieje sposób na użycie tej techniki w klauzuli ON w lewym złączeniu? Zobacz moje pytanie tutaj . Próbowałem po wewnętrznej stronie mojego lewego zamknięcia, $join->on('winners.year','=',DB::select('?',array('2013'));ale nie zadziałało.
— prograhammer

nie do końca tak elokwentne ma działać. równie dobrze możesz po prostu zrobić DB :: raw () i zrobić to sam
— mydoglixu
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.