A JOIN mit zusätzlichen Bedingungen mit Query Builder oder Eloquent

91

Ich versuche, eine Bedingung mithilfe einer JOIN-Abfrage mit Laravel Query Builder hinzuzufügen.

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

Ich weiß, dass ich Raw Expressions verwenden kann, aber dann gibt es SQL-Injection-Punkte. Ich habe mit Query Builder Folgendes versucht, aber die generierte Abfrage (und natürlich die Abfrageergebnisse) sind nicht das, was ich beabsichtigt habe:

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

Dies ist die von Laravel generierte Abfrage:

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

Wie Sie sehen können, hat die Abfrageausgabe nicht die Struktur (insbesondere im Bereich JOIN). Ist es möglich, unter JOIN zusätzliche Bedingungen hinzuzufügen?

Wie kann ich dieselbe Abfrage mit Laravels Query Builder erstellen (falls möglich)? Ist es besser, Eloquent zu verwenden, oder sollte ich bei DB :: select bleiben?

dede
quelle

Antworten:

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

Ich bin mir nicht ganz sicher, ob die Between-Klausel dem Join in Laravel hinzugefügt werden kann.

Anmerkungen:

  • DB::raw() weist Laravel an, keine Anführungszeichen zu setzen.
  • Durch Übergeben eines Abschlusses an Verknüpfungsmethoden können Sie weitere Verknüpfungsbedingungen on()hinzufügen, ANDBedingungen hinzufügen und Bedingungen orOn()hinzufügen OR.
Abishek
quelle
Danke für die Antwort. Leider unterscheidet sich die generierte Abfrage geringfügig, da jetzt and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?stattdessen die Join-Bedingung gilt AND ( r.arrival between ? and ? OR r.departure between ? and ? )(beachten Sie die ORKlausel). Dies fügt zusätzliche Zeilen hinzu, die nicht vorhanden sein sollten. Ich denke, es ist nicht möglich, alle Arten von Bedingungen in Join mit QB zu generieren.
dede
1
Eigentlich ist mir aufgefallen, dass die? wird nicht in die ON-Klauseln aufgenommen. Diese Werte im ON sind nicht parametrisiert und nicht vor SQL-Injection geschützt. Ich habe eine Frage gestellt , um die Lösung herauszufinden.
Programmierer
3
Dies beantwortete nicht die ursprüngliche Frage, die eine oder -Klausel enthielt, die in dieser Antwort ignoriert wurde.
Ionescho
Perfekte Lösung. Denken Sie daran, DB :: raw zu verwenden, wenn Sie einen Wert verwenden. Andernfalls fügt Laravel (zumindest in 5.6.29) Anführungszeichen hinzu und "bricht" das Ziel.
Adrian Hernandez-Lopez
34

Sie können diese Klammern im linken Join replizieren:

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

ist

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

Sie müssen die Bindungen später mit "setBindings" festlegen, wie in diesem SO-Beitrag beschrieben: Wie binde ich Parameter an eine unformatierte DB-Abfrage in Laravel, die für ein Modell verwendet wird?

Es ist nicht schön, aber es funktioniert.

Rickywiens
quelle
31

Wenn Sie einige Parameter haben, können Sie dies tun.

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

und geben Sie dann Ihre Anfrage zurück

$ results zurückgeben;

vroldan
quelle
21

Das SQL-Abfragebeispiel sieht so aus

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

Laravel verbindet mit mehreren Bedingungen

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

Ich verwende laravel5.2 und wir können Verknüpfungen mit verschiedenen Optionen hinzufügen, die Sie gemäß Ihren Anforderungen ändern können.

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

Es gibt einen Unterschied zwischen den Rohabfragen und den Standardauswahlen (zwischen den Methoden DB::rawund DB::select).

Sie können tun, was Sie wollen, indem Sie ein verwenden DB::selectund einfach den ?Platzhalter einfügen, ähnlich wie bei vorbereiteten Anweisungen (es ist tatsächlich das, was es tut).

Ein kleines Beispiel:

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

Der zweite Parameter ist ein Array von Werten, mit denen die Platzhalter in der Abfrage von links nach rechts ersetzt werden.

Jason Lewis
quelle
Bedeutet das, dass Parameter automatisch maskiert werden (außer bei SQL-Injection)?
Dede
2
Ja, es ist nur ein Wrapper für PDO-vorbereitete Anweisungen.
Jason Lewis
Gibt es eine Möglichkeit, diese Technik in einer ON-Klausel in einem linken Join zu verwenden? Siehe meine Frage hier . Ich habe versucht, in meinem LeftJoin-Verschluss etwas zu tun, $join->on('winners.year','=',DB::select('?',array('2013'));aber es hat nicht funktioniert.
Programmierer
So ist Beredsamkeit nicht wirklich ausgelegt. Sie können genauso gut ein DB :: raw () machen und es selbst machen
mydoglixu