Enriching Your Laravel API: Appending Computed Values to JSON Output

Enriching Your Laravel API: Appending Computed Values to JSON Output

When working with Laravel's Eloquent models, you often need to include computed or derived values in your API responses. Laravel provides an elegant way to append these values to your model's JSON representation. Let's explore how to leverage this feature to create more informative and flexible APIs.

Creating an Accessor

The first step is to define an accessor for the value you want to append. Accessors allow you to add custom attributes to your model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Determine if the user is an administrator.
     */
    protected function isAdmin(): Attribute
    {
        return new Attribute(
            get: fn () => $this->role === 'admin',
        );
    }
}

In this example, we've created an isAdmin accessor that checks if the user's role is 'admin'.

Appending the Attribute

To include this accessor in your model's JSON representation, add its name to the $appends property:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['is_admin'];

    // ... accessor definition here
}

Now, whenever this model is converted to an array or JSON, it will include the is_admin attribute.

Dynamic Appends

You can also append attributes dynamically using the append method:

$user = User::first();
$user->append('is_admin');

This is useful when you need to include certain attributes only in specific scenarios.

Appending Relationship Counts

Laravel also allows you to append relationship counts easily:

class User extends Model
{
    protected $appends = ['posts_count'];

    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    protected function postsCount(): Attribute
    {
        return new Attribute(
            get: fn () => $this->whenCounted('posts'),
        );
    }
}

This will include the posts count in the JSON output, but only if it has been explicitly counted in the query.

Practical Example

Let's consider a more comprehensive example:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $appends = ['formatted_price', 'is_in_stock'];

    protected function formattedPrice(): Attribute
    {
        return new Attribute(
            get: fn () => "$" . number_format($this->price, 2),
        );
    }

    protected function isInStock(): Attribute
    {
        return new Attribute(
            get: fn () => $this->stock_quantity > 0,
        );
    }

    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function toArray()
    {
        $array = parent::toArray();
        
        if ($this->relationLoaded('category')) {
            $array['category_name'] = $this->category->name;
        }

        return $array;
    }
}

By leveraging Laravel's attribute appending features, you can create rich, informative API responses that include computed values and conditional data. This allows for more flexible and powerful APIs without complicating your database structure or query logic.

If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe