Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

osxtra's avatar

Aggregates as part get in an Eloquent query

Howdy, all, am having a little trouble with an Eloquent query that uses multiple models.

The trouble lies in gathering one of the returned columns, which is an aggregate.

Tables:

Model1 -> Contains foreignID fields for model2_id and model_id.
Model2 -> Whose ID field will match to model1.model2_id.
Model3 -> Whose ID field will match to model1.model3_id.
Model4 -> Contains a foreignID field which will match to model1.id.

Model1 records match exactly one Model2 record, and one Model3 record.

There are zero or more Model4 records associated with Model1.

This works in SQL:

select 
    mdl1.id as Mdl1_ID, 
    mdl2.id as Mdl2_ID, 
    mdl2.name as Name, 
    format(mdl1.field3, 2) as Desired_Number, 
    count(mdl4.Model1_id) as Desired_Aggregate
from  
    Model1 mdl1  
    join Model2 mdl2 on mdl1.mdl2_id = mdl2.id 
    join Model3 mdl3 on mdl1.mdl3_id = mdl3.id 
    left join Model4 mdl4 on mdl4.mdl1_id = mdl1.id 
where 
    mdl3.id = '5'
group by
    mdl1.id 
order by
    Desired_Aggregate desc,
    Name
limit 5;

SQL Result

| Mdl1_ID | Mdl2_ID | Name | Desired_Number | Desired_Aggregate |

| ------- | ------- | ---------------------- | --------------- | ----------------- |

| 1234 | 567 | Homer Simpson | 25.27 | 3 |

| 1357 | 216 | Pliny the Elder | 105.00 | 2 |

| 4416 | 704 | M.C. 900 Foot Jesus | 12.44 | 1 |

| 8092 | 35 | Carl Reiner | 82.19 | 0 |

| 6113 | 228 | Jubal Harshaw | 15.05 | 0 |

The controller has 'use' statements for all models.

This works in a controller method, WITHOUT the aggregate "Desired_Aggregate" column as part of the returned collection:

return Model1::join('model2', 'model1.model2_id', '=', 'model2.id')
    ->join('model3', 'model1.model3_id', '=', 'model3.id')
    ->where('model3.id', '=', '5')
    ->get([
        'model1.id as model1_id',
        'model2.id as model2_id',
        'model2.name as Name',
        'model1.ttl as Desired_Number'
        ]);

After adding a leftJoin so the query can match Model1.id to Model4.model1_id, use of the the aggregate 'count(model4.model1_id)' in the get() method breaks the query, saying it's an unknown column.

What would be the eloquent version of that working SQL query?

I have tried various combinations of ->select, using DB::table instead the model, and other things, but have not gotten this to work.

I could probably do two queries and merge based on Model1's ID, but that seems inefficient. There has to be a way to do it in one pass, with the same result as the working SQL query.

Any illumination would be most appreciated. Thanks!

0 likes
8 replies
osxtra's avatar

Sorry 'bout the bad SQL formatting...

osxtra's avatar

Thanks, SQL output didn't want to line up, but should look better...

Sinnbeck's avatar

The first thing I notice is the missing groupBy(). And a query like this, I would probably not use eloquent for. Just the plain query builder :)

Can you show the latest version with select and the aggregate?

osxtra's avatar

What I thought would work (just adding in the aggregate to pull counts from Model4) was:

return Model1::join('model2', 'model1.model2_id', '=', 'model2.id')
    ->join('model3', 'model1.model3_id', '=', 'model3.id')
    ->leftJoin('model4', 'model4.model1_id' '=', 'model1.id')
    ->where('model3.id', '=', '5')
    ->get([
        'model1.id as model1_id',
        'model2.id as model2_id',
        'model2.name as Name',
        'model1.ttl as Desired_Number',
        'count(model4.model1_id) as Desired_Aggregate'
        ])
    ->groupBy('Model1.ID')

When I add Model4, it crashes with:

   "Unknown column 'count(model4.model1_id)' in field list."

Is it possible to have an aggregate in the get method?

Returning the count - zero or not - as one of the columns in the collection is where I'm having trouble.

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@osxtra I don't know if you can get it to work with aggregates in get. But this is how I would do it. Also your groupBy is after you have executed the query

return Model1::join('model2', 'model1.model2_id', '=', 'model2.id')
    ->join('model3', 'model1.model3_id', '=', 'model3.id')
    ->leftJoin('model4', 'model4.model1_id' '=', 'model1.id')
   ->selectRaw('count(model4.model1_id) as Desired_Aggregate')
   ->addSelect([
        'model1.id as model1_id',
        'model2.id as model2_id',
        'model2.name as Name',
        'model1.ttl as Desired_Number', 
   ])
    ->where('model3.id', '=', '5')
    ->groupBy('model1.id')
   ->orderByDesc('Desired_Aggregate')
   ->orderBy('name')
   ->toBase() //convert to regular result instead of eloquent model 
    ->get()
osxtra's avatar

@Sinnbeck, Thank you, that was illuminating. Should have studied the Builder class more; hadn't tried using selectRaw or addSelect.

Alas, nothing wrong with your code, but calling ->get() still breaks, saying

"1055 'database.model1.id' isn't in GROUP BY"

Using ->toSql() instead of ->get() produces a valid query string.

The groupby error is weird, as it references a completely different table and field than what is being supplied ('name' aliased as 'Name' from model2).

Will poke around the Builder class to see if I can figure out why, but the get function calls at least six things so it may take a little bit to unpack it.

There is a groupByRaw method, but using that didn't seem to change anything, either with ->get() or ->toSql().

Also, trying

->groupBy('model1.id')

--or--

->groupByRaw('database.model1.id')

etc., just switched the error to the next joined table with

"1055 'database.model2.id' isn't in GROUP BY"

Also, the where value started rendering as a question mark, but using whereRaw fixed that, by adding any numeric formatting - format(model1.ttl, 2) as Desired_Number, etc. - to selectRaw.

(Taking those back out of selectRaw leaving only the aggregate didn't affect the groupby error when using ->get(), and ->toSql() still produced the correct query with them in selectRaw, no matter the order.)

In any event, thanks for pointing me to the selectRaw and addSelect methods. Now if ->get() would just work...

Take care!

Please or to participate in this conversation.