Skip to content

Eloquent Serialization — Model JSON Transformation in Laravel

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Eloquent Serialization. We cover key concepts, practical examples, and best practices to help you master this topic.

Laravel Eloquent serialization controls how models and collections convert to arrays and JSON, with API Resources providing fine-grained transformation control for API responses.

What You'll Learn

By the end of this tutorial, you'll customize model serialization with $hidden and $appends, use API Resources for JSON transformation, handle pagination, and conditionally include attributes.

Why Serialization Matters

Every API response requires careful control over which model attributes are exposed. Proper serialization prevents data leaks and provides consistent API output.

Real-World Use

A Laravel API uses UserResource to expose public profile data while hiding emails from non-admin users. The resource conditionally includes posts count and recent activity.

Serialization Path

flowchart LR
  A[Eloquent ORM] --> B[Serialization]
  B --> C[Cast & Append]
  B --> D[API Resources]
  B --> E[Conditional Load]
  B --> F[Pagination]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Basic Serialization Control

Hide sensitive attributes and append accessor values.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
    protected $hidden = [
        "password",
        "remember_token",
        "two_factor_secret",
    ];
    protected $appends = [
        "full_name",
        "is_admin",
    ];
    protected function fullName(): Attribute {
        return Attribute::make(get: fn() => "$this->first_name $this->last_name");
    }
    protected function isAdmin(): Attribute {
        return Attribute::make(get: fn() => $this->role === "admin");
    }
}

Conditional Attribute Inclusion

Dynamically include attributes in serialization.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Post extends Model {
    protected function excerpt(): Attribute {
        return Attribute::make(
            get: fn(mixed $value) => $value ?? Str::limit($this->content, 200),
        );
    }
    // Make visible/invisible at runtime
}
// Usage
$post->makeVisible("author_email");
$post->makeHidden("internal_notes");

API Resources

Full control over JSON API output.

<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource {
    public function toArray(Request $request): array {
        return [
            "id" => $this->id,
            "title" => $this->title,
            "slug" => $this->slug,
            "excerpt" => $this->excerpt,
            "published_at" => $this->published_at?->diffForHumans(),
            "author" => new UserResource($this->whenLoaded("author")),
            "tags" => TagResource::collection($this->whenLoaded("tags")),
            "comments_count" => $this->whenCounted("comments"),
            "can" => [
                "edit" => $request->user()?->can("update", $this->resource),
                "delete" => $request->user()?->can("delete", $this->resource),
            ],
        ];
    }
}

Resource Collections

Handle paginated and collection responses.

<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PostCollection extends ResourceCollection {
    public $collects = PostResource::class;
    public function toArray(Request $request): array {
        return [
            "data" => $this->collection,
            "meta" => [
                "total" => $this->total(),
                "current_page" => $this->currentPage(),
                "per_page" => $this->perPage(),
                "last_page" => $this->lastPage(),
            ],
            "links" => [
                "first" => $this->url(1),
                "last" => $this->url($this->lastPage()),
                "prev" => $this->previousPageUrl(),
                "next" => $this->nextPageUrl(),
            ],
        ];
    }
}

Conditional Relationships

Include relationships only when loaded.

<?php
namespace App\Http\Resources;
class OrderResource extends JsonResource {
    public function toArray(Request $request): array {
        return [
            "id" => $this->id,
            "total" => $this->total,
            "status" => $this->status,
            "items" => OrderItemResource::collection($this->whenLoaded("items")),
            "customer" => new CustomerResource($this->whenLoaded("customer")),
            "payment" => new PaymentResource($this->whenLoaded("payment")),
        ];
    }
}
// In controller
return new OrderResource(Order::with(["items", "customer"])->findOrFail($id));

Common Mistakes

1. Exposing Sensitive Data

Leaving password, tokens, or emails in $hidden accidentally. Always review serialized output.

2. N+1 in API Resources

Accessing $this->relation without whenLoaded causes N+1. Always use whenLoaded for relations.

3. Over-Appending Attributes

Every $appends attribute runs on every serialization. Keep appends minimal.

4. Inconsistent Resource Naming

Use ResourceCollection for collections, JsonResource for single models.

5. Not Using Pagination for Lists

Returning all records in list endpoints causes performance issues. Always paginate.

Practice Questions

1. How do you hide attributes from JSON output?

Add attributes to the $hidden array on the model.

2. What is whenLoaded used for?

Conditionally including relationships in resources only when they are loaded.

3. How do you append accessor values to JSON?

Add the accessor name to the $appends array.

4. What is the difference between JsonResource and ResourceCollection?

JsonResource transforms a single model. ResourceCollection transforms a collection.

5. Challenge: Create an API resource with conditional relationship inclusion.

<?php
class UserResource extends JsonResource {
    public function toArray(Request $r): array {
        return [
            "id" => $this->id,
            "name" => $this->name,
            "email" => $this->when($r->user()?->isAdmin(), $this->email),
            "posts" => PostResource::collection($this->whenLoaded("posts")),
            "posts_count" => $this->whenCounted("posts"),
        ];
    }
}

FAQ

What is the difference between $hidden and $guarded?

$hidden hides from JSON. $guarded protects from mass assignment.

Can I use API Resources without Eloquent?

Yes. Pass arrays or other objects that implement the Arrayable interface.

How do I serialize a collection without API Resources?

Use $collection->toArray() or $collection->toJson().

What is whenCounted?

Includes a count attribute when it has been loaded with withCount().

Can I nest API Resources infinitely?

Yes, but deep nesting hurts performance. Limit to 2-3 levels.

Mini Project: User API Resource

Build a complete user API resource with conditional exposure.

<?php
class UserResource extends JsonResource {
    public function toArray(Request $request): array {
        return [
            "id" => $this->hashId(),
            "display_name" => $this->display_name,
            "avatar" => $this->avatar_url,
            "joined" => $this->created_at->diffForHumans(),
            "email" => $this->when($this->email_visible, $this->email),
            "is_following" => $this->when($request->user(), fn() => $request->user()->isFollowing($this->resource)),
        ];
    }
}

What's Next

Eloquent Eager Loading Eloquent Performance Eloquent API Resources

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro