ccharz / laravel-dto-lite
Simple Laravel Data Transfer Object
Requires
- php: ^8.3
- laravel/framework: ^12.0||^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.20
- orchestra/testbench: ^10.0||^11.0
- rector/rector: ^2.0
Suggests
- spatie/laravel-typescript-transformer: Install to easily convert DTOs to typescript
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-13 15:56:48 UTC
README
This is a basic implementation of the data transfer object (DTO) concept. The idea is to describe input and output of data in one simple basic PHP class file. It is meant to replace FormRequests and Resources and can also be used to automatically generate TypeScript definitions.
This package is similar to the Laravel Data Package from Spatie. The main difference is that it contains no reflection class magic and only provides the basic functionalities.
Installation
composer require ccharz/laravel-dto-lite
Usage
Example DTO
enum ContactType: string { case PERSON = 'person'; case COMPANY = 'company'; }
use Ccharz\DtoLite\Concerns\IsDataTransferObject; use Ccharz\DtoLite\Contracts\DataTransferObject; readonly class ContactData implements DataTransferObject { use IsDataTransferObject; public function __construct( public string $name, public string $email, public ContactType $type, ) {} }
Casts
Casts for attributes can be defined similar to eloquent casts. It is also possible to define casts to an array
use Ccharz\DtoLite\Concerns\IsDataTransferObject; use Ccharz\DtoLite\Contracts\DataTransferObject; enum ContactType: string { case PERSON = 'person'; case COMPANY = 'company'; } readonly class AddressData implements DataTransferObject { use IsDataTransferObject; public function __construct( public ?string $country = null, public ?string $zip = null, public ?string $location = null, public ?string $street = null, public ?string $streetnumber = null, public ?string $stair = null, public ?string $top = null, ) { } } readonly class ContactData implements DataTransferObject { use IsDataTransferObject; public function __construct( /** @var AddressData[] $addresses */ public array $addresses, public DateTime $birthday, public ContactType $type, ) {} public static function casts(): array { return [ 'addresses' => AddressData::class . '[]', 'birthday' => 'datetime', 'type' => ContactType::class, ]; } }
Validation
If a rules method exists, validation is automatically performed when creating a DTO from a request manually or by automatic dependency injection.
use Illuminate\Http\Request; public static function rules(?Request $request = null): array { return ['prename' => 'min:2']; }
You can also inject the rules from casts in your rules:
use Illuminate\Http\Request; public static function rules(?Request $request = null): array { return [ ...static::castRules(), ]; }
You can customize the validation attributes and messages with the messages() and attributes() functions.
To perform additional validation you can use the afterValidation function which passes the data to the after function of the validator.
use Illuminate\Http\Request; use Illuminate\Validation\Validator; /** * @return array<callable|string> */ public static function afterValidation(?Request $request = null): array { return [ function (Validator $validator) { if ($validator->errors()->isEmpty() && ! static::isConsistent($validator->validated())) { $validator->errors()->add('field', 'Something is wrong with this field!'); } } ]; }
Automatic Injection
With the help of Laravel's dependency injection, the DTO can be used in a controller method function and is automatically filled with the validated input data from the request.
public function store(ContactData $contactData): RedirectResponse { Contact::create([ 'name' => $contactData->name, 'email' => $contactData->email, ]); return redirect()->back(); }
Eloquent Castable
DataTransferObjects can be used as a cast in eloquent models
class Contact extends Model { protected $casts = [ 'address' => AddressData::class, ]; }
If the column is nullable, you have to append the cast parameter "nullable":
class Contact extends Model { protected $casts = [ 'address' => AddressData::class . ':nullable', ]; }
Casting arrays of Data Transfer Objects:
use Ccharz\DtoLite\Casts\AsDataTransferObjectCollection; /** * Get the attributes that should be cast. * * @return array<string, string> */ protected function casts(): array { return [ 'addresses' => AsDataTransferObjectCollection::of(AddressData::class), ]; }
Response
Data Transfer Objects are automatically converted to a response if returned from a controller
class ContactController extends Controller { public function show(Contact $contact): ContactData { return ContactData::make($contact); } }
You can also return a resource collection of data transfer objects
class ContactController extends Controller { public function index() { return ContactData::collection(Contact::paginate()); } }
Map to DTO Array
An array can automatically be mapped to an array of the DTO:
$addresses = [ [ 'country' => 'AT', 'zip' => '8010', ], [ 'country' => 'AT', 'zip' => '8010', ] ]; AddressData::mapToDtoArray($addresses);
TypeScript Definitions
You can use spatie/laravel-typescript-transformer to automatically generate TypeScript definitions for your Data Transfer Objects and Enums.
namespace App\Data; use Ccharz\DtoLite\Concerns\IsDataTransferObject; use Ccharz\DtoLite\Contracts\DataTransferObject; use Spatie\TypeScriptTransformer\Attributes\TypeScript; #[TypeScript] readonly class AddressData implements DataTransferObject { use IsDataTransferObject; public function __construct( public readonly ?string $country = null, public readonly ?string $zip = null, public readonly ?string $location = null, public readonly ?string $street = null, public readonly ?string $streetnumber = null, public readonly ?string $stair = null, public readonly ?string $top = null, ) { } }
generates to the following TypeScript definition:
declare namespace App.Data { export type AddressData = { country: string | null; zip: string | null; location: string | null; street: string | null; streetnumber: string | null; stair: string | null; top: string | null; }; }
Artisan Command
To create a new data transfer object, use the make:dto Artisan command:
php artisan make:dto Address