A zero‑boilerplate, attribute‑driven serializer / DTO helper for Laravel 10 & 11
Built for PHP ≥ 8.2 — lighter than 20 KB.
- Why another serializer?
- Features
- Requirements
- Installation
- Quick start
- Advanced usage
- Performance & Limitations
- Contributing
- License
flolefebvre/serializer focuses on simplicity and strictness:
- DTOs are just classes extending
Serializable. - Validation occurs before the object exists — no invalid state can persist.
- No generated code or heavy reflection caches; your build stays ultra‑fast.
| ✅ Constructor‑promoted DTOs only | ✅ Automatic validation via #[Rule] |
| ✅ Pure attribute configuration | ✅ Typed arrays (#[ArrayType]) |
| ✅ Laravel DI & Request auto‑binding | ✅ Custom type‑casts (TypeCast interface) |
| ✅ Eloquent JSON cast (single / list) | ✅ JsonResponse ready to return |
- PHP 8.2+
- Laravel 10 / 11
ext-json
composer require flolefebvre/serializerCreate a DTO:
<?php
namespace App\DTO;
use Flolefebvre\Serializer\Serializable;
use Flolefebvre\Serializer\Attributes\Rule;
class PostData extends Serializable
{
public function __construct(
public string $title,
#[Rule('nullable|string|max:280')]
public ?string $excerpt,
#[Rule('required|url')]
public string $url,
) {}
}Bind it in a route:
use App\DTO\PostData;
use Illuminate\Support\Facades\Route;
Route::post('/posts', function (PostData $data) {
// $data is a fully validated DTO.
// ...
return $data; // => JsonResponse 201
});Try it:
curl -X POST http://localhost/api/posts -H "Content-Type: application/json" -d '{"title":"Hello","excerpt":null,"url":"https://example.com"}'Response:
{
"title": "Hello",
"excerpt": null,
"url": "https://example.com",
"_type": "App\\DTO\\PostData"
}use Ramsey\Uuid\UuidInterface;
use Flolefebvre\Serializer\Attributes\CastTypeWith;
use Flolefebvre\Serializer\Casts\TypeCast;
use Ramsey\Uuid\Uuid;
class UuidCast implements TypeCast
{
// How the value will appear once serialized
public string $serializedType = 'uuid';
public function serialize(mixed $value): mixed
{
return (string) $value;
}
public function unserialize(mixed $value): mixed
{
return Uuid::fromString($value);
}
}
class OrderData extends Serializable
{
public function __construct(
#[CastTypeWith(UuidCast::class)]
public UuidInterface $id,
) {}
}use Flolefebvre\Serializer\Attributes\ArrayType;
class BlogData extends Serializable
{
public function __construct(
#[ArrayType(CommentData::class)]
public array $comments,
) {}
}| Topic | Notes |
|---|---|
| Reflection | Metadata is rebuilt on every call → enable OPcache and/or add a static cache for high‑traffic endpoints. |
| Union/Intersection Types | Not yet supported — contributions welcome. |
| Polymorphism | The _type field is required for subclasses during deserialization. |
- Fork & clone
composer install && composer test- Create your feature branch (
git checkout -b feature/my-feature) - Push and open a PR
- Ensure tests pass and follow PSR‑12.
Released under the MIT License — see LICENSE.