Une jointure avec des conditions supplémentaires utilisant Query Builder ou Eloquent


93

J'essaye d'ajouter une condition en utilisant une requête JOIN avec 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')
);

Je sais que je peux utiliser des expressions brutes, mais il y aura des points d'injection SQL. J'ai essayé ce qui suit avec Query Builder mais la requête générée (et évidemment, les résultats de la requête) ne sont pas ce que je voulais:

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

Voici la requête générée par 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

Comme vous pouvez le voir, la sortie de la requête n'a pas la structure (en particulier sous la portée JOIN). Est-il possible d'ajouter des conditions supplémentaires dans le cadre du JOIN?

Comment puis-je créer la même requête en utilisant le Générateur de requêtes de Laravel (si possible) Est-il préférable d'utiliser Eloquent, ou devrait-il rester avec DB :: select?

Réponses:


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

Je ne sais pas si la clause between peut être ajoutée à la jointure dans laravel.

Remarques:

  • DB::raw() ordonne à Laravel de ne pas remettre de devis.
  • En passant une fermeture aux méthodes de jointure, vous pouvez y ajouter plus de conditions de jointure, on()ajouter une ANDcondition et orOn()ajouter une ORcondition.

Merci d'avoir répondu. Malheureusement, la requête générée est légèrement différente puisque la condition de jointure a maintenant à la and arrival >= ? and arrival <= ? and departure >= ? and departure <= ?place AND ( r.arrival between ? and ? OR r.departure between ? and ? )(veuillez noter la ORclause). Cela ajoute des lignes supplémentaires au résultat qui ne devraient pas être là. Je suppose qu'il n'est pas possible de générer tous les types de conditions dans la jointure en utilisant QB.
Dede

1
En fait, j'ai remarqué que le? n'est pas ajouté dans les clauses ON. Ces valeurs dans ON ne sont pas paramétrées et ne sont pas protégées contre l'injection SQL. J'ai posté une question pour essayer de trouver la solution.
prograhammer

3
cela ne répondait pas à la question d'origine qui contenait une clause ou ignorée dans cette réponse.
ionescho

Solution parfaite. N'oubliez pas d'utiliser DB :: raw lorsque vous utilisez une valeur, sinon Laravel (au moins en 5.6.29) ajoute des guillemets et "brise" le but.
Adrian Hernandez-Lopez

35

Vous pouvez répliquer ces crochets dans la jointure de gauche:

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

est

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

Vous devrez ensuite définir les liaisons plus tard en utilisant "setBindings" comme décrit dans cet article SO: Comment lier des paramètres à une requête DB brute dans Laravel qui est utilisée sur un modèle?

Ce n'est pas joli mais ça marche.


32

Si vous avez des paramètres, vous pouvez le faire.

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

puis renvoyez votre requête

retourne $ results;


23

L'exemple de requête SQL comme celui-ci

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

Laravel rejoint avec plusieurs conditions

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

J'utilise laravel5.2 et nous pouvons ajouter des jointures avec différentes options, vous pouvez modifier selon vos besoins.

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

Il y a une différence entre les requêtes brutes et les sélections standard (entre les méthodes DB::rawet DB::select).

Vous pouvez faire ce que vous voulez en utilisant un DB::selectet en déposant simplement l' ?espace réservé, comme vous le faites avec des instructions préparées (c'est en fait ce que cela fait).

Un petit exemple:

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

Le deuxième paramètre est un tableau de valeurs qui sera utilisé pour remplacer les espaces réservés dans la requête de gauche à droite.


Cela signifie-t-il que les paramètres sont automatiquement échappés (sauf à partir de l'injection SQL)?
dédée

2
Oui, c'est juste un wrapper pour les instructions préparées par PDO.
Jason Lewis

Existe-t-il un moyen d'utiliser cette technique dans une clause ON dans une jointure gauche? Voir ma question ici . J'ai essayé à l'intérieur de ma fermeture à gauche, $join->on('winners.year','=',DB::select('?',array('2013'));mais je n'ai pas fonctionné.
prograhammer

ce n'est pas vraiment comment éloquent est conçu pour fonctionner. vous pourriez aussi bien faire un DB :: raw () et le faire vous
mydoglixu
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.