Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Data Laravel Package

spatie/laravel-data

Create rich, typed data objects for Laravel that replace form requests and API transformers. Automatically map from requests, validate with inferred rules, transform to resources (with lazy/partial fields), and generate TypeScript definitions from the same source.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require spatie/laravel-data
    

    Publish config (if needed):

    php artisan vendor:publish --provider="Spatie\LaravelData\LaravelDataServiceProvider"
    
  2. Define a Data Object Create a class extending Spatie\LaravelData\Data:

    use Spatie\LaravelData\Data;
    
    class UserData extends Data
    {
        public function __construct(
            public string $name,
            public int $age,
        ) {}
    }
    
  3. First Use Case: Create from Request

    $data = UserData::from(request()->all());
    // Validates and casts automatically
    
  4. First Use Case: Transform to JSON

    $json = $data->toJson();
    // Returns JSON with typed properties
    

Where to Look First


Implementation Patterns

Core Workflows

1. Data Transfer Objects (DTOs)

Replace form requests/API transformers with Data objects:

// Instead of:
class CreateUserRequest extends FormRequest { ... }

// Use:
class UserData extends Data { ... }
$validated = UserData::from(request()->all());

2. Lazy Properties

Load nested data on-demand:

class UserData extends Data
{
    public function __construct(
        public Lazy|PostData $latestPost,
    ) {}
}

// Access only when needed:
$post = $userData->latestPost; // Loads lazily

3. Validation

Automatic validation via type hints + custom rules:

class UserData extends Data
{
    public function __construct(
        #[Required]
        public string $email,
        #[Email]
        public string $name,
    ) {}

    public function rules(): array {
        return [
            'email' => 'unique:users',
        ];
    }
}

4. TypeScript Generation

Generate frontend types:

php artisan laravel-data:typescript

Outputs UserData.d.ts with inferred types.


Integration Tips

With Eloquent Models

class User extends Model
{
    use \Spatie\LaravelData\Attributes\WithData;

    protected $dataClass = UserData::class;
}

// Usage:
$user = User::find(1);
$data = $user->getData(); // Returns UserData

With API Resources

Replace JsonResource:

class UserResource extends JsonResource
{
    public function toArray($request): array
    {
        return UserData::from($this->resource)->toArray();
    }
}

With Livewire/Alpine

Bind data objects directly to frontend:

// Livewire component
public UserData $userData;

// Alpine.js
const user = @this.userData.toJson();

Custom Casts/Transformers

// Cast: string → Carbon
class DateCast implements Cast
{
    public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): Carbon
    {
        return Carbon::parse($value);
    }
}

// Usage:
class EventData extends Data
{
    public function __construct(
        #[WithCast(DateCast::class)]
        public Carbon $date,
    ) {}
}

Gotchas and Tips

Pitfalls

  1. Property Name Mismatches

    • Issue: Input keys don’t match property names (e.g., snake_case vs camelCase).
    • Fix: Use #[MapName(SnakeCaseMapper::class)] or #[MapInputName]/#[MapOutputName].
    • Example:
      class UserData extends Data
      {
          #[MapName(SnakeCaseMapper::class)]
          public string $firstName; // Maps to `first_name` in input/output
      }
      
  2. Lazy Loading Overhead

    • Issue: N+1 queries with lazy properties.
    • Fix: Eager-load related data or use ->load():
      $userData = UserData::from(User::with('posts')->find(1));
      
  3. Validation Rules Not Triggering

    • Issue: Custom rules in rules() method ignored.
    • Fix: Ensure ValidContext is passed or use validate():
      UserData::validate($data); // Explicit validation
      
  4. TypeScript Generation Conflicts

    • Issue: Duplicate types or incorrect mappings.
    • Fix: Exclude problematic classes in config/laravel-data.php:
      'typescript' => [
          'exclude' => [
              'App/Data/Internal/*',
          ],
      ],
      

Debugging Tips

  1. Inspect Data Structure Use ->toArray() or ->toJson() to debug:

    dd($data->toArray());
    
  2. Validation Errors Check raw errors:

    try {
        $data = UserData::from($request->all());
    } catch (\Spatie\LaravelData\Exceptions\ValidationException $e) {
        dd($e->errors());
    }
    
  3. Property Mappers Verify mapper behavior:

    $data = UserData::from(['firstName' => 'John']);
    dd($data->first_name); // Check if mapped correctly
    

Extension Points

  1. Custom Property Mappers Extend Spatie\LaravelData\Mappers\Mapper:

    class CustomMapper implements Mapper
    {
        public function map(string $name): string
        {
            return strtoupper($name);
        }
    }
    
  2. Dynamic Data Classes Use #[DataClass] attribute for dynamic generation:

    #[DataClass]
    class DynamicUserData extends Data
    {
        public function __construct(
            public string $name,
        ) {}
    }
    
  3. Package Integration For testing, include the service provider:

    // In TestCase
    protected function getPackageProviders($app)
    {
        return [
            \Spatie\LaravelData\LaravelDataServiceProvider::class,
        ];
    }
    

Performance Quirks

  1. Avoid Over-Casting

    • Casts/transformers add overhead. Use only when necessary:
      // Bad: Casts every time
      #[WithCast(DateCast::class)]
      public Carbon $date;
      
      // Good: Cast only during creation
      public static function fromArray(array $data): static
      {
          return new self(
              date: Carbon::parse($data['date']),
          );
      }
      
  2. Lazy Properties

    • Initialize lazily to defer expensive operations:
      public function __construct(
          public Lazy|function() => array $expensiveData,
      ) {}
      
  3. TypeScript Generation

    • Exclude large classes to speed up generation:
      'typescript' => [
          'exclude' => [
              'App/Data/BigDataClass',
          ],
      ],
      

Config Tricks

  1. Disable Validation Skip validation during creation:

    UserData::from($data, validate: false);
    
  2. Custom Validation Messages Override in rules():

    public function rules(): array
    {
        return [
            'email' => ['required', 'email', 'unique:users'],
        ];
    }
    
    public function messages(): array
    {
        return [
            'email.unique' => 'This email is already taken!',
        ];
    }
    
  3. Global Defaults Set defaults in config/laravel-data.php:

    'default' => [
        'validate' => true,
        'map_property_names' => true,
    ],
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai