Belongs To Through Relations in Laravel 9
How can we "fake" belongs-to-through relations in Laravel using a native has-one-through relationship.
Take the following 3 models:
customer
id - integer
name - string
invoices
id - integer
status - string
customer_id - integer
credit_notes
id - integer
status - string
invoice_id - integer
Laravel gives us an easy way to get all Credit Notes that belong to a particular Customer through the invoice table. What you need to do, is to define a hasManyThrough relationship on the Customer model, as in:
class Customer extends Model
{
/**
* Get all credit notes of the customer
*/
public function creditNotes()
{
// we could call the below function with just first 2 arguments,
// but I like being explicit when defining 'through' relations.
return $this->hasManyThrough(
CreditNote::class,
Invoice::class,
'customer_id',
'invoice_id',
'id',
'id'
);
}
}
What if we want to find out which Customer a particular Credit Note belongs to? We could call $creditNote->invoice->customer, but what if we don't want to load the Invoice model in the process? We can use the hasOneThrough relationship that can also act as a "belongs to through" relationship. The "trick" is to use custmomize arguments 3-6 of the relationship as below:
class CreditNote extends Model
{
/**
* Get the customer for whom the credit note was issued.
*/
public function customer()
{
return $this->hasOneThrough(
Customer::class,
Invoice::class,
'id', // refers to id column on invoices table
'id', // refers to id column on customers table
'invoice_id', // refers to invoice_id column on credit_notes table
'customer_id' // refers to customer_id column on invoices table
);
}
}
Why do we need "Belongs To Through" relationships?
In my case, I use this "trick" a lot when I need to eager-load deep relationship, as in:
App\CreditNote::all()->load('customer');
This saves us from unnecessarily loading Invoices associated with each Credit Note.
So far, I haven't run into any issues with defining "Belongs To Through" relationship this way, but since it's not something that you can find in the official Laravel Docs, I supposed there may be some "gotchas". If you have run into any of them, please let me know in the comments below.
Comments
Join the discussionThanks for the great article, needed this about a month ago or so.
But here
'invoice_id', // refers to invoice_id column on customers table
I think you meant on the credit_notes table, right?
Thank you for reading the article and the comment :) You're right, I just fixed it.