From 1578cd914556c768c91cab59a94e67e553b66557 Mon Sep 17 00:00:00 2001 From: lotyp Date: Wed, 19 Jun 2024 15:30:23 +0300 Subject: [PATCH 1/2] docs: updating README.md --- README.md | 296 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 265 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 5bd5d447..caf186a1 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,33 @@ # Laravel Symfony Serializer -## πŸ“„ About - 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](https://symfony.com/doc/current/components/serializer.html). -### β†’ Purpose +
+ +## πŸ—‚οΈ Table of Contents + +- [Purpose](#-purpose) +- [Installation](#-installation) +- [Configuration](#-configuration) + - [Configuration Options](#-configuration-options) + - [Custom Strategies](#-custom-strategies) +- [Usage](#-usage) + - [Components](#-components) + - [Example DTO](#-example-dto) + - [Using `SerializerManager` in your Service Classes](#-using-serializermanager-in-your-service-classes) + - [Using `ResponseFactory` in Laravel Controllers](#-using-responsefactory-in-laravel-controllers) + - [Using in Laravel Queues](#-using-in-laravel-queues) +- [Security Policy](#-security-policy) +- [Want to Contribute?](#-want-to-contribute) +- [Contributors](#-contributors) +- [Social Links](#-social-links) +- [License](#-license) +- [Credits and Useful Resources](#-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. This includes object normalization, handling of circular references, property grouping, and format-specific encoders. @@ -72,27 +92,191 @@ $ php artisan vendor:publish \
+## πŸ”§ 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 +, + * encoderRegistrationStrategy: class-string, + * metadataLoader: class-string|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 the `SERIALIZER_DEFAULT_FORMAT`environment variable. The default is `symfony-json`. +- **`debug`**: Enables debug mode for `ProblemNormalizer`. This can be set using the `SERIALIZER_DEBUG_MODE` environment variable. It defaults to the `APP_DEBUG` value. +- **`normalizerRegistrationStrategy`**: Specifies the strategy class for registering normalizers. The default strategy is [`WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy`](https://github.com/wayofdev/laravel-symfony-serializer/blob/master/src/DefaultNormalizerRegistrationStrategy.php). +- **`encoderRegistrationStrategy`**: Specifies the strategy class for registering encoders. The default strategy is [`WayOfDev\Serializer\DefaultEncoderRegistrationStrategy`](https://github.com/wayofdev/laravel-symfony-serializer/blob/master/src/DefaultEncoderRegistrationStrategy.php). +- **`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](https://elliotderhay.com/blog/caching-laravel-configs-that-use-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 + +1. Implement the [`NormalizerRegistrationStrategy`](https://github.com/wayofdev/laravel-symfony-serializer/blob/master/src/Contracts/NormalizerRegistrationStrategy.php) interface: + + ```php + }> + */ + public function normalizers(): iterable + { + // ... + } + } + ``` + +2. Change `serializer.php` config to use your custom strategy: + + ```php + 'normalizerRegistrationStrategy' => CustomNormalizerRegistrationStrategy::class, + ``` + +#### Encoder Registration Strategy + +To create a custom encoder registration strategy: + +1. Implement the [`EncoderRegistrationStrategy`](https://github.com/wayofdev/laravel-symfony-serializer/blob/master/src/Contracts/EncoderRegistrationStrategy.php) interface: + + ```php + + */ + 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()]; + } + } + } + ``` + +2. Change `serializer.php` config to use your custom strategy: + + ```php + 'encoderRegistrationStrategy' => CustomEncoderRegistrationStrategy::class, + ``` + +
+ ## πŸ’» Usage The package provides a list of serializers that can be used to serialize and deserialize objects. -The serializers available in this package are: `symfony-json`, `symfony-csv`, `symfony-xml`, `symfony-yaml`. +The default serializers available in this package are: `symfony-json`, `symfony-csv`, `symfony-xml`, `symfony-yaml`. -> **Warning** +> [!WARNING] > The `yaml` encoder requires the `symfony/yaml` package and is disabled when the package is not installed. > Install the `symfony/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 underlying SerializerManager +- `Serializer` β€” To access bound and configured original Symfony Serializer instance. + +### β†’ Example DTO + We will use this example DTO for serialization purposes: ```php getSerializer('json'); - $dto = new MyDTO(1, 'John Doe', 'john@example.com'); + $serializer = $serializer->serializer('symfony-json'); + $dto = new UserDTO(1, 'John Doe', 'john@example.com'); - $content = $serializer->normalize( - data: $dto, + $serialized = $serializer->serialize( + payload: $dto, context: ['groups' => ['private']] ); - - $serialized = $serializer->serialize($content); } } ``` -### β†’ Using ResponseFactory in Laravel Controllers +### β†’ Using `ResponseFactory` in Laravel Controllers -Here's an example of how you can use the `ResponseFactory` in a Laravel controller: +Here's an example of how you can use the `ResponseFactory` in a Laravel Controller: **Example Controller:** ```php response = $response; } public function index() { - $dto = new MyDTO(1, 'John Doe', 'john@example.com'); + $dto = new UserDTO(1, 'John Doe', 'john@example.com'); $this->response->withContext(['groups' => ['private']]); $this->response->withStatusCode(HttpCode::HTTP_OK); @@ -201,6 +380,61 @@ class MyController extends Controller
+### β†’ 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 +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); + } +} +``` + +
+ ## πŸ”’ Security Policy This project has a [security policy](.github/SECURITY.md). @@ -214,7 +448,7 @@ Thank you for considering contributing to the wayofdev community! We are open to - πŸ€” [Suggest a feature](https://github.com/wayofdev/laravel-symfony-serializer/issues/new?assignees=&labels=type%3A+enhancement&projects=&template=2-feature-request.yml&title=%5BFeature%5D%3A+) - πŸ› [Report an issue](https://github.com/wayofdev/laravel-symfony-serializer/issues/new?assignees=&labels=type%3A+documentation%2Ctype%3A+maintenance&projects=&template=1-bug-report.yml&title=%5BBug%5D%3A+) - πŸ“– [Improve documentation](https://github.com/wayofdev/laravel-symfony-serializer/issues/new?assignees=&labels=type%3A+documentation%2Ctype%3A+maintenance&projects=&template=4-docs-bug-report.yml&title=%5BDocs%5D%3A+) -- πŸ‘¨β€πŸ’» Contribute to the code +- πŸ‘¨β€πŸ’» [Contribute to the code](.github/CONTRIBUTING.md) You are more than welcome. Before contributing, kindly check our [contribution guidelines](.github/CONTRIBUTING.md). @@ -239,7 +473,7 @@ You are more than welcome. Before contributing, kindly check our [contribution g
-## βš–οΈ License +## πŸ“œ License [![Licence](https://img.shields.io/github/license/wayofdev/laravel-symfony-serializer?style=for-the-badge&color=blue)](./LICENSE.md) From 434e73baf6ead436c36a36c79d20fc96f222b46b Mon Sep 17 00:00:00 2001 From: lotyp Date: Wed, 19 Jun 2024 15:30:41 +0300 Subject: [PATCH 2/2] fix: add static types to Serializer facade --- config/serializer.php | 16 +++++++++++----- src/Bridge/Laravel/Facades/Serializer.php | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/config/serializer.php b/config/serializer.php index 44512213..37e554f0 100644 --- a/config/serializer.php +++ b/config/serializer.php @@ -2,13 +2,19 @@ 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, - * encoderRegistrationStrategy: class-string, - * metadataLoader: class-string|null, + * normalizerRegistrationStrategy: class-string, + * encoderRegistrationStrategy: class-string, + * metadataLoader: class-string|null, * } */ return [ @@ -38,7 +44,7 @@ * Allows you to specify the strategy class for registering your normalizers. * Default is 'WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy'. */ - 'normalizerRegistrationStrategy' => WayOfDev\Serializer\DefaultNormalizerRegistrationStrategy::class, + 'normalizerRegistrationStrategy' => DefaultNormalizerRegistrationStrategy::class, /* * Allows you to register your custom encoders. @@ -53,7 +59,7 @@ * You can replace the default encoders with your custom ones by implementing * your own registration strategy. */ - 'encoderRegistrationStrategy' => WayOfDev\Serializer\DefaultEncoderRegistrationStrategy::class, + 'encoderRegistrationStrategy' => DefaultEncoderRegistrationStrategy::class, /* * Allows you to register your custom metadata loader. diff --git a/src/Bridge/Laravel/Facades/Serializer.php b/src/Bridge/Laravel/Facades/Serializer.php index da11941e..b97f7c9c 100644 --- a/src/Bridge/Laravel/Facades/Serializer.php +++ b/src/Bridge/Laravel/Facades/Serializer.php @@ -4,8 +4,26 @@ namespace WayOfDev\Serializer\Bridge\Laravel\Facades; +use ArrayObject; use Illuminate\Support\Facades\Facade; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +/** + * @method static string serialize(mixed $data, string $format, array $context = []) + * @method static mixed deserialize(string $data, string $type, string $format, array $context = []) + * @method static array|string|int|float|bool|ArrayObject|null normalize(mixed $data, string $format, array $context = []) + * @method static mixed denormalize(mixed $data, string $type, ?string $format = null, array $context = []) + * @method static array getSupportedTypes(?string $format) + * @method static bool supportsNormalization(mixed $data, ?string $format = null, array $context = []) + * @method static bool supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []) + * @method static NormalizerInterface|null getNormalizer(mixed $data, ?string $format, array $context) + * @method static DenormalizerInterface|null getDenormalizer(mixed $data, string $type, ?string $format, array $context) + * @method static string encode(mixed $data, string $format, array $context = []) + * @method static mixed decode(string $data, string $type, string $format, array $context = []) + * @method static bool supportsEncoding(string $format, array $context = []) + * @method static bool supportsDecoding(string $format, array $context = []) + */ class Serializer extends Facade { protected static function getFacadeAccessor(): string