UN JOIN con condizioni aggiuntive utilizzando Query Builder o Eloquent


93

Sto cercando di aggiungere una condizione utilizzando una query JOIN con 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')
);

So di poter usare Raw Expressions ma poi ci saranno punti di iniezione SQL. Ho provato quanto segue con Query Builder ma la query generata (e ovviamente i risultati della query) non sono ciò che intendevo:

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

Questa è la query generata da 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

Come puoi vedere, l'output della query non ha la struttura (specialmente nell'ambito di JOIN). È possibile aggiungere ulteriori condizioni sotto il JOIN?

Come posso costruire la stessa query usando il Query Builder di Laravel (se possibile) È meglio usare Eloquent o dovrebbe rimanere con DB :: select?

Risposte:


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

Non sono abbastanza sicuro se la clausola between può essere aggiunta al join in laravel.

Appunti:

  • DB::raw() indica a Laravel di non rimettere le virgolette.
  • Passando una chiusura ai metodi di unione è possibile aggiungere più condizioni di unione ad essa, on()aggiungerà ANDcondizione e orOn()aggiungerà ORcondizione.

Grazie per la risposta. Sfortunatamente, la query generata è leggermente diversa poiché la condizione di join ora ha and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?invece AND ( r.arrival between ? and ? OR r.departure between ? and ? )(si prega di notare la ORclausola). Ciò aggiunge ulteriori righe al risultato che non dovrebbero essere presenti. Immagino che non sia possibile generare tutti i tipi di condizioni in join utilizzando QB.
dede

1
In realtà ho notato che il? non viene aggiunto nelle clausole ON. Questi valori in ON non sono parametrizzati e non sono protetti da SQL injection. Ho pubblicato una domanda cercando di capire la soluzione.
prograhammer

3
questo non ha risposto alla domanda originale che aveva una clausola o che è stata ignorata in questa risposta.
ionescho

Soluzione perfetta. Ricorda solo di usare DB :: raw quando usi un valore, altrimenti Laravel (almeno in 5.6.29) aggiunge virgolette e "spezza" l'obiettivo.
Adrian Hernandez-Lopez

35

Puoi replicare quelle parentesi nel join sinistro:

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

Dovrai quindi impostare le associazioni in seguito utilizzando "setBindings" come descritto in questo post SO: Come associare i parametri a una query DB non elaborata in Laravel che viene utilizzata su un modello?

Non è carino ma funziona.


32

Se hai dei parametri, puoi farlo.

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

e quindi restituisci la tua query

restituire $ risultati;


23

L'esempio di query sql come questo

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

Laravel si unisce con più condizioni

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

Sto usando laravel5.2 e possiamo aggiungere join con diverse opzioni, puoi modificare secondo le tue esigenze.

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

C'è una differenza tra le query non elaborate e le selezioni standard (tra i metodi DB::rawe DB::select).

Puoi fare quello che vuoi usando un DB::selecte semplicemente rilasciando il ?segnaposto proprio come fai con le dichiarazioni preparate (è in realtà quello che sta facendo).

Un piccolo esempio:

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

Il secondo parametro è una matrice di valori che verrà utilizzata per sostituire i segnaposto nella query da sinistra a destra.


Ciò significa che i parametri vengono automaticamente sottoposti a escape (salvo dall'iniezione SQL)?
dede

2
Sì, è solo un involucro per le dichiarazioni preparate DOP.
Jason Lewis

C'è un modo per utilizzare quella tecnica in una clausola ON in un join sinistro? Vedi la mia domanda qui . Ho provato a farlo all'interno della mia chiusura leftJoin $join->on('winners.year','=',DB::select('?',array('2013'));ma non ha funzionato.
prograhammer

non è proprio così che eloquente è progettato per funzionare. potresti anche fare un DB :: raw () e farlo da solo
mydoglixu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.