The Inefficiency of the Human-Centric Web Modern websites are built for eyes, not algorithms. When an AI agent crawls a standard webpage, it must sift through mountains of HTML, CSS, and JavaScript just to find a single piece of data. This bloat creates a massive bottleneck. Every unnecessary tag and styling rule translates into wasted tokens, higher costs, and slower response times. The industry is reaching a tipping point where serving the same heavy payload to a bot as you do to a human no longer makes technical or financial sense. Markdown as the Universal Protocol A new trend is emerging: returning Markdown specifically for AI agents. Markdown strips away the visual noise, providing a clean, semantic structure that Large Language Models (LLMs) understand natively. This approach dramatically reduces token consumption. Cloudflare recently signaled the importance of this shift by introducing "Markdown for Agents," a feature designed to detect bot traffic and serve optimized content automatically. By delivering structured text instead of nested div tags, developers ensure their data is parsed accurately and cheaply. Tools for the Agent-Aware Backend The ecosystem is moving fast to support this transition. Laravel Cloud has integrated specific agent-focused markdown features, while the Spatie team is launching laravel-markdown-response, a package that streamlines this delivery for PHP developers. Independent innovators are also contributing, such as Emra with markdown.new, a tool specifically designed for agent-centric formatting. These tools allow developers to treat AI agents as first-class citizens in their application's routing logic. The Economics of Agent Access Beyond technical efficiency, this shift introduces a new pricing dimension for Software as a Service (SaaS). We are likely moving toward a tiered web where human access remains free or low-cost, while AI agent access carries a premium. If a bot scrapes your entire database in seconds using your optimized Markdown endpoints, it places a different kind of load on your business model. Differentiating traffic by client type allows for sophisticated pricing strategies that reflect the high value—and high resource cost—of AI-driven consumption.
Spatie
Companies
- Feb 19, 2026
- Feb 13, 2026
- Feb 13, 2026
- Jan 20, 2026
- Nov 11, 2025
The New Frontier of AI-Native Development The relationship between developers and their code is undergoing a fundamental transformation. We are moving past the era of simple auto-completion and into a world where AI agents act as full-fledged pair programmers. Ashley Hindle, leading the AI initiatives at Laravel, describes this shift not as a replacement of the developer's craft, but as an expansion of their capabilities. The challenge remains that while Large Language Models (LLMs) are becoming increasingly sophisticated, they often lack the specific, up-to-date context of a framework's evolving ecosystem. They might know PHP, but they might not know the breaking changes in the latest version of Pest or the specific architectural nuances of a Filament project. This is where Laravel Boost enters the scene. It is not an LLM itself; rather, it is a sophisticated bridge. By providing a composer package that injects guidelines, tools, and version-specific documentation directly into the AI agent's context, it eliminates the "hallucination gap" that occurs when an AI relies on stale training data. The goal is simple: make the AI agent a more competent contributor by giving it the same reference materials a human developer would use. This approach moves development from "vibe coding"—relying on the AI's best guess—to a deterministic, high-quality workflow grounded in the actual state of the codebase and the framework. The Architecture of Context: Ingestion and Vector Search To understand how Boost works, we must look at the ingestion pipeline that powers its documentation search. Unlike static documentation, the information fed to an AI agent needs to be formatted for retrieval. Ashley Hindle explains that the team uses Laravel Cloud to host an API that serves as the central nervous system for documentation. The pipeline downloads markdown files from GitHub APIs and processes them through a recursive text splitter. This "chunking" is vital because an AI cannot ingest a 50-page manual in one go and expect to find a specific method signature accurately. These chunks are then vectorized using OpenAI embedding models and stored in PostgreSQL via PGVector. Interestingly, the team does not rely solely on vector search. They employ a hybrid approach that includes Postgres full-text search with GIN indexes. This dual-layer strategy ensures that both semantic meaning (found through embeddings) and specific syntax or keyword matches (found through full-text search) are captured. For a developer, this means when the AI searches for a specific Inertia.js helper, it finds the exact documentation snippet relevant to their specific version, rather than a generic or outdated example. Mastering the Model Context Protocol (MCP) A core technical pillar of Boost is the Model Context Protocol (MCP). Think of MCP as a standardized way for an AI agent to "talk" to a server and use its features. Ashley Hindle uses a physical analogy: if the AI is the brain, MCP provides the hands. It allows the agent to ask, "What are you capable of?" and receive a list of tools—such as searching documentation, scanning a `composer.lock` file, or checking Tailwind CSS configurations. The brilliance of the MCP implementation in Boost lies in its invisibility. When a developer installs Boost, it auto-detects system-installed IDEs and agents like Cursor, Claude Code, or PHPStorm and configures the MCP server automatically. The AI agent then decides when to call these tools based on the user's prompt. If you ask the AI to write a test, it sees the `search_docs` tool in its inventory, notices you have Pest installed, and retrieves the latest Pest documentation before writing a single line of code. This autonomous decision-making by the AI, guided by the tool descriptions provided by Boost, creates a seamless experience where the developer doesn't have to manually prompt the AI to "look at the docs." Guidelines vs. Tools: The Art of Nudging There is a subtle but critical distinction between providing an AI with a tool and providing it with a guideline. A tool is a functional capability, while a guideline is a set of behavioral rules. Ashley Hindle discovered during development that tools alone weren't enough. An AI might have access to documentation but still write code in an old style. By providing specific guidelines—often delivered via `claude.md` or `custom-instructions` files—Boost "nudges" the AI to follow modern conventions. These guidelines are dynamically generated based on the project's specific dependencies. If a project uses Livewire, Boost includes Livewire guidelines; if it uses React, it swaps them. This prevents context bloat, ensuring the AI isn't distracted by irrelevant rules. Furthermore, Boost is designed to respect the "existing conventions" of a codebase. Guidelines often tell the AI to look at sibling controllers or existing patterns first. This ensures that the AI doesn't just write "perfect" Laravel code, but code that actually fits the specific project it is working in. The team is currently working on an override system that allows developers to provide their own custom blade files for guidelines, ensuring that team-specific standards take precedence over defaults. The Economics of Tokens and Efficiency A common concern with AI-assisted development is the cost and token usage. Adding thousands of lines of documentation and guidelines to every request sounds expensive. However, Ashley Hindle argues that Boost often pays for itself. While the guidelines might add roughly 2,000 tokens to a request—a small fraction of the 200,000+ context windows in modern models like Claude 3.5 Sonnet—they significantly reduce the number of failed attempts. When an AI has the correct context, it gets the code right on the first try. Without Boost, a developer might go through five or six back-and-forth prompts to correct the AI's hallucinations, consuming far more tokens in the long run. Additionally, many providers now support prompt caching. Because the Boost guidelines remain consistent across a session, they are frequently cached at the API level, often resulting in a 90% discount on those tokens. The efficiency isn't just financial; it's temporal. The developer stays in the "flow state" because they aren't constantly acting as a human debugger for the AI's mistakes. Future Horizons: Benchmarks and Package Integration The roadmap for Laravel Boost is ambitious. One of the most significant upcoming projects is "Boost Benchmarks." Ashley Hindle is building a comprehensive suite of projects and evaluations to move beyond "gut feel" testing. This will allow the team to statistically prove that one version of Boost is, for example, 20% more accurate at fixing bugs in Filament than the previous version. It will also provide data on which LLMs—be it Claude, GPT-4o, or Gemini—perform best with specific Laravel tasks. Another major shift is the move toward a package-contributed guideline system. The Laravel team cannot write and maintain guidelines for every package in the ecosystem. The goal is to create an API that allows package creators—like Spatie—to include their own Boost-compatible guidelines within their repositories. When a developer runs `boost install`, the system will detect these third-party packages and automatically pull in the author-approved AI instructions. This decentralization will ensure that the entire PHP ecosystem can become AI-native, with every package providing the necessary context for agents to use it effectively. As context windows continue to expand toward the millions, the bottleneck will no longer be how much the AI can remember, but how accurately we can feed it the truth.
Aug 30, 2025Overview of Modern Laravel Data Strategies Efficiently moving data from a database to a user's browser involves more than simple SQL queries. It requires a cohesive strategy that maintains data integrity, ensures developer productivity, and optimizes performance. This tutorial explores two pillars of the Laravel ecosystem: TypeScript integration via Spatie packages and the advanced application of the Eloquent ORM. By synchronizing server-side PHP types with client-side TypeScript definitions, developers can eliminate a massive category of "undefined" errors. Simultaneously, mastering Eloquent ORM allows for the creation of readable, performant code that scales from simple MVPs to large-scale data systems. Prerequisites for Full-Stack Integration To get the most out of this guide, you should have a solid foundation in the following areas: * **PHP & Laravel Fundamentals:** Familiarity with Laravel's routing, controllers, and Eloquent ORM models. * **JavaScript/TypeScript:** Basic understanding of TypeScript interfaces and how Inertia.js bridges the gap between the two languages. * **Composer & NPM:** Proficiency in managing packages on both the backend and frontend. * **Relational Databases:** Conceptual knowledge of table relationships (one-to-many, many-to-many). Key Libraries & Tools We will utilize several industry-standard tools and libraries specifically designed to enhance the Laravel experience: * Laravel Data **(Spatie):** A powerful package that replaces traditional Laravel Resources and Form Requests with rich Data Transfer Objects (DTOs). * **TypeScript Transformer (Spatie):** A tool that scans your PHP classes and automatically generates matching TypeScript definitions. * Inertia.js**:** The "modern monolith" framework that allows you to build single-page apps using classic server-side routing. * **Laravel IDE Helper:** A must-have for local development to ensure your editor understands Eloquent ORM's magic methods. * **Sentry:** While used for error tracking, it's often a hallmark of professional-grade Laravel deployments. Code Walkthrough: Implementing Consistent Types Step 1: Defining the Data Object Instead of returning a model directly, we create a Laravel Data object. This acts as our single source of truth. We use the `#[TypeScript]` attribute to signal that this class should be transformed. ```python namespace App\Data; use Spatie\LaravelData\Data; use Spatie\TypeScriptTransformer\Attributes\TypeScript; #[TypeScript] class UserData extends Data { public function __construct( public int $id, public string $first_name, public string $last_name, public string $email, public ?string $avatar, ) {} public static function fromModel(User $user): self { return new self( id: $user->id, first_name: $user->first_name, last_name: $user->last_name, email: $user->email, avatar: $user->avatar_url, // Custom attribute ); } } ``` In this block, we define exactly what the frontend receives. By using `fromModel`, we can transform database-specific names into a cleaner API for our React or Vue components. Step 2: Automating Type Generation Once the PHP classes are ready, we run the transformation command. This creates a `.d.ts` file in our resources directory. ```bash php artisan typescript:transform ``` This command looks for the `#[TypeScript]` attribute and converts the PHP types (string, int, bool, nullable) into their TypeScript equivalents. This ensures that if you change a field name in PHP, your frontend will immediately show a red squiggly line until it's fixed. Step 3: Consuming Types in the Frontend In our Inertia.js components, we can now import these generated types. This gives us full autocomplete support when accessing properties like `user.first_name`. ```typescript import { UserData } from '@/types/generated'; interface Props { user: UserData; } default function Dashboard({ user }: Props) { return ( <div>Welcome, {user.first_name}</div> ); } ``` Deep Dive into Eloquent ORM Optimization Drishti Jain emphasizes that Eloquent ORM is a sophisticated engine that requires careful handling to maintain speed. Understanding the difference between how data is retrieved and how it's modified is crucial for scaling. Efficient Querying with Scopes Instead of cluttering your controllers with repetitive `where` clauses, use Query Scopes to encapsulate business logic. This makes your code more readable and easier to test. ```python // Inside your Model public function scopeActive($query) { return $query->where('status', 'active')->where('verified_at', '!=', null); } // Inside your Controller $users = User::active()->get(); ``` The Power of Eager Loading The N+1 query problem is the most common performance killer in Laravel. When you loop through 50 users and access their `posts`, Laravel might execute 51 queries. Use the `with()` method to reduce this to just two queries. ```python // Bad: N+1 problem $users = User::all(); foreach($users as $user) { echo $user->profile->bio; } // Good: Eager Loading $users = User::with('profile')->get(); ``` Drishti Jain notes that while eager loading is vital, you should avoid "unnecessary" eager loading for data that isn't always used, as this bloats memory usage. Syntax Notes & Conventions * **Attributes vs. Annotations:** Modern Laravel uses PHP 8 attributes (like `#[TypeScript]`) which are natively parsed, unlike the older docblock annotations. * **CamelCase vs. Snake_case:** While PHP models typically use snake_case for database columns, many developers use Laravel Data to transform these into camelCase for the JavaScript frontend to follow TypeScript conventions. * **Fluent Interface:** Eloquent ORM uses a fluent interface, allowing you to chain methods like `User::where(...)->active()->latest()->paginate()`. The order often matters for performance, specifically placing filters before sorting. Practical Examples Real-World Case: The Address Form When building an address creation form, you can use Laravel Data to both provide the initial empty state to the frontend and validate the incoming request. This eliminates the need for separate Form Request classes and manual array mapping. 1. **Backend:** The Data object defines the validation rules. 2. **Frontend:** The generated TypeScript interface ensures the form inputs match the expected keys. 3. **Result:** A perfectly typed form where the frontend and backend are never out of sync. Tips & Gotchas * **The Hidden Data Key:** Standard Laravel Resources wrap data in a `data` key. Laravel Data gives you more control over this, allowing you to flatten the response for simpler frontend access. * **CI/CD Integration:** Do not commit generated TypeScript files if you are in a large team. Instead, run the transformation command as part of your build process or use a Vite plugin to watch for changes in real-time. * **Database Transactions:** When testing Eloquent ORM logic, always wrap your tests in transactions. This ensures your test database stays clean without needing to manually delete records after every run. * **Batch Processing:** For datasets with millions of rows, never use `all()`. Use `chunk()` or `lazy()` to process records in small batches to avoid exhausting the server's memory.
Aug 21, 2024Overview Building an API is a rite of passage for many developers, but building one that remains stable under load, remains easy to maintain, and provides a delightful developer experience is an entirely different challenge. This guide focuses on creating performant APIs by leaning heavily into the Laravel ecosystem. We explore how to move beyond basic CRUD operations to implement advanced route management, standardized response objects, robust caching strategies, and asynchronous write operations. The goal is to maximize the tools Laravel provides out of the box to build systems that scale gracefully without immediately reaching for third-party dependencies. Prerequisites To follow this tutorial, you should have a solid grasp of: - **PHP 8.2+**: Familiarity with modern features like read-only classes and enums. - **Laravel Fundamentals**: Understanding of Eloquent, Controllers, and Migrations. - **RESTful Concepts**: Basic knowledge of HTTP methods (GET, POST, etc.) and status codes. - **Composer**: Ability to manage PHP dependencies. Key Libraries & Tools - Laravel Framework: The primary PHP framework used for building the API. - Eloquent ORM: Laravel's built-in database mapper used for data retrieval and manipulation. - Laravel Query Builder: While the tutorial emphasizes native tools, Spatie's package is highlighted as a premier tool for filtering and sorting. - Laravel Vapor: Mentioned as a serverless deployment platform for extreme scaling. - Octane: A high-performance application server for Laravel that utilizes Swoole or RoadRunner. - Redis / Database: Used as the caching driver back-end. Section 1: Strategic Route Management and Versioning Organization is the first step toward performance. Visual overload and cognitive stress are real issues when your `api.php` file swells to hundreds of lines. Instead of a monolithic file, you should modularize your routes by resource and version them from day one. Versioning the API Versioning prevents breaking changes for your consumers. By prefixing your routes with `V1`, `V2`, etc., you can iterate on specific endpoints without disrupting existing integrations. ```php Route::prefix('v1')->name('v1:')->group(function () { Route::prefix('conversations')->name('conversations:')->group(base_path('routes/api/v1/conversations.php')); Route::prefix('messages')->name('messages:')->group(base_path('routes/api/v1/messages.php')); }); ``` Standalone API Configuration If you are building a standalone API, you might want to remove the default `api` prefix provided by Laravel. This is done in the application bootstrap or a service provider by setting the prefix to an empty string. This creates a cleaner URL structure like `https://api.myapp.test/v1/conversations`. Section 2: Standardizing Data with API Resources Performance isn't just about speed; it's about the size of the payload sent over the wire. Laravel API Resources act as a transformation layer between your Eloquent models and the JSON response. They allow you to be surgical about what data is exposed. The Conversation Resource By defining a resource, you ensure consistency across your application. Every time a conversation is returned, it follows the same shape. Use the `whenLoaded` method to prevent N+1 query issues when including relationships like a sender. ```php namespace App\Http\Resources\V1; use Illuminate\Http\Resources\Json\JsonResource; class ConversationResource extends JsonResource { public function toArray($request): array { return [ 'id' => $this->id, 'name' => $this->name, 'sender' => new UserResource($this->whenLoaded('sender')), '_links' => [ 'self' => route('v1:conversations:show', $this->id), ], ]; } } ``` Managing Pagination Payloads Standard Laravel pagination includes a lot of metadata that API consumers might not need, such as raw CSS class names for UI buttons. Using `simplePaginate()` reduces the payload size significantly by only providing the current page and indicators for more data, which is faster to calculate and transmit. Section 3: Advanced Response Objects and 'Responsible' Interfaces A common mistake is returning raw arrays from controllers. For a truly performant and discoverable API, implement the `Responsible` interface. This allows you to create dedicated response classes that handle their own logic, status codes, and headers. Creating a Collection Response Instead of the dreaded `data.data.data` nesting, a custom `CollectionResponse` class allows you to key your data meaningfully (e.g., `conversations` or `messages`) and include consistent metadata. ```php namespace App\Http\Responses\V1; use Illuminate\Contracts\Support\Responsable; use Illuminate\Http\JsonResponse; class CollectionResponse implements Responsable { public function __construct( private readonly mixed $data, private readonly string $key, private readonly int $status = 200 ) {} public function toResponse($request): JsonResponse { return new JsonResponse([ 'status' => $this->status, $this->key => $this->data, 'meta' => [ 'count' => count($this->data), ] ], $this->status); } } ``` Section 4: Proactive Caching and Cache Busting Caching is the most effective way to improve API read performance. However, "floating" cache keys (hardcoded strings scattered throughout the app) lead to stale data and debugging nightmares. Use PHP Enums to manage your cache keys and durations. The Forever Cache Pattern Rather than setting a Time-To-Live (TTL) of one hour and hoping for the best, cache your data forever and use Eloquent Observers to bust the cache immediately when data changes. This ensures the cache is always fresh and eliminates the "empty cache" performance hit that happens every hour. ```php namespace App\Observers; use App\Models\Conversation; use Illuminate\Support\Facades\Cache; use App\Enums\CacheKeys; class ConversationObserver { public function created(Conversation $conversation): void { Cache::forget(CacheKeys::conversations_for_user($conversation->sender_id)); } } ``` Warming the Cache You can further enhance performance by creating a scheduled command to "warm" the cache for your most active users. This ensures that even after a deployment or a cache flush, the first request a user makes is still served from the cache. Section 5: Asynchronous Write Operations with Jobs and Payloads Synchronous writes are a bottleneck. If a user creates a new conversation, the API should not wait for the database transaction and indexing to finish before responding. Instead, validate the request, dispatch a Laravel Job, and return a `202 Accepted` status code immediately. Implementing Data Payloads To pass data to background jobs safely, use Data Transfer Objects (DTOs) or simple Read-Only classes. This keeps your data immutable and structured. ```php namespace App\Jobs\V1; use App\Payloads\NewConversationPayload; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Support\Facades\DB; class CreateNewConversation implements ShouldQueue { use Queueable; public function __construct(private readonly NewConversationPayload $payload) {} public function handle(): void { DB::transaction(fn() => Conversation::create($this->payload->toArray()), 3); } } ``` By returning an immediate response, your API feels instantaneous to the user, even if the database work takes a few extra milliseconds in the background. Syntax Notes - **Read-only Classes**: Use `readonly class` to ensure data integrity for DTOs and Response objects. It prevents accidental state mutation during a request lifecycle. - **Attributes**: Features like `[ObservedBy(ConversationObserver::class)]` and `[AsCommand('warm:conversations')]` clean up service providers by keeping registration logic directly on the classes they affect. - **Named Routes**: Always name your API routes with a versioned prefix (e.g., `v1:conversations:index`). This makes generating links within API Resources much more resilient to URL changes. Practical Examples - **Real-time Chat**: Using the asynchronous write pattern allows a chat app to send a message and update the local UI instantly while the server processes the message in the background. - **Analytics Ingress**: At companies like Treblle, handling billions of requests monthly requires pushing every bit of processing to background workers and using Octane to keep the application in memory, reducing boot times. - **Public Data Feeds**: For APIs providing stock prices or weather data, the "Forever Cache + Observer" pattern ensures zero-latency reads with instant updates as soon as the source data changes. Tips & Gotchas - **The 'Data' Wrapper**: If your frontend team complains about `response.data.data`, check your `AppServiceProvider`. You can call `JsonResource::withoutWrapping()` to flatten your responses. - **Database Transactions**: When writing in background jobs, always include a retry count (e.g., `DB::transaction(..., 3)`). This prevents failures due to temporary deadlocks on highly active databases. - **Standardization vs. Consistency**: Don't lose sleep over following JSON:API or HAL standards perfectly. It is far better to be internally consistent across your own endpoints than to follow a standard poorly. - **Testing**: Never ship a performance optimization without a test. Use Laravel's fluent testing helpers to verify that your cache is actually being hit and that your background jobs are dispatched with the correct data.
Jul 31, 2024Overview Fetching the same data multiple times within a single request is a common performance bottleneck. Whether you are retrieving a user profile for the header, a sidebar, and a footer, or fetching a list of featured items, redundant database queries waste resources. The `once` helper in Laravel provides an elegant solution for in-memory memoization. It ensures that a given callback is executed exactly once during the lifecycle of a request, returning the cached result for all subsequent calls. Prerequisites To follow this tutorial, you should have a baseline understanding of PHP and the Laravel framework. Familiarity with anonymous functions (closures) and static methods will help you grasp the implementation details. You will need Laravel 11 or higher to use this as a built-in feature. Key Libraries & Tools * **Laravel Framework**: The primary PHP framework providing the helper. * **Spatie once**: The original package that inspired this native implementation. * **Tinkerwell**: An excellent code runner for testing Laravel snippets in real-time. Code Walkthrough Suppose we have a `Podcast` model with a static method that fetches featured shows via an action class. Without optimization, every call triggers a new query. ```python // Before: Every call hits the database public static function featured() { return (new GetFeaturedPodcastsAction)->execute(); } ``` To optimize this, wrap the logic in the `once` helper. The helper accepts a `callable` and stores the result based on the calling context. ```python // After: Optimized with the once helper public static function featured() { return once(fn () => (new GetFeaturedPodcastsAction)->execute()); } ``` When the application executes this code three times in one request, Laravel runs the inner logic on the first attempt, caches the return value, and serves that cached value for calls two and three. This eliminates redundant database overhead instantly. Syntax Notes The `once` helper uses the object instance or class and the method name to create a unique cache key. It relies on a closure (`fn () => ...`) to encapsulate the logic you want to memoize. This pattern is much cleaner than manually checking if a property is null before assigning a value. Tips & Gotchas Remember that `once` only persists for the duration of the current HTTP request. If you need to store data across multiple requests or for hours at a time, you must use Laravel's standard `Cache` facade. Additionally, be mindful of state; if the underlying data changes mid-request, `once` will still return the original result until the request ends.
May 6, 2024Overview of the Data Definition Problem Modern web applications often suffer from a "multiple definition" problem. When building a feature, you typically define the same set of attributes in a Laravel form request for validation, again in an API resource for output, and a third time as a TypeScript interface for your frontend. This duplication isn't just tedious; it's a breeding ground for bugs. If you add a `middle_name` field to your database but forget to update the API resource, your frontend breaks. Laravel-Data, a package by Spatie, solves this by providing a single source of truth: the **Data Object**. This object handles validation, transformation, and type generation in one elegant class. Prerequisites and Toolkit To follow this guide, you should be comfortable with PHP 8.x and the Laravel framework. You should understand the basics of Inertia.js if you plan on using the frontend features, and have a working knowledge of TypeScript for the automated type generation components. This tutorial assumes you have a Laravel project where you're tired of writing repetitive boilerplate for requests and resources. Key Libraries & Tools * Laravel-Data: The core package that creates powerful data objects to replace form requests and API resources. * TypeScript Transformer: A built-in feature of the package that scans your PHP classes and generates matching TypeScript definitions. * Inertia.js: Frequently used alongside this package to bridge the gap between backend data and frontend React or Vue components. Code Walkthrough: Implementing a Data Object Let's replace the standard Laravel ceremony with a single Laravel-Data object. Instead of creating a `StoreContactRequest` and a `ContactResource`, we create a `ContactData` class. ```php namespace App\Data; use Spatie\LaravelData\Data; use Spatie\LaravelData\Attributes\Validation\Email; class ContactData extends Data { public function __construct( public string $name, #[Email] public string $email, public ?string $address, ) {} } ``` By extending the `Data` class, this object now performs multiple roles. When used in a controller, it automatically validates incoming request data based on the property types. For example, the `string $name` property implies a `required|string` validation rule. If you want more specific constraints, use attributes like `#[Email]`. In your controller, you can swap out the standard request and resource calls: ```php public function update(ContactData $contactData, Contact $contact) { $contact->update($contactData->toArray()); return back(); } public function show(Contact $contact) { return ContactData::from($contact); } ``` The `from()` method is a "smart" factory. It accepts a model, an array, or a request and maps the attributes automatically. This eliminates the manual mapping usually found in API Resources. Automated TypeScript Integration One of the most powerful features of Laravel-Data is the ability to keep your frontend types in sync. By running a simple Artisan command, the package scans your data objects and generates a TypeScript file. ```bash php artisan typescript:transform ``` This generates interfaces that match your PHP logic exactly. If a property is nullable in PHP (`?string`), it becomes optional or nullable in TypeScript. This bridge ensures that your React components have full auto-completion and type safety, preventing "undefined" errors at runtime. Advanced Features: Nesting and Route Actions Real-world data is rarely flat. Laravel-Data supports nested data objects seamlessly. If a `Project` has many `Guests`, you can define a `ProjectData` class that contains a collection of `GuestData` objects. The TypeScript transformer will respect this hierarchy, generating nested interfaces. In the upcoming Version 4, the package introduces a **Route Action Helper**. This tool scans your Laravel routes and generates TypeScript helpers. This allows you to call routes in your frontend using the controller name and method with full auto-completion for route parameters. You no longer need to hardcode URLs or guess which parameters a specific endpoint requires. Tips & Gotchas While Laravel-Data is powerful, don't feel obligated to use it for every single interaction. Standard Laravel form requests are still excellent for simple validation logic in smaller projects. Use Laravel-Data when you find yourself repeating the same attribute list across three or more files. **Best Practice:** Always use the `from()` method rather than manual instantiation. It handles the heavy lifting of converting models and multi-dimensional arrays into the correct object format. If you need to hide sensitive data like addresses for certain users, use the `except()` or `only()` methods on the data object to filter the output dynamically.
Jul 27, 2023