/derived-values-in-laravel-eloquent-models

Derived values in Laravel Eloquent Models

March 7th, 2024
· 3 min read ·
Code
Laravel

Coming from Svelte, I appreciated the availability of derived/reactive values. They work like this:

let count = 0;
$: doubled = count * 2;

This essentially means that whenever count changes, doubled would change as well (obviously, hence the name).

For a current project, I needed similar behaviour in a Laravel Eloquent Model.

This model has a one-to-many relationship. For the sake of this tutorial, let’s say it’s a task-list that has many tasks. Apart from returning all tasks (via the tasks attribute), I also wanted to add a filtered_tasks attribute. If there are from and to query params in the request, it should only return the tasks in the specified timeframe.

There are two steps for accomplishing this:

1. Create the new attribute with an accessor

From the docs:

An accessor transforms an Eloquent attribute value when it is accessed.

The cool thing is, you can also “define” new attributes that are not even available in the original model. For example, if you want to derive them from an existing attribute (like we want).

Quick heads up, I’m using the older syntax with getXYAttribute here, as I find it easier to use.

Here’s how you can do it:

public function getFilteredTasksAttribute()
{
	// Get query params
    $from = request()->query('from');
    $to = request()->query('to');

    // Validate date format
    try {
        $from = Carbon::createFromFormat('Y-m-d', $from);
        $to = Carbon::createFromFormat('Y-m-d', $to);
    } catch (Exception $e) {
        $from = null;
        $to = null;
    }

    // Build the query
    $query = $this->revenue_events();

    // Filter items, if params are available
    if ($from && $to) {
        $query = $query->whereBetween('created_at', [$from, $to]);
    }

    // Return data
    return $query->get();
}

Note that I’m using the request() helper for accessing the query parameters. You can’t pass the request into the accessor via a parameter, like you would in a controller method.

The only thing left now is to append this new attribute to the model.

2. Append the attribute

This one is pretty easy. Just use the appends property of the model and add the new attribute in snake case to it:

protected $appends = [
    'filtered_tasks',
];

More info on that part in the docs as well.

That’s it already! You can now access the new attribute like normal: $taskList->filtered_tasks.

I hope this tutorial helped you. If you know a better or more efficient way to do this inside an Eloquent Model, please reach out!

What are your thoughts on this post?

I’d love to hear from you! Please write me an email by clicking this link (I reply to every email I receive!).