Why you should use withPivot in Laravel to simplify your relations

Last updated 12th November, 2023

Introduction

With the use of pivot tables and Laravel's BelongsToMany relation, you can relate two separate data types together.

Pivot tables traditionally consist of two columns; one ID representing one foreign data object, and second, representing another. The interesting part comes where you want to add additional data to this table. Data that might represent configuration about the join between these datapoints.

By default in Laravel, you won't be able to access this additional data. But in this article, I'll detail usage of withPivot, and some other handy methods you can use to simplify and optimise your Laravel relations.

Accessing Pivot data

We'll use an example I've worked with recently; a user having subscriptions to multiple mailing lists.

Let's explore how withPivot in Laravel can simplify your relations.

In our application, consider the following scenario:

class User extends Model
{
    public function mailingLists(): BelongsToMany
    {
        return $this->belongsToMany(MailingList::class); 
    }
}

When calling $user->mailingLists, we can expect to receive a collection of mailing lists that the user belongs to. However, by default, Laravel only includes the related keys and not the additional data stored in the pivot table.

What if the pivot table contains information crucial to our application?

For instance, I needed to store a confirmation code and a confirmation timestamp on the pivot table in a recent project to be referenced later. Our goal was to use the confirmation code for a subscription confirmation email and track when users confirmed their subscriptions.

Unfortunately, with our current code, we lack access to that data. Laravel's default behaviour is to avoid unnecessary data retrieval from the database and includes only the related keys in BelongsToMany relations.

To retrieve the pivot record, we may try:

$list = $user->mailingLists->first();

$list->pivot

The result would be something like:

[
    'mailing_list_id' => 1,
    'user_id' => 1,
]

Getting The Additional Fields

So how do we get Laravel to give us those additional fields?

This is where withPivot comes in handy. It allows you to specify the records you want to retrieve from the pivot table and include them with the relation:

class User extends Model
{
    public function mailingLists(): BelongsToMany
    {
        return $this
            ->belongsToMany(MailingList::class)
            ->withPivot([
                'confirmation_code', 'confirmed_at'
            ]);
    }
}

Now, when we call the relation, we will see those two additional records included:

[
    'mailing_list_id' => 1,
    'user_id' => 1,
    'confirmation_code' => 'supersecretcode',
    'confirmed_at' => null,
]

Making This More Fluent

We can take it a step further. Laravel provides the as method, allowing you to customize the pivot relation's name. While not necessary, it can greatly improve the clarity of your business logic:

class User extends Model
{
    public function mailingLists(): BelongsToMany
    {
        return $this
            ->belongsToMany(MailingList::class)
            ->withPivot([
                'confirmation_code', 'confirmed_at'
            ])
            ->as('subscription');
    }
}

Now, instead of accessing the pivot relation as before:

$list = $user->mailingLists->first();

$list->pivot;

We can use the name provided to the 'as' method to reference the pivot relation:

$list = $user->mailingLists->first();

$list->subscription;

This results in a more elegant and readable code structure that is easier for developers, including the original author, to understand.

The WithTimestamps Helper

If the extra data you need from your pivot tables are timestamps, Laravel includes a helper to make this access from fluent. Laravel includes a withTimestamps helper that will pull in the createdAt and updatedAt timestamps into your pivot data.

Here's how that might look with our example above:

class User extends Model
{
    public function mailingLists(): BelongsToMany
    {
        return $this
            ->belongsToMany(MailingList::class)
            ->withTimestamps();
    }
}

Accessing the pivot data will result in the following:

[
    'mailing_list_id' => 1,
    'user_id' => 1,
    'created_at' => '2023-07-04 09:21:03',
    'updated_at' => '2023-07-04 09:21:03'
]

You can also customise the names of the timestamp columns, if required:

class User extends Model
{
    public function mailingLists(): BelongsToMany
    {
        return $this
            ->belongsToMany(MailingList::class)
            ->withTimestamps('confirmed_at', 'reset_at');
    }
}

Resulting in a similar output, but with your custom timestamp columns:

[
    'mailing_list_id' => 1,
    'user_id' => 1,
    'confirmed_at' => '2023-07-04 09:21:03',
    'reset_at' => '2023-07-04 09:21:03'
]

Conclusion

In conclusion, leveraging Laravel's withPivot method, the as method, and the withTimestamps helper can greatly simplify your relation management and enhance your Laravel development workflow.

By optimizing your code readability and accessing necessary pivot data, you can streamline your application's functionality and improve overall efficiency. Incorporate these best practices into your Laravel projects to elevate your development experience and achieve remarkable results.

Hope this helps 🤙

No comments yet…

DevInTheWild.

Login To Add Comments.

Want More Like This?

Subscribe to occasional updates.

Related Articles