Skip to content

Migration from Spatie Laravel Data

Complete guide to migrating from Spatie Laravel Data to SimpleDTO.

SimpleDTO provides a smooth migration path from Spatie Laravel Data:

  • Similar API - Familiar methods and patterns
  • More Features - 18 conditional attributes for fine-grained control
  • Framework Independent - Works with Laravel, Symfony, and plain PHP
  • Backward Compatible - Easy to migrate incrementally
  • Validation Caching - Built-in caching for better performance
FeatureSpatie DataSimpleDTO
Framework SupportLaravel onlyLaravel, Symfony, PHP
TypeScript Generation✅ Yes✅ Yes
Lazy Properties✅ Yes✅ Yes
Computed Properties✅ Yes✅ Yes
Collections✅ Yes✅ Yes
Validation Caching❌ No✅ Yes
Conditional Attributes2 attributes18 attributes

The easiest way to migrate is using the built-in Artisan command:

Terminal window
# Migrate all Spatie Data classes in app/Data
php artisan dto:migrate-spatie
# Migrate specific directory
php artisan dto:migrate-spatie --path=app/Data/Api
# Dry run (preview changes without modifying files)
php artisan dto:migrate-spatie --dry-run
# Backup files before migration
php artisan dto:migrate-spatie --backup

The command will:

  1. Find all Spatie Data classes
  2. Replace base class (DataSimpleDTO)
  3. Update namespace imports
  4. Add readonly to properties
  5. Update attribute namespaces
  6. Create backup files (if --backup flag is used)

If you prefer manual migration:

Terminal window
composer require event4u/data-helpers

Before (Spatie):

use Spatie\LaravelData\Data;
class UserData extends Data
{
public function __construct(
public string $name,
public string $email,
) {}
}

After (SimpleDTO):

use event4u\DataHelpers\SimpleDTO\SimpleDTO;
class UserDTO extends SimpleDTO
{
public function __construct(
public readonly string $name,
public readonly string $email,
) {}
}

Before (Spatie):

use Spatie\LaravelData\Attributes\Validation\Required;
use Spatie\LaravelData\Attributes\Validation\Email;
class UserData extends Data
{
public function __construct(
#[Required]
public string $name,
#[Required, Email]
public string $email,
) {}
}

After (SimpleDTO):

use event4u\DataHelpers\SimpleDTO\Attributes\Required;
use event4u\DataHelpers\SimpleDTO\Attributes\Email;
class UserDTO extends SimpleDTO
{
public function __construct(
#[Required]
public readonly string $name,
#[Required, Email]
public readonly string $email,
) {}
}
Spatie DataSimpleDTONotes
Data::from()SimpleDTO::fromArray()Same functionality
Data::collect()DataCollection::make()Same functionality
Data::validateAndCreate()SimpleDTO::validateAndCreate()Same functionality
Spatie DataSimpleDTONotes
toArray()toArray()Same
toJson()toJson()Same
toXml()toXml()Same
Spatie DataSimpleDTONotes
#[Hidden]#[Hidden]Same
#[Computed]#[Computed]Same
#[Lazy]#[Lazy]Same
#[WithCast]#[Cast]Different name

Before (Spatie):

use Spatie\LaravelData\Data;
class UserData extends Data
{
public function __construct(
public int $id,
public string $name,
public string $email,
) {}
}
$user = UserData::from([
'id' => 1,
'name' => 'John Doe',
'email' => 'john@example.com',
]);

After (SimpleDTO):

use event4u\DataHelpers\SimpleDTO\SimpleDTO;
class UserDTO extends SimpleDTO
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $email,
) {}
}
$user = UserDTO::fromArray([
'id' => 1,
'name' => 'John Doe',
'email' => 'john@example.com',
]);

Before (Spatie):

use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\Validation\Required;
use Spatie\LaravelData\Attributes\Validation\Email;
class CreateUserData extends Data
{
public function __construct(
#[Required]
public string $name,
#[Required, Email]
public string $email,
) {}
}

After (SimpleDTO):

use event4u\DataHelpers\SimpleDTO\SimpleDTO;
use event4u\DataHelpers\SimpleDTO\Attributes\Required;
use event4u\DataHelpers\SimpleDTO\Attributes\Email;
class CreateUserDTO extends SimpleDTO
{
public function __construct(
#[Required]
public readonly string $name,
#[Required, Email]
public readonly string $email,
) {}
}

Before (Spatie):

use Spatie\LaravelData\DataCollection;
$users = UserData::collect($usersArray);

After (SimpleDTO):

use event4u\DataHelpers\SimpleDTO\DataCollection;
$users = DataCollection::make($usersArray, UserDTO::class);

SimpleDTO has 18 conditional attributes for fine-grained control:

class UserDTO extends SimpleDTO
{
public function __construct(
public readonly string $name,
// Authentication-based
#[WhenAuth]
public readonly ?string $email = null,
#[WhenGuest]
public readonly ?string $publicProfile = null,
// Permission-based
#[WhenCan('view-sensitive-data')]
public readonly ?string $ssn = null,
// Role-based
#[WhenRole('admin')]
public readonly ?array $adminPanel = null,
// Value-based
#[WhenValue('status', 'active')]
public readonly ?string $activeFeatures = null,
) {}
}
Terminal window
# Cache validation rules for better performance
php artisan dto:cache
// Works in Laravel
$dto = UserDTO::fromModel($user);
// Works in Symfony
$dto = UserDTO::fromEntity($user);
// Works in plain PHP
$dto = UserDTO::fromArray($data);
  • Review current Spatie Data usage
  • Identify custom casts and transformers
  • Document conditional logic
  • Backup codebase
  • Install SimpleDTO
  • Run dto:migrate-spatie command or migrate manually
  • Update method calls if needed
  • Test thoroughly
  • Remove Spatie Data package
  • Update tests
  • Generate TypeScript types
  • Cache validation rules

Solution: Add readonly keyword to all properties:

// Before
public string $name;
// After
public readonly string $name;

Solution: Use validateAndCreate() instead of fromArray():

// This validates
$dto = UserDTO::validateAndCreate($data);
// This doesn't validate
$dto = UserDTO::fromArray($data);

Solution: Use DataCollection::make() with class name:

// Before (Spatie)
$users = UserData::collect($data);
// After (SimpleDTO)
$users = DataCollection::make($data, UserDTO::class);