Skip to main content

Command Palette

Search for a command to run...

Belongs To Through Relations in Laravel 9

How can we "fake" belongs-to-through relations in Laravel using a native has-one-through relationship.

Updated
2 min read

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.

More from this blog

Amadeusz Annissimo Blog

16 posts