I have been trying to do some queries and getting a count on related tables using eloquent.
Tables:
- requests
- contact (belongs to requests)
- history (belongs to contact)
As such X number of requests each have Y number of contacts which in term each have Z number of histories
Using sql I can do something like this to get all the counts.
SELECT
id,
(
SELECT count(contact.id)
FROM contact
WHERE contact.requests_id = requests.id
) AS n_contact,
(
SELECT count(history.id)
FROM contact INNER JOIN history ON (history.contact_id = contact.id)
WHERE contact.requests_id = requests.id
) AS n_history
FROM requests;
But I am a bit lost when using eloquent to build queries. If for instance I was selecting all contacts for a given request at what point would I join/count the history? Or do I need to add in some accessor’s into the relevant Models for these 3 tables?
public function getAllContacts($id) {
return Requests::where('requests.id', '=', $id)
->join('requests', 'contact.requests_id', '=', 'requests.id')
->select('contact.*', 'requests.name');
->get();
}
Thanks in advance for any help.
Advertisement
Answer
You can use helper relation for this, if you’d like to use Eloquent instead of manual joins:
// Request model
public function contactsCount()
{
return $this->hasOne('Contact')->selectRaw('request_id, count(*) as aggregate')->groupBy('request_id');
}
public function getContactsCountAttribute()
{
if ( ! array_key_exists('contactsCount', $this->relations)) $this->load('contactsCount');
return $this->getRelation('contactsCount')->aggregate;
}
The same would go for Contact
model towards History
model.
For counting far relation (Request
-> History
) you can use hasManyThrough
relation with a little adjustment.
This way you can eager load those aggregates for multiple models without n+1 issue, nice and easy:
$requests = Request::with('contactsCount', 'contacts.historyCount')->get();
// 1 query for reuqests, 1 query for contacts count and 2 queries for contacts.historyCount
// example output
$requests->first()->contactsCount; // 17
$requests->first()->contacts->first()->historyCount; // 5