Skip to main content
This quickstart guides you through creating a blog application with type-safe data objects. You’ll learn the core functionality of Laravel Data by building real examples.
Make sure you’ve installed Laravel Data before starting.

Creating your first data object

1

Create the PostData class

Let’s create a data object for blog posts. A post has a title, content, status, and publication date:
use Spatie\LaravelData\Data;

class PostData extends Data
{
    public function __construct(
        public string $title,
        public string $content,
        public PostStatus $status,
        public ?CarbonImmutable $published_at
    ) {
    }
}
Store data objects in app/Data/PostData.php or use php artisan make:data Post to generate the file automatically.
2

Define the PostStatus enum

Create a native PHP enum for the post status:
enum PostStatus: string
{
    case draft = 'draft';
    case published = 'published';
    case archived = 'archived';
}
3

Create a PostData instance

You can create data objects like regular PHP objects:
$post = new PostData(
    'Hello laravel-data',
    'This is an introduction post for the new package',
    PostStatus::published,
    CarbonImmutable::now()
);
Or use the powerful from method to create from various sources:
$post = PostData::from([
    'title' => 'Hello laravel-data',
    'content' => 'This is an introduction post for the new package',
    'status' => PostStatus::published,
    'published_at' => CarbonImmutable::now(),
]);

Using data objects in controllers

1

Without Laravel Data (the old way)

Here’s a typical controller without Laravel Data:
class PostController
{
    public function store(Request $request)
    {
        $request->validate([
            'title' => ['required', 'string'],
            'content' => ['required', 'string'],
            'status' => ['required', new Enum(PostStatus::class)],
            'published_at' => ['nullable', 'date'],
        ]);

        $postData = new PostData(
            title: $request->input('title'),
            content: $request->input('content'),
            status: $request->enum('status', PostStatus::class),
            published_at: $request->has('published_at')
                ? CarbonImmutable::createFromFormat(DATE_ATOM, $request->input('published_at'))
                : null,
        );

        Post::create($postData->toArray());

        return redirect()->back();
    }
}
That’s a lot of boilerplate code!
2

With Laravel Data (the new way)

Laravel Data reduces this to just a few lines:
class PostController
{
    public function store(PostData $postData)
    {
        Post::create($postData->toArray());

        return redirect()->back();
    }
}
Here’s what happens automatically:
  1. Laravel boots and routes to PostController
  2. PostData generates validation rules from property types
  3. The request is validated automatically
  4. The PostData object is created from validated request data
  5. You receive a fully validated, type-safe data object
3

Inspect auto-generated validation rules

You can view the rules Laravel Data generates:
dd(PostData::getValidationRules($request->toArray()));
Output:
[
    "title" => ["required", "string"],
    "content" => ["required", "string"],
    "status" => ["required", Illuminate\Validation\Rules\Enum],
    "published_at" => ["nullable"]
]
Laravel Data automatically infers rules based on property types:
  • required for non-nullable properties
  • nullable for nullable properties
  • string, numeric, boolean, array based on type hints
  • enum for native enums

Adding validation attributes

Enhance auto-generated rules with validation attributes:
use Spatie\LaravelData\Attributes\Validation\Date;

class PostData extends Data
{
    public function __construct(
        public string $title,
        public string $content,
        public PostStatus $status,
        #[Date]
        public ?CarbonImmutable $published_at
    ) {
    }
}
Now the validation rules include the date rule:
[
    "title" => ["required", "string"],
    "content" => ["required", "string"],
    "status" => ["required", Illuminate\Validation\Rules\Enum],
    "published_at" => ["nullable", "date"] // 👈 Date rule added
]
Laravel Data provides dozens of validation attributes like Email, Max, Min, Url, and more.

Transforming data objects

Data objects can be easily transformed into arrays or JSON:
$postData->toArray();

// Result:
[
    "title" => "Hello laravel-data",
    "content" => "This is an introduction post",
    "status" => "published",
    "published_at" => "2020-05-16T00:00:00+00:00"
]

Nesting data objects

Create complex data structures by nesting data objects:
class AuthorData extends Data
{
    /**
     * @param array<int, PostData> $posts
     */
    public function __construct(
        public string $name,
        public array $posts
    ) {
    }
}
Create nested data objects from arrays:
$author = AuthorData::from([
    'name' => 'Ruben Van Assche',
    'posts' => [
        [
            'title' => 'Hello laravel-data',
            'content' => 'Introduction post',
            'status' => PostStatus::published,
        ],
        [
            'title' => 'What is a data object',
            'content' => 'How does it work',
            'status' => PostStatus::draft,
        ],
    ],
]);
The package automatically converts nested arrays into PostData objects based on the docblock type hint.

Working with lazy properties

Lazy properties are only included when explicitly requested, perfect for optimizing API responses:
use Spatie\LaravelData\Lazy;

class AuthorData extends Data
{
    public function __construct(
        public string $name,
        public Collection|Lazy $posts
    ) {
    }

    public static function fromModel(Author $author): self
    {
        return new self(
            $author->name,
            Lazy::create(fn() => PostData::collect($author->posts))
        );
    }
}
Control what’s included in the output:
$author = AuthorData::from($authorModel);
return $author;

// Result:
{
    "name": "Ruben Van Assche"
}

Generating blueprints

Create empty data structures for forms:
PostData::empty();

// Result:
[
    'title' => null,
    'content' => null,
    'status' => null,
    'published_at' => null,
]
With default values:
PostData::empty([
    'status' => PostStatus::draft
]);

// Result:
[
    'title' => null,
    'content' => null,
    'status' => 'draft',
    'published_at' => null,
]

Next steps

You’ve learned the basics! Laravel Data can do much more:

Casts

Convert simple types to complex types

Transformers

Convert complex types to simple types

Validation

Deep dive into validation features

Collections

Work with collections of data objects

Eloquent Casting

Cast data objects in Eloquent models

TypeScript

Generate TypeScript definitions

Build docs developers (and LLMs) love