Installation:
composer require spiral/marshaller
Ensure your project uses PHP 8.1+.
Basic Serialization:
For a simple class like User with public properties, no annotations are needed:
use Spiral\Marshaller\Marshaller;
use Spiral\Marshaller\Mapper\AttributeMapperFactory;
use Spiral\Attributes\AttributeReader;
$marshaller = new Marshaller(new AttributeMapperFactory(new AttributeReader()));
$user = new User('John', 'Doe');
$array = $marshaller->marshal($user);
First Use Case: Convert an Eloquent model to an array for API responses:
$post = Post::find(1);
$data = $marshaller->marshal($post);
return response()->json($data);
Marshaller class: Core interface for marshal() and unmarshal() methods.#[Marshal] attribute: Key to customizing serialization behavior.Auto-Mapping Public Properties:
class User {
public function __construct(
public string $name,
public string $email
) {}
}
// Automatically maps to `['name' => 'John', 'email' => 'john@example.com']`.
Customizing with Attributes:
class User {
#[Marshal(name: 'full_name')]
private string $name;
}
// Outputs `['full_name' => 'John']` instead of `['name' => 'John']`.
Nested Objects:
class Address {
public function __construct(
public string $street,
public string $city
) {}
}
class User {
#[Marshal(of: Address::class)]
public Address $address;
}
// Recursively serializes the `Address` object.
Collections/Arrays:
class User {
#[MarshalArray(of: Post::class)]
public array $posts;
}
// Serializes each `Post` in the array.
Deserialization:
$data = ['name' => 'John', 'email' => 'john@example.com'];
$user = $marshaller->unmarshal($data, new User(...));
Laravel HTTP Layer:
Use unmarshal() to populate DTOs from request data:
public function store(Request $request) {
$dto = $marshaller->unmarshal($request->all(), new CreateUserDto());
// Validate/process $dto.
}
API Resources:
Replace toArray() with marshal() for consistency:
public function toArray($request) {
return $this->marshaller->marshal($this->resource);
}
Database Hydration: Unmarshal query results into collections:
$users = User::all()->map(fn ($user) => $marshaller->marshal($user));
Event Payloads: Standardize event data shapes:
event(new UserRegistered($marshaller->marshal($user)));
Custom Type Handlers:
Extend TypeInterface for unsupported types (e.g., custom UUIDs):
class UuidType implements TypeInterface {
public function marshal($value): array { ... }
public function unmarshal(array $data): UuidInterface { ... }
}
Decorators: Modify behavior globally via decorators:
$marshaller->decorate(new IgnoreNullDecorator());
Caching: Cache marshalled results for performance:
$cacheKey = 'user:'.$user->id;
$data = Cache::remember($cacheKey, now()->addHour(), fn () =>
$marshaller->marshal($user)
);
Private/Protected Properties:
#[Marshal], private/protected properties are ignored.AttributeMapperFactory with reflection.Circular References:
User ↔ Post bidirectional relationships).#[Marshal(ignore: true)] or implement a custom TypeInterface.Enum/Complex Types:
UuidInterface) require explicit type hints:
#[Marshal(of: Status::class, type: EnumType::class)]
private Status $status;
EnumType for enums and ObjectType for custom classes.Array Keys:
Performance:
AttributeReader instances or use AttributeMapperFactory with pre-compiled metadata.Inspect Metadata:
Use AttributeReader to debug annotations:
$reader = new AttributeReader();
$reflection = new ReflectionClass(User::class);
$attributes = $reader->getAttributes($reflection->getProperty('name'));
Validate Input Data: Always validate unmarshalled data:
try {
$user = $marshaller->unmarshal($data, new User(...));
} catch (MarshallingException $e) {
// Handle missing/invalid fields.
}
Test Edge Cases:
Custom Attributes:
Extend Marshal or create new attributes (e.g., #[Marshal(skip_if: fn ($val) => empty($val))]).
Type System:
Implement TypeInterface for unsupported types (e.g., Carbon, Money).
Decorators: Modify marshaling behavior globally (e.g., add timestamps, filter fields):
$marshaller->decorate(new AddTimestampsDecorator());
Integration with Laravel:
Bind Marshaller to the container:
$app->singleton(Marshaller::class, fn () => new Marshaller(
new AttributeMapperFactory(new AttributeReader())
));
AttributeReader:
Spiral\Attributes\AttributeReader (not Laravel’s Attribute facade) for consistency.Case Sensitivity:
Default Values:
Pair with Laravel Validation:
Use unmarshal() + ValidatesWhen for DTO validation:
$dto = $marshaller->unmarshal($request->all(), new CreateUserDto());
$this->validate($dto);
API Versioning: Use decorators to version API responses:
$marshaller->decorate(new V2ResponseDecorator());
Document Annotations: Add PHPDoc to classes to clarify expected input/output shapes:
/**
* @property string $name
* @property array{street: string, city: string} $address
*/
class User { ... }
How can I help you explore Laravel packages today?