Awesome
<p align="center"> <br> <a href="https://wayof.dev" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/wayofdev/.github/master/assets/logo.gh-dark-mode-only.png"> <img width="400" src="https://raw.githubusercontent.com/wayofdev/.github/master/assets/logo.gh-light-mode-only.png" alt="WayOfDev Logo"> </picture> </a> <br> </p> <p align="center"> <strong>Build</strong><br> <a href="https://github.com/wayofdev/laravel-symfony-serializer/actions" target="_blank"><img alt="Build Status" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fwayofdev%2Flaravel-symfony-serializer%2Fbadge&style=flat-square&label=github%20actions"/></a> </p> <p align="center"> <strong>Project</strong><br> <a href="https://packagist.org/packages/wayofdev/laravel-symfony-serializer" target="_blank"><img src="https://img.shields.io/packagist/dt/wayofdev/laravel-symfony-serializer?&style=flat-square" alt="Total Downloads"></a> <a href="https://packagist.org/packages/wayofdev/laravel-symfony-serializer" target="_blank"><img src="https://img.shields.io/packagist/v/wayofdev/laravel-symfony-serializer?&style=flat-square" alt="Latest Stable Version"></a> <a href="https://packagist.org/packages/wayofdev/laravel-symfony-serializer" target="_blank"><img alt="Commits since latest release" src="https://img.shields.io/github/commits-since/wayofdev/laravel-symfony-serializer/latest?style=flat-square"></a> <a href="https://packagist.org/packages/wayofdev/laravel-symfony-serializer" target="_blank"><img alt="PHP Version Require" src="https://poser.pugx.org/wayofdev/laravel-symfony-serializer/require/php?style=flat-square"></a> </p> <p align="center"> <strong>Quality</strong><br> <a href="https://app.codecov.io/gh/wayofdev/laravel-symfony-serializer" target="_blank"><img alt="Codecov" src="https://img.shields.io/codecov/c/github/wayofdev/laravel-symfony-serializer?style=flat-square&logo=codecov"></a> <a href="https://dashboard.stryker-mutator.io/reports/github.com/wayofdev/laravel-symfony-serializer/master" target="_blank"><img alt="Mutation testing badge" src="https://img.shields.io/endpoint?style=flat-square&label=mutation%20score&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fwayofdev%2Flaravel-symfony-serializer%2Fmaster"></a> <a href=""><img src="https://img.shields.io/badge/phpstan%20level-8%20of%209-brightgreen?style=flat-square" alt="PHP Stan Level 6 of 9"></a> </p> <p align="center"> <strong>Community</strong><br> <a href="https://discord.gg/CE3TcCC5vr" target="_blank"><img alt="Discord" src="https://img.shields.io/discord/1228506758562058391?style=flat-square&logo=discord&labelColor=7289d9&logoColor=white&color=39456d"></a> <a href="https://x.com/intent/follow?screen_name=wayofdev" target="_blank"><img alt="Follow on Twitter (X)" src="https://img.shields.io/badge/-Follow-black?style=flat-square&logo=X"></a> </p> <br>Laravel Symfony Serializer
This package integrates the Symfony Serializer component into Laravel, providing a powerful tool for serializing and deserializing objects into various formats such as JSON, XML, CSV, and YAML.
Detailed documentation on the Symfony Serializer can be found on their official page.
<br>ποΈ Table of Contents
- Purpose
- Installation
- Configuration
- Usage
- Security Policy
- Want to Contribute?
- Contributors
- Social Links
- License
- Credits and Useful Resources
π€ Purpose
This package brings the power of the Symfony Serializer component to Laravel. While Laravel does not have a built-in serializer and typically relies on array or JSON transformations, this package provides more advanced serialization capabilities. These include object normalization, handling of circular references, property grouping, and format-specific encoders.
If you are building a REST API, working with queues, or have complex serialization needs, this package will be especially useful. It allows you to use objects as payloads instead of simple arrays and supports various formats such as JSON, XML, CSV, and YAML. This documentation will guide you through the installation process and provide examples of how to use the package to serialize and deserialize your objects.
<br>π If you find this repository useful, please consider giving it a βοΈ. Thank you!
<br>πΏ Installation
Require the package as a dependency:
composer require wayofdev/laravel-symfony-serializer
You can publish the config file with:
$ php artisan vendor:publish \
--provider="WayOfDev\Serializer\Bridge\Laravel\Providers\SerializerServiceProvider" \
--tag="config"
<br>
π§ Configuration
The package configuration file allows you to customize various aspects of the serialization process.
Below is the default configuration provided by the package:
<?php
declare(strict_types=1);
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
use WayOfDev\Serializer\Contracts\EncoderRegistrationStrategy;
use WayOfDev\Serializer\Contracts\NormalizerRegistrationStrategy;
use WayOfDev\Serializer\DefaultEncoderRegistrationStrategy;
use WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy;
/**
* @return array{
* default: string,
* debug: bool,
* normalizerRegistrationStrategy: class-string<NormalizerRegistrationStrategy>,
* encoderRegistrationStrategy: class-string<EncoderRegistrationStrategy>,
* metadataLoader: class-string<LoaderInterface>|null,
* }
*/
return [
'default' => env('SERIALIZER_DEFAULT_FORMAT', 'symfony-json'),
'debug' => env('SERIALIZER_DEBUG_MODE', env('APP_DEBUG', false)),
'normalizerRegistrationStrategy' => DefaultNormalizerRegistrationStrategy::class,
'encoderRegistrationStrategy' => DefaultEncoderRegistrationStrategy::class,
'metadataLoader' => null,
];
β Configuration Options
default
: Specifies the default serializer format. This can be overridden by setting theSERIALIZER_DEFAULT_FORMAT
environment variable. The default issymfony-json
.debug
: Enables debug mode forProblemNormalizer
. This can be set using theSERIALIZER_DEBUG_MODE
environment variable. It defaults to theAPP_DEBUG
value.normalizerRegistrationStrategy
: Specifies the strategy class for registering normalizers. The default strategy isWayOfDev\Serializer\DefaultNormalizerRegistrationStrategy
.encoderRegistrationStrategy
: Specifies the strategy class for registering encoders. The default strategy isWayOfDev\Serializer\DefaultEncoderRegistrationStrategy
.metadataLoader
: Allows registration of a custom metadata loader. By default,Symfony\Component\Serializer\Mapping\Loader\AttributeLoader
is used.
β Custom Strategies
Due to Laravel's caching limitations, where configs cannot instantiate objects, this package uses strategies to register normalizers and encoders.
You can create custom normalizer or encoder registration strategies by implementing the respective interfaces.
Normalizer Registration Strategy
To create a custom normalizer registration strategy:
-
Implement the
NormalizerRegistrationStrategy
interface:<?php declare(strict_types=1); namespace Infrastructure\Serializer; use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface; use Symfony\Component\Serializer\Normalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use WayOfDev\Serializer\Contracts\NormalizerRegistrationStrategy; // ... final readonly class CustomNormalizerRegistrationStrategy implements NormalizerRegistrationStrategy { public function __construct( private LoaderInterface $loader, private bool $debugMode = false, ) { } /** * @return iterable<array{normalizer: NormalizerInterface|DenormalizerInterface, priority: int<0, max>}> */ public function normalizers(): iterable { // ... } }
-
Change
serializer.php
config to use your custom strategy:'normalizerRegistrationStrategy' => CustomNormalizerRegistrationStrategy::class,
Encoder Registration Strategy
To create a custom encoder registration strategy:
-
Implement the
EncoderRegistrationStrategy
interface:<?php declare(strict_types=1); namespace Infrastructure\Serializer; use Symfony\Component\Serializer\Encoder; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Yaml\Dumper; use function class_exists; final class CustomEncoderRegistrationStrategy implements Contracts\EncoderRegistrationStrategy { /** * @return iterable<array{encoder: EncoderInterface|DecoderInterface}> */ public function encoders(): iterable { // Register your encoders here... yield ['encoder' => new Encoder\JsonEncoder()]; yield ['encoder' => new Encoder\CsvEncoder()]; yield ['encoder' => new Encoder\XmlEncoder()]; if (class_exists(Dumper::class)) { yield ['encoder' => new Encoder\YamlEncoder()]; } } }
-
Change
serializer.php
config to use your custom strategy:'encoderRegistrationStrategy' => CustomEncoderRegistrationStrategy::class,
π» Usage
The package provides a list of serializers that can be used to serialize and deserialize objects.
The default serializers available in this package are: symfony-json
, symfony-csv
, symfony-xml
, symfony-yaml
.
[!WARNING] The
yaml
encoder requires thesymfony/yaml
package and is disabled when the package is not installed. Install thesymfony/yaml
package, and the encoder will be automatically enabled.
β Components
SerializerManager
The SerializerManager
handles the different serializers available in this package. It can be used to serialize and deserialize objects.
ResponseFactory
The ResponseFactory
is used to create responses in Laravel controllers, making it easy to include serialized data in HTTP responses.
Facades
This package includes two Laravel Facades:
Manager
β To access the underlyingSerializerManager
Serializer
β To access the bound and configured original Symfony Serializer instance.
β Example DTO
We will use this example DTO for serialization purposes:
<?php
namespace Application\User;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
class UserDTO
{
#[Groups(['public'])]
#[SerializedName('id')]
private int $id;
#[Groups(['public'])]
#[SerializedName('name')]
private string $name;
#[Groups(['private', 'public'])]
#[SerializedName('emailAddress')]
private string $email;
public function __construct(int $id, string $name, string $email)
{
$this->id = $id;
$this->name = $name;
$this->email = $email;
}
public function id(): int
{
return $this->id;
}
public function name(): string
{
return $this->name;
}
public function email(): string
{
return $this->email;
}
}
β Using SerializerManager
in Service Classes
<?php
namespace Application\Services;
use WayOfDev\Serializer\Manager\SerializerManager;
use Application\User\UserDTO;
class ProductService
{
public function __construct(
private readonly SerializerManager $serializer,
) {
}
public function someMethod(): void
{
$serializer = $this->serializer->serializer('symfony-json');
$dto = new UserDTO(1, 'John Doe', 'john@example.com');
$serialized = $serializer->serialize(
payload: $dto,
context: ['groups' => ['private']]
);
}
}
β Using ResponseFactory
in Laravel Controllers
Here's an example of how you can use the ResponseFactory
in a Laravel Controller:
Example Controller:
<?php
namespace Bridge\Laravel\Public\Product\Controllers;
use Application\User\UserDTO;
use Illuminate\Http\Request;
use WayOfDev\Serializer\Bridge\Laravel\Http\HttpCode;
use WayOfDev\Serializer\Bridge\Laravel\Http\ResponseFactory;
class UserController extends Controller
{
public function __construct(private ResponseFactory $response)
{
}
public function index()
{
$dto = new UserDTO(1, 'John Doe', 'john@example.com');
$this->response->withContext(['groups' => ['private']]);
$this->response->withStatusCode(HttpCode::HTTP_OK);
return $this->response->create($dto);
}
}
<br>
β Using in Laravel Queues
To switch from Laravel's default serialization to this implementation in queues, you can override the __serialize
and __unserialize
methods in your queue jobs. Hereβs an example:
<?php
declare(strict_types=1);
namespace Bridge\Laravel\Public\Product\Jobs;
use Domain\Product\Models\Product;
use Domain\Product\ProductProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use WayOfDev\Serializer\Bridge\Laravel\Facades\Manager;
/**
* This Job class shows how Symfony Serializer can be used with Laravel Queues.
*/
class ProcessProductJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public Product $product;
public function __construct(Product $product)
{
$this->product = $product;
}
public function handle(ProductProcessor $processor): void
{
$processor->process($this->product);
}
public function __serialize(): array
{
return [
'product' => Manager::serialize($this->product),
];
}
public function __unserialize(array $values): void
{
$this->product = Manager::deserialize($values['product'], Product::class);
}
}
<br>
π Security Policy
This project has a security policy.
<br>π Want to Contribute?
Thank you for considering contributing to the wayofdev community! We welcome all kinds of contributions. If you want to:
- π€ Suggest a feature
- π Report an issue
- π Improve documentation
- π¨βπ» Contribute to the code
You are more than welcome. Before contributing, please check our contribution guidelines.
<br>π«‘ Contributors
<p align="left"> <a href="https://github.com/wayofdev/laravel-symfony-serializer/graphs/contributors"> <img align="left" src="https://img.shields.io/github/contributors-anon/wayofdev/laravel-symfony-serializer?style=for-the-badge" alt="Contributors Badge"/> </a> <br> <br> </p>π Social Links
- Twitter: Follow our organization @wayofdev and the author @wlotyp.
- Discord: Join our community on Discord.
π License
<br>𧱠Credits and Useful Resources
This repository is inspired by the following projects: