web-dev-qa-db-fra.com

Comment créer une sous-requête en utilisant Laravel Eloquent?

J'ai la requête Eloquent suivante (Il s'agit d'une version simplifiée d'une requête qui se compose de plus de wheres et orWheres d'où la façon apparente de contourner cela - la théorie est ce qui est important):

$start_date = //some date;

$prices = BenchmarkPrice::select('price_date', 'price')
->orderBy('price_date', 'ASC')
->where('ticker', $this->ticker)
->where(function($q) use ($start_date) {

    // some wheres...

    $q->orWhere(function($q2) use ($start_date){
        $dateToCompare = BenchmarkPrice::select(DB::raw('min(price_date) as min_date'))
        ->where('price_date', '>=', $start_date)
        ->where('ticker', $this->ticker)
        ->pluck('min_date');

        $q2->where('price_date', $dateToCompare);
    });
})
->get();

Comme vous pouvez le voir, je pluck la première date qui se produit le ou après mon start_date. Il en résulte une requête distincte exécutée pour obtenir cette date qui est ensuite utilisée comme paramètre dans la requête principale. Existe-t-il un moyen éloquent d'incorporer les requêtes ensemble pour former une sous-requête et donc seulement 1 appel de base de données au lieu de 2?

Modifier:

Selon la réponse de @ Jarek, voici ma requête:

$prices = BenchmarkPrice::select('price_date', 'price')
->orderBy('price_date', 'ASC')
->where('ticker', $this->ticker)
->where(function($q) use ($start_date, $end_date, $last_day) {
    if ($start_date) $q->where('price_date' ,'>=', $start_date);
    if ($end_date) $q->where('price_date' ,'<=', $end_date);
    if ($last_day) $q->where('price_date', DB::raw('LAST_DAY(price_date)'));

    if ($start_date) $q->orWhere('price_date', '=', function($d) use ($start_date) {

        // Get the earliest date on of after the start date
        $d->selectRaw('min(price_date)')
        ->where('price_date', '>=', $start_date)
        ->where('ticker', $this->ticker);                
    });
    if ($end_date) $q->orWhere('price_date', '=', function($d) use ($end_date) {

        // Get the latest date on or before the end date
        $d->selectRaw('max(price_date)')
        ->where('price_date', '<=', $end_date)
        ->where('ticker', $this->ticker);
    });
});
$this->prices = $prices->remember($_ENV['LONG_CACHE_TIME'])->get();

Les blocs orWhere provoquent la disparition soudaine de tous les paramètres de la requête. Par exemple. WHEREprice_date>= 2009-09-07. Lorsque je supprime le orWheres, la requête fonctionne correctement. Pourquoi est-ce?

16
harryg

Voici comment vous effectuez une sous-requête où:

$q->where('price_date', function($q) use ($start_date)
{
   $q->from('benchmarks_table_name')
    ->selectRaw('min(price_date)')
    ->where('price_date', '>=', $start_date)
    ->where('ticker', $this->ticker);
});

Malheureusement orWhere nécessite explicitement fourni $operator, sinon cela générera une erreur, donc dans votre cas:

$q->orWhere('price_date', '=', function($q) use ($start_date)
{
   $q->from('benchmarks_table_name')
    ->selectRaw('min(price_date)')
    ->where('price_date', '>=', $start_date)
    ->where('ticker', $this->ticker);
});

EDIT: Vous devez spécifier from dans la fermeture en fait, sinon il ne construira pas la requête correcte.

24
Jarek Tkaczyk