### Install Valinor using Composer Source: https://github.com/cuyz/valinor/blob/master/docs/pages/getting-started.md This snippet shows the Composer command to install the Valinor library. Composer is a dependency manager for PHP, ensuring all necessary packages are downloaded and installed. ```bash composer require cuyz/valinor ``` -------------------------------- ### Map to Array of Objects in PHP Source: https://github.com/cuyz/valinor/blob/master/docs/pages/getting-started.md Illustrates how to map an input source to an array of objects using Valinor. This is useful when dealing with collections of items where each item conforms to a specific class structure. Error handling for mapping issues is included. ```php try { $objects = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map( 'array<' . SomeClass::class . '>', [/* … */] ); } catch (\CuyZ\Valinor\Mapper\MappingError $error) { // Do something… } ``` -------------------------------- ### Map JSON to PHP Thread Object Source: https://github.com/cuyz/valinor/blob/master/docs/pages/getting-started.md Demonstrates mapping a JSON string representing a thread and its answers into corresponding PHP value objects (Thread and Answer). It utilizes Valinor's MapperBuilder to create a mapper and then maps the JSON source to the Thread class, including error handling for mapping errors. ```php final class Thread { public function __construct( public readonly int $id, public readonly string $content, public readonly DateTimeInterface $date, /** @var Answer[] */ public readonly array $answers, ) {} } final class Answer { public function __construct( public readonly string $user, public readonly string $message, public readonly DateTimeInterface $date, ) {} } ``` ```php public function getThread(int $id): Thread { $rawJson = $this->client->request("https://example.com/thread/$id"); try { return (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map( Thread::class, new \CuyZ\Valinor\Mapper\Source\JsonSource($rawJson) ); } catch (\CuyZ\Valinor\Mapper\MappingError $error) { // Do something… } } ``` -------------------------------- ### Install Valinor using Composer Source: https://github.com/cuyz/valinor/blob/master/README.md This command installs the Valinor library using Composer, the dependency manager for PHP. Ensure you have Composer installed on your system. ```Bash composer require cuyz/valinor ``` -------------------------------- ### Map to Array Shape in PHP Source: https://github.com/cuyz/valinor/blob/master/docs/pages/getting-started.md Demonstrates mapping an input to a specific array structure (array shape) with defined keys and types in PHP. This provides type safety for array access and is suitable for simpler data structures. Includes error handling. ```php try { $array = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map( 'array{foo: string, bar: int}', [/* … */] ); echo $array['foo']; echo $array['bar'] * 2; } catch (\CuyZ\Valinor\Mapper\MappingError $error) { // Do something… } ``` -------------------------------- ### Install Valinor Symfony Bundle Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/app-and-framework-integration.md Installs the Valinor Symfony bundle using Composer, enabling automatic integration with Symfony applications. This is the recommended approach for Symfony users. ```bash composer require cuyz/valinor-bundle ``` -------------------------------- ### PHP: Injecting Cache with MapperBuilder Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/upgrading.md Provides an example of how to inject a cache implementation into Valinor's `MapperBuilder`. It shows the setup for both a file system cache and a file watching cache for development environments. ```PHP $cache = new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-directory'); if ($isApplicationInDevelopmentEnvironment) { $cache = new \CuyZ\Valinor\Cache\FileWatchingCache($cache); } (new \CuyZ\Valinor\MapperBuilder()) ->withCache($cache) ->mapper() ->map(SomeClass::class, [/* … */]); ``` -------------------------------- ### Cast to Boolean using Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates how to register a custom converter globally to cast string representations of boolean values ('yes', 'no', 'on', 'off') to actual boolean types. It shows the setup of a User class and the mapping process using MapperBuilder. ```php namespace My\App; final readonly class User { public function __construct( public string $name, public bool $isActive, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter(function (string $value, callable $next): bool { $value = match ($value) { 'yes', 'on' => true, 'no', 'off' => false, default => $value, }; return $next($value); }) ->mapper() ->map(\My\App\User::class, [ 'name' => 'John Doe', 'isActive' => 'yes', ]); $user->name === 'John Doe'; $user->isActive === true; ``` -------------------------------- ### Cast to String using Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates how to register a global converter to cast integer or float values to strings. It sets up a User class with a string ID and maps an integer value to it, showing the successful conversion. ```php namespace My\App; final readonly class User { public function __construct( public string $id, public string $name, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( fn (int|float $value): string => (string)$value ) ->mapper() ->map(\My\App\User::class, [ 'id' => 1337, // Integer 1337 will be converted to string '1337' 'name' => 'John Doe', ]); $user->id === '1337'; $user->name === 'John Doe'; ``` -------------------------------- ### PHP: Global Key Renaming with Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates how to globally rename keys in Valinor using a custom converter. The converter iterates through the input array, applying a predefined mapping to rename keys before passing them to the next stage of the mapping process. It requires the CuyZValinor library. ```php namespace My\App; final readonly class Location { public string $city; public string $zipCode; } (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( function (array $value, callable $next): mixed { $mapping = [ 'town' => 'city', 'postalCode' => 'zipCode', ]; $renamed = []; foreach ($value as $key => $item) { $renamed[$mapping[$key] ?? $key] = $item; } return $next($renamed); } ) ->mapper() ->map(\My\App\Location::class, [ 'town' => 'Lyon', // `town` will be renamed to `city` 'postalCode' => '69000', // `postalCode` will be renamed to `zipCode` ]); ``` -------------------------------- ### PHP: Convert snake_case to camelCase keys using attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example shows how to achieve snake_case to camelCase key conversion using a custom attribute with the CuyZ/Valinor library. The `CamelCaseKeys` attribute is applied directly to the target class, providing a more declarative way to manage key transformations. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_CLASS)] final class CamelCaseKeys { /** * @param array $value * @param callable(array): object $next */ public function map(array $value, callable $next): object { $transformed = []; foreach ($value as $key => $item) { $camelCaseKey = lcfirst(str_replace('_', '', ucwords($key, '_'))); $transformed[$camelCaseKey] = $item; } return $next($transformed); } } #[My\App\CamelCaseKeys] final readonly class User { public string $userName; public \DateTimeInterface $birthDate; } (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\'My\App\User::class, [ // Note that the input keys have the `snake_case` format, but the // properties of `User` have the `camelCase` format. 'user_name' => 'John Doe', 'birth_date' => '1971-11-08T00:00:00+00:00', ]); ``` -------------------------------- ### PHP: Granular Key Renaming with Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example shows how to rename keys for specific objects in Valinor using a custom attribute. The `RenameKeys` attribute takes a mapping array and applies it during the mapping process, providing more targeted control over key transformations. It requires the CuyZValinor library. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_CLASS)] final class RenameKeys { public function __construct( /** @var non-empty-array */ private array $mapping, ) {} /** * @param array $value * @param callable(array): object $next */ public function map(array $value, callable $next): object { $renamed = []; foreach ($value as $key => $item) { $renamed[$this->mapping[$key] ?? $key] = $item; } return $next($renamed); } } #[My\App\RenameKeys([ 'town' => 'city', 'postalCode' => 'zipCode', ])] final readonly class Location { public string $city; public string $zipCode; } (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\Location::class, [ 'town' => 'Lyon', // `town` will be renamed to `city` 'postalCode' => '69000', // `postalCode` will be renamed to `zipCode` ]); ``` -------------------------------- ### PHP: Warm up Cache for Mapper Builder Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/performance-and-caching.md This code example shows how to initialize a file system cache and use it to warm up the cache for specific classes using a `MapperBuilder`. This is useful for pre-populating the cache during build or deployment processes. ```php $cache = new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-dir'); $mapperBuilder = (new \CuyZ\Valinor\MapperBuilder())->withCache($cache); // During the build: $mapperBuilder->warmupCacheFor(SomeClass::class, SomeOtherClass::class); // In the application: $mapperBuilder->mapper()->map(SomeClass::class, [/* … */]); ``` -------------------------------- ### PHP: Customize Global Date Format Source: https://github.com/cuyz/valinor/blob/master/docs/pages/serialization/common-transformers-examples.md This example demonstrates how to globally customize the date format using a transformer. It registers a custom transformer with the NormalizerBuilder to format all dates to 'Y/m/d'. ```php (new \CuyZ\Valinor\NormalizerBuilder()) ->registerTransformer( fn (\DateTimeInterface $date) => $date->format('Y/m/d') ) ->normalizer(\CuyZ\Valinor\Normalizer\Format::array()) ->normalize( new \My\App\Event( eventName: 'Release of legendary album', date: new \DateTimeImmutable('1971-11-08'), ) ); // [ // 'eventName' => 'Release of legendary album', // 'date' => '1971/11/08', // ] ``` -------------------------------- ### PHP: Convert snake_case to camelCase keys Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates how to convert array keys from snake_case to camelCase format before mapping them to object properties. It utilizes the CuyZ/Valinor library's converter registration API to handle the transformation. ```php namespace My\App; final readonly class User { public string $userName; public \DateTimeInterface $birthDate; } (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( /** * Note that this converter will only be called when the input is an * array and the target type is an object. */ function (array $values, callable $next): object { $camelCaseConverted = array_combine( array_map( fn ($key) => lcfirst(str_replace('_', '', ucwords($key, '_'))), array_keys($values), ), $values, ); return $next($camelCaseConverted); } ) ->mapper() ->map(\'My\App\User::class, [ // Note that the input keys have the `snake_case` format, but the // properties of `User` have the `camelCase` format. 'user_name' => 'John Doe', 'birth_date' => '1971-11-08T00:00:00+00:00', ]); ``` -------------------------------- ### Create and Use Custom Iterable Source Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/import-formatted-source.md Explains how to create a custom source by implementing the `IteratorAggregate` interface. It shows an example of a custom `AcmeSource` that processes an iterable and can be combined with modifiers like `camelCaseKeys`. ```PHP final class AcmeSource implements IteratorAggregate { private iterable $source; public function __construct(iterable $source) { $this->source = $this->doSomething($source); } private function doSomething(iterable $source): iterable { // Do something with $source return $source; } public function getIterator() { yield from $this->source; } } $source = CuyZValinorMapperSourceSource::iterable( new AcmeSource([ 'valueA' => 'foo', 'valueB' => 'bar', ]) )->camelCaseKeys(); (new CuyZValinorMapperBuilder()) ->mapper() ->map(SomeClass::class, $source); ``` -------------------------------- ### PHP: Instantiate and use NormalizerBuilder Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/changelog/version-2.0.0.md Shows how to use the new NormalizerBuilder in PHP to register transformers and normalize data. This example demonstrates creating a normalizer that formats DateTimeInterface objects. ```php $normalizer = (new \CuyZ\Valinor\NormalizerBuilder()) ->registerTransformer( fn (\DateTimeInterface $date) => $date->format('Y/m/d') ) ->normalizer(\CuyZ\Valinor\Normalizer\Format::array()) ->normalize($someData); ``` -------------------------------- ### Configure File System Cache in PHP Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/app-and-framework-integration.md Shows how to configure a file system cache for the Valinor mapper and normalizer builders. It includes an example of wrapping the cache with `FileWatchingCache` for development environments to automatically clear the cache when files change. ```php $cache = new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-directory'); // If the application can detect when it is in development environment, it is // advised to wrap the cache with a `FileWatchingCache` instance, to avoid // having to manually clear the cache when a file changes during development. if ($isApplicationInDevelopmentEnvironment) { $cache = new \CuyZ\Valinor\Cache\FileWatchingCache($cache); } $mapperBuilder = $mapperBuilder->withCache($cache); $normalizerBuilder = $normalizerBuilder->withCache($cache); ``` -------------------------------- ### PHP - Basic Uppercase Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/convert-input.md A basic example of a converter that converts string inputs to uppercase. It registers a callable converter with the MapperBuilder and then uses the mapper to convert a string. ```php (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( fn (string $value): string => strtoupper($value) ) ->mapper() ->map('string', 'hello world'); // 'HELLO WORLD' ``` -------------------------------- ### PHP - Chained Converters with Priority Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/convert-input.md Demonstrates chaining converters with different priorities. A callable converter can call the next one in the chain, and priorities control the execution order. This example converts to uppercase, appends '!', and then appends '?'. ```php (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( fn (string $value, callable $next): string => $next(strtoupper($value)) ) ->registerConverter( fn (string $value, callable $next): string => $next($value . '!'), priority: -10, ) ->registerConverter( fn (string $value, callable $next): string => $next($value . '?'), priority: 10, ) ->mapper() ->map('string', 'hello world'); // 'HELLO WORLD?!' ``` -------------------------------- ### PHP: Register chained mapper converters with priority Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/changelog/version-2.0.0.md Illustrates registering multiple mapper converters in PHP with chaining and priority control. This example shows how to apply transformations sequentially and manage their execution order using priorities. ```php (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter( function(string $value, callable $next): string { return $next(strtoupper($value)); } ) ->registerConverter( function(string $value, callable $next): string { return $next($value . '!'); }, priority: -10, ) ->registerConverter( function(string $value, callable $next): string { return $next($value . '?'); }, priority: 10, ) ->mapper() ->map('string', 'hello world'); // 'HELLO WORLD?!' ``` -------------------------------- ### PHP Enum Definition and Usage Source: https://github.com/cuyz/valinor/blob/master/docs/pages/usage/type-reference.md Illustrates the definition and usage of enums in PHP. It provides examples of creating an enum and using it in class properties with type hints, including referencing specific enum cases and pattern matching. ```php enum SomeEnum { case FOO; case BAR; case BAZ; } final class SomeClass { public function __construct( private SomeEnum $enum, /** @var SomeEnum::FOO|SomeEnum::BAR */ private SomeEnum $oneOfTwoCasesOfEnum, /** @var SomeEnum::BA* (matches BAR or BAZ) */ private SomeEnum $casesOfEnumMatchingPattern, ) {} } ``` -------------------------------- ### Import JSON, YAML, or File Source Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/import-formatted-source.md Demonstrates how to create a `Source` object from JSON strings, YAML strings, or files using `Source::json()`, `Source::yaml()`, and `Source::file()`. It then shows how to map this source to a class using `MapperBuilder`. ```PHP $source = CuyZValinorMapperSourceSource::json($jsonString); // or… $source = CuyZValinorMapperSourceSource::yaml($yamlString); // or… // File containing valid Json or Yaml content and with valid extension $source = CuyZValinorMapperSourceSource::file( new SplFileObject('path/to/my/file.json') ); (new CuyZValinorMapperBuilder()) ->mapper() ->map(SomeClass::class, $source); ``` -------------------------------- ### Create Benchmark Baseline - Shell Source: https://github.com/cuyz/valinor/blob/master/docs/pages/internals/benchmarks.md Establishes a performance baseline by running benchmarks and storing their results. This baseline serves as a reference point for future performance comparisons. ```Shell composer run-script benchmark-baseline ``` -------------------------------- ### PHP: Configure and Use File System Cache with File Watching Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/performance-and-caching.md This snippet demonstrates how to initialize a file system cache and optionally decorate it with `FileWatchingCache` for development environments. It then shows how to use the cache with both a `MapperBuilder` and a `NormalizerBuilder`. ```php $cache = new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-directory'); if ($isApplicationInDevelopmentEnvironment) { $cache = new \CuyZ\Valinor\Cache\FileWatchingCache($cache); } (new \CuyZ\Valinor\MapperBuilder()) ->withCache($cache) ->mapper() ->map(SomeClass::class, [/* … */]); (new \CuyZ\Valinor\NormalizerBuilder()) ->withCache($cache) ->normalizer(\CuyZ\Valinor\Normalizer\Format::json()) ->normalize($someData); ``` -------------------------------- ### PHP: Cast String to Float with Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates casting a string representation of a float to a float type using a custom converter. The converter checks if the string is numeric before converting it to a float. ```php namespace My\App; final readonly class User { public function __construct( public string $name, public float $accountBalance, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter(function (string $value, callable $next): float { if (is_numeric($value)) { return (float)$value; } return $next($value); }) ->mapper() ->map(My\App\User::class, [ 'name' => 'John Doe', 'accountBalance' => '1337.42', // String '1337.42' will be converted to float 1337.42 ]); $user->name === 'John Doe'; $user->accountBalance === 1337.42; ``` -------------------------------- ### Cast to String using Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example illustrates using an attribute for property-specific string casting. A CastToString attribute is defined and applied to the 'id' property of the User class, converting an integer input to a string. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(Attribute::TARGET_PROPERTY)] final class CastToString { public function map(int|float $value): string { return (string)$value; } } final readonly class User { public function __construct( #[My\App\CastToString] public string $id, public string $name, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\User::class, [ 'id' => 1337, // Integer 1337 will be converted to string '1337' 'name' => 'John Doe', ]); $user->id === '1337'; $user->name === 'John Doe'; ``` -------------------------------- ### Run Benchmarks - Shell Source: https://github.com/cuyz/valinor/blob/master/docs/pages/internals/benchmarks.md Executes all defined benchmarks using a Composer script. This command initiates the performance testing process, generating output that includes detailed statistics for each benchmarked subject. ```Shell composer run-script benchmark ``` -------------------------------- ### PHP: Cast String to Integer with Converter Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example demonstrates how to cast a string value to an integer using a custom converter registered with Valinor's MapperBuilder. The converter checks if the string is a valid integer before casting. ```php namespace My\App; final readonly class User { public function __construct( public string $name, public int $age, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->registerConverter(function (string $value, callable $next): int { if (filter_var($value, FILTER_VALIDATE_INT)) { return (int)$value; } return $next($value); }) ->mapper() ->map(My\App\User::class, [ 'name' => 'John Doe', 'age' => '42', // String '42' will be converted to integer 42 ]); $user->name === 'John Doe'; $user->age === 42; ``` -------------------------------- ### PHP: Customize Specific Date Format with Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/serialization/common-transformers-examples.md This example shows how to format a specific date property using a custom attribute. A `DateTimeFormat` attribute is defined to specify the desired date format, which is then applied to the property. ```php namespace My\App; #[CuyZ\Valinor\Normalizer\AsTransformer] #[Attribute(\Attribute::TARGET_PROPERTY)] final class DateTimeFormat { public function __construct(private string $format) {} public function normalize(\DateTimeInterface $date): string { return $date->format($this->format); } } final readonly class Event { public function __construct( public string $eventName, #[My\App\DateTimeFormat('Y/m/d')] public \DateTimeInterface $date, ) {} } (new \CuyZ\Valinor\NormalizerBuilder()) ->normalizer(\CuyZ\Valinor\Normalizer\Format::array()) ->normalize( new \My\App\Event( eventName: 'Release of legendary album', date: new \DateTimeImmutable('1971-11-08'), ) ); // [ // 'eventName' => 'Release of legendary album', // 'date' => '1971/11/08', // ] ``` -------------------------------- ### PHP: Using NormalizerBuilder for Normalization Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/upgrading.md Shows the usage of the new `NormalizerBuilder` in Valinor 2.x for creating and configuring normalizers. It demonstrates registering a transformer and specifying the normalization format. ```PHP $normalizer = (new \CuyZ\Valinor\NormalizerBuilder()) ->registerTransformer( fn (\DateTimeInterface $date) => $date->format('Y/m/d') ) ->normalizer(\CuyZ\Valinor\Normalizer\Format::array()) ->normalize($someData); ``` -------------------------------- ### PHP: Cast String to Float with Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example shows how to cast a string representation of a float to a float type for a specific property using a custom attribute. The `CastToFloat` attribute is applied directly to the property for targeted conversion. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_PROPERTY)] final class CastToFloat { /** * @param callable(string): float $next */ public function map(string $value, callable $next): float { if (is_numeric($value)) { return (float)$value; } return $next($value); } } final readonly class User { public function __construct( public string $name, #[My\App\CastToFloat] public float $accountBalance, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(My\App\User::class, [ 'name' => 'John Doe', 'accountBalance' => '1337.42', // String '1337.42' will be converted to float 1337.42 ]); $user->name === 'John Doe'; $user->accountBalance === 1337.42; ``` -------------------------------- ### Configure and Use Cache with Valinor Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/changelog/version-0.9.0.md Demonstrates how to configure and use the cache feature in Valinor, including setting a cache directory, optionally decorating with FileWatchingCache for development environments, and warming up the cache. ```PHP $cache = new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-directory'); if ($isApplicationInDevelopmentEnvironment) { $cache = new \CuyZ\Valinor\Cache\FileWatchingCache($cache); } $mapperBuilder = (new \CuyZ\Valinor\MapperBuilder())->withCache($cache); // During the build: $mapperBuilder->warmup(SomeClass::class, SomeOtherClass::class); // In the application: $mapperBuilder->mapper()->map(SomeClass::class, [/* … */]); ``` -------------------------------- ### PHP: Cast String to Integer with Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example shows how to cast a string value to an integer for a specific property using a custom attribute. The `CastToInt` attribute is applied directly to the property, providing granular control over the casting process. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_PROPERTY)] final class CastToInt { /** * @param callable(string): int $next */ public function map(string $value, callable $next): int { if (filter_var($value, FILTER_VALIDATE_INT)) { return (int)$value; } return $next($value); } } final readonly class User { public function __construct( public string $name, #[My\App\CastToInt] public int $age, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(My\App\User::class, [ 'name' => 'John Doe', 'age' => '42', // String '42' will be converted to integer 42 ]); $user->name === 'John Doe'; $user->age === 42; ``` -------------------------------- ### Cast to Boolean using Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md This example shows how to use an attribute to target specific properties for boolean casting. A custom CastToBool attribute is defined and applied to the 'isActive' property of the User class, enabling the conversion of string inputs to booleans. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(Attribute::TARGET_PROPERTY)] final class CastToBool { /** * @param callable(mixed): bool $next */ public function map(string $value, callable $next): bool { $value = match ($value) { 'yes', 'on' => true, 'no', 'off' => false, default => $value, }; return $next($value); } } final readonly class User { public function __construct( public string $name, #[My\App\CastToBool] public bool $isActive, ) {} } $user = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\User::class, [ 'name' => 'John Doe', 'isActive' => 'yes', ]); $user->name === 'John Doe'; $user->isActive === true; ``` -------------------------------- ### Inferring interface implementations with multiple options Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/infer-interfaces.md This example demonstrates how to infer which implementation of an interface should be used based on a string value in the input data. The callback function returns a class-string, and if multiple implementations are possible, a return signature with all possible class-strings is provided. ```php $mapper = (new \CuyZ\Valinor\MapperBuilder()) ->infer(UuidInterface::class, fn () => MyUuid::class) ->infer( SomeInterface::class, /** @return class-string */ fn (string $type) => match($type) { 'first' => FirstImplementation::class, 'second' => SecondImplementation::class, default => throw new DomainException("Unhandled type `$type`.") } )->mapper(); // Will return an instance of `FirstImplementation` $mapper->map(SomeInterface::class, [ 'type' => 'first', 'uuid' => 'a6868d61-acba-406d-bcff-30ecd8c0ceb6', 'someString' => 'foo', ]); // Will return an instance of `SecondImplementation` $mapper->map(SomeInterface::class, [ 'type' => 'second', 'uuid' => 'a6868d61-acba-406d-bcff-30ecd8c0ceb6', 'someInt' => 42, ]); interface SomeInterface {} final class FirstImplementation implements SomeInterface { public readonly UuidInterface $uuid; public readonly string $someString; } final class SecondImplementation implements SomeInterface { public readonly UuidInterface $uuid; public readonly int $someInt; } ``` -------------------------------- ### PHP: Warming up Cache in Valinor Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/upgrading.md This snippet demonstrates how to warm up the cache for Valinor using the renamed `warmupCacheFor()` method. ```PHP warmupCacheFor(MyClass::class); $mapper = $mapperBuilder->mapper(); ``` -------------------------------- ### Map Source Paths Using Dot Notation Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/import-formatted-source.md Demonstrates the `map()` modifier for transforming source data paths using dot notation. It explains how to define mappings with source paths as keys and target paths as values, including support for array paths with `*`. ```PHP final class Country { /** @var non-empty-string */ public readonly string $name; /** @var list */ public readonly array $cities; } final class City { /** @var non-empty-string */ public readonly string $name; public readonly DateTimeZone $timeZone; } $source = CuyZValinorMapperSourceSource::array([ 'identification' => 'France', 'towns' => [ [ 'label' => 'Paris', 'timeZone' => 'Europe/Paris', ], [ 'label' => 'Lyon', 'timeZone' => 'Europe/Paris', ], ], ])->map([ 'identification' => 'name', 'towns' => 'cities', 'towns.*.label' => 'name', ]); // After modification this is what the source will look like: // [ // 'name' => 'France', // 'cities' => [ // [ // 'name' => 'Paris', // 'timeZone' => 'Europe/Paris', // ], // [ // 'name' => 'Lyon', // 'timeZone' => 'Europe/Paris', // ], // ], // ]; (new CuyZValinorMapperBuilder())->mapper()->map(Country::class, $source); ``` -------------------------------- ### PHP: Array to List Conversion Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md Provides an `ArrayToList` attribute that converts an associative array into a numerically indexed list. The `map` method uses `array_values` to re-index the array. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_PROPERTY)] final class ArrayToList { /** * @param array $value * @return list */ public function map(array $value): array { return array_values($value); } } final readonly class Person { public string $name; /** @var list */ #[My\App\ArrayToList] public array $pets; } $person = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\Person::class, [ 'name' => 'John Doe', 'pets' => [ 'Dog' => 'Dog', 'Cat' => 'Cat', ], ]); $person->pets === [0 => 'Dog', 1 => 'Cat']; ``` -------------------------------- ### Register Mapper and Normalizer in PHP Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/app-and-framework-integration.md Demonstrates how to register mapper and normalizer instances as shared services within a framework's service container. This ensures efficient caching and performance by reusing heavy operations. ```php $mapperBuilder = new \CuyZ\Valinor\MapperBuilder(); $normalizerBuilder = new \CuyZ\Valinor\NormalizerBuilder(); // …customization of the mapper builder and normalization builder… $mapper = $mapperBuilder->mapper(); $jsonNormalizer = $normalizerBuilder->normalizer(\CuyZ\Valinor\Normalizer\Format::json()); $container->addSharedService('mapper', $mapper); $container->addSharedService('normalizer_json', $jsonNormalizer); ``` -------------------------------- ### PHP: Use AsTransformer Attribute for Normalization Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/changelog/version-1.11.0.md Introduces the `AsTransformer` attribute in PHP for the Valinor normalizer, simplifying the registration of custom transformers. This example shows how to format a DateTime object during normalization. ```php namespace My\App; #[CuyZ\Valinor\Normalizer\AsTransformer] #[Attribute(\Attribute::TARGET_PROPERTY)] final class DateTimeFormat { public function __construct(private string $format) {} public function normalize(\DateTimeInterface $date): string { return $date->format($this->format); } } final readonly class Event { public function __construct( public string $eventName, #[My\App\DateTimeFormat('Y/m/d')] public \DateTimeInterface $date, ) {} } (new \CuyZ\Valinor\MapperBuilder()) ->normalizer(\CuyZ\Valinor\Normalizer\Format::array()) ->normalize(new \My\App\Event( eventName: 'Release of legendary album', date: new \DateTimeImmutable('1971-11-08'), )); // [ // 'eventName' => 'Release of legendary album', // 'date' => '1971/11/08', // ] ``` -------------------------------- ### PHP: Accessing Message Information Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/upgrading.md Illustrates how to access information like name, path, type, and source value from a message object in Valinor. The 2.x version provides direct methods on the message object, simplifying access compared to the 1.x version's node traversal. ```PHP // Before (1.x): $name = $message->node()->name(); $path = $message->node()->path(); $type = $message->node()->type(); $type = $message->node()->sourceValue(); // After (2.x): $name = $message->name(); $path = $message->path(); $type = $message->type(); $type = $message->sourceValue(); ``` -------------------------------- ### PHP: JSON Decode Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md Introduces a `JsonDecode` attribute to automatically decode JSON strings into PHP arrays or other types. The `map` method uses `json_decode` with `JSON_THROW_ON_ERROR` for robust error handling. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_PROPERTY)] final class JsonDecode { /** * @param callable(mixed): mixed $next */ public function map(string $value, callable $next): mixed { $decoded = json_decode($value, associative: true, flags: JSON_THROW_ON_ERROR); return $next($decoded); } } final readonly class UserProfile { public string $username; public string $email; /** @var array */ #[My\App\JsonDecode] public array $preferences; /** @var list */ #[My\App\JsonDecode] public array $tags; } $userProfile = (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\UserProfile::class, [ 'username' => 'john_doe', 'email' => 'john.doe@example.com', 'preferences' => '{"theme": "dark", "notifications": true}', 'tags' => '["developer", "php", "api"]', ]); $userProfile->preferences === ['theme' => 'dark', 'notifications' => true]; $userProfile->tags === ['developer', 'php', 'api']; ``` -------------------------------- ### PHP: Mapping callable arguments with static analysis Source: https://github.com/cuyz/valinor/blob/master/docs/pages/other/static-analysis.md This PHP example demonstrates using Valinor to map arguments for a callable function. Static analysis verifies that the provided arguments match the expected types and names, ensuring the function is called correctly. ```php $someFunction = function(string $foo, int $bar): string { return "$foo / $bar"; }; $arguments = (new \CuyZ\Valinor\MapperBuilder()) ->argumentsMapper() ->mapArguments($someFunction, [ 'foo' => 'some value', 'bar' => 42, ]); // ✅ Arguments have a correct shape, no error reported echo $someFunction(...$arguments); ``` -------------------------------- ### PHP: Explode String to List Attribute Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/common-converters-examples.md Implements an `Explode` attribute to convert a comma-separated string into an array (list) based on a specified separator. The `map` method uses the `explode` function for this conversion. ```php namespace My\App; #[CuyZ\Valinor\Mapper\AsConverter] #[Attribute(\Attribute::TARGET_PROPERTY)] final class Explode { public function __construct( /** @var non-empty-string */ private string $separator, ) {} /** * @return array */ public function map(string $value): array { return explode($this->separator, $value); } } final readonly class Product { public string $name; /** @var list */ #[My\App\Explode(separator: ',')] public array $size; } (new \CuyZ\Valinor\MapperBuilder()) ->mapper() ->map(\My\App\Product::class, [ 'name' => 'T-Shirt', 'size' => 'XS,S,M,L,XL', ]); ``` -------------------------------- ### Map Arguments for Class Method Source: https://github.com/cuyz/valinor/blob/master/docs/pages/how-to/map-arguments-of-a-callable.md Illustrates mapping arguments for a static method of a PHP class using Valinor. This example shows how to use the arguments mapper with a class method reference, ensuring the input data conforms to the method's parameters. ```php final class SomeController { public static function someAction(string $foo, int $bar): string { return "$foo / $bar"; } } try { $arguments = (new \CuyZ\Valinor\MapperBuilder()) ->argumentsMapper() ->mapArguments(SomeController::someAction(...), [ 'foo' => 'some value', 'bar' => 42, ]); // some value / 42 echo SomeController::someAction(...$arguments); } catch (\CuyZ\Valinor\Mapper\MappingError $error) { // Do something… } ``` -------------------------------- ### PHP: Allow Non-Sequential List Casting Source: https://github.com/cuyz/valinor/blob/master/docs/pages/project/changelog/version-1.17.0.md Shows how to configure Valinor to accept non-sequential array keys when mapping to list types, converting associative arrays into lists with sequential keys starting from 0. ```PHP (new \CuyZ\Valinor\MapperBuilder()) ->allowNonSequentialList() ->mapper() ->map('list', [ 'foo' => 42, 'bar' => 1337, ]); // => [0 => 42, 1 => 1337] ```