Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a subquery using Laravel Eloquent?

I have the following Eloquent query (This is a simplified version of a query which consists of of more wheres and orWheres hence the apparent roundabout way of going about this - the theory is what's 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();

As you can see I pluck the earliest date that occurs on or after my start_date. This results in a seperate query being run to get this date which is then used as a parameter in the main query. Is there a way in eloquent to embed the queries together to form a subquery and thus only 1 database call rather than 2?

Edit:

As per @Jarek's answer this is my query:

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

The orWhere blocks are causing all parameters in the query to suddenly become unquoted. E.g. WHEREprice_date>= 2009-09-07. When I remove the orWheres the query works fine. Why is this?

like image 212
harryg Avatar asked Nov 21 '14 15:11

harryg


People also ask

How do you write a subquery in eloquent?

$bills = Bill::upcoming()->where('amount', function ($query) { $query->selectRaw('SUM(deposits. amount)') ->from(with(new Deposit)->getTable()) ->where('bill_id', '=', 'bills.id') })->get();

How do I create a subquery in Laravel?

For subquery in select statement we are going to use DB::raw(). DB raw function through we can simply make suquery join in Laravel Eloquent Query Builder. Now if i use MySQL Query then it will simple as like bellow, But i require to convert this query into Laravel Query Builder. ->get();

Is Laravel eloquent slow?

Laracasts Veteran Sure it is slow, first it fetch all the record in one time, then it instantiate an eloquent object for each lines. It is not made to retrieve 10 000 objects.

What is the difference between eloquent and query builder in Laravel?

Eloquent ORM is best suited working with fewer data in a particular table. On the other side, query builder takes less time to handle numerous data whether in one or more tables faster than Eloquent ORM. In my case, I use ELoquent ORM in an application with tables that will hold less than 17500 entries.


1 Answers

This is how you do a subquery where:

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

Unfortunately orWhere requires explicitly provided $operator, otherwise it will raise an error, so in your case:

$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: You need to specify from in the closure in fact, otherwise it will not build correct query.

like image 108
Jarek Tkaczyk Avatar answered Sep 21 '22 17:09

Jarek Tkaczyk