cuyz/valinor
Valinor maps raw input (JSON/arrays) into fully typed PHP objects, validating along the way with clear, human-readable errors. Supports advanced types (PHPStan/Psalm), shaped arrays, generics, ranges, and can normalize objects back to JSON/CSV while preserving structure.
Installation:
composer require cuyz/valinor
Basic Mapping:
Define a DTO class with typed properties (e.g., Country with name and cities).
Use MapperBuilder to map raw input (JSON/array) to the DTO:
$country = (new \CuyZ\Valinor\MapperBuilder())
->mapper()
->map(Country::class, \CuyZ\Valinor\Mapper\Source\Source::json($json));
Error Handling:
Wrap mapping in a try-catch to handle MappingError:
try {
$country = $mapper->map(Country::class, $source);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
// Log or return user-friendly errors
}
Map HTTP request data (e.g., JSON payload) to a DTO for validation:
$mapper = (new MapperBuilder())->mapper();
$requestData = json_decode(file_get_contents('php://input'), true);
$dto = $mapper->map(UserCreateRequest::class, Source::array($requestData));
non-empty-string, list<City>) for runtime validation.readonly properties to enforce immutability.City inside Country) for complex structures.MapperBuilder once (e.g., as a service) and reuse the mapper:
$mapper = (new MapperBuilder())->mapper();
$container->bind('mapper', fn() => $mapper);
Source::json(), Source::array(), or Source::file() for different input types.cuyz/valinor-bundle for automatic request mapping in controllers.mezzio/mezzio-valinor for PSR-7 request handling.ArgumentsMapper:
$arguments = $argumentsMapper->mapArguments($controller, HttpRequest::fromPsr($psrRequest));
Convert DTOs back to arrays/JSON for APIs or storage:
$normalizer = (new NormalizerBuilder())->normalizer(Format::json());
$json = $normalizer->normalize($country);
Override default mapping for complex objects:
$mapperBuilder->withConstructorResolver(new CustomConstructorResolver());
Cache Invalidation:
MapperBuilder::clearCache()) after changing DTOs causes stale mappings.FileWatchingCache to auto-invalidate on file changes:
$cache = new FileWatchingCache(new FileSystemCache('cache_dir'));
$mapperBuilder->withCache($cache);
Performance:
$mapperBuilder->warmupCacheFor([Country::class, City::class]);
MapperBuilder instances; rebuilding mappers is expensive.Static Analysis:
@phpstan-ignore or @psalm-suppress if needed.valinor-phpstan-suppress-pure-errors.php in phpstan.neon to ignore all purity errors.Flexible Modes:
MapperBuilder::withFlexibleMode() to allow partial mapping (e.g., missing optional fields), but validate strictly by default.MappingError::getErrors() to inspect validation failures:
foreach ($error->getErrors() as $path => $error) {
echo "Path $path: {$error->getMessage()}\n";
}
string * int in shaped arrays).NormalizerInterface.Source to handle custom input formats (e.g., XML).MapperBuilder::withValidationRules() to add custom rules (e.g., regex validation):
$mapperBuilder->withValidationRules([
'email' => new EmailValidationRule(),
]);
spatie/immutable-array for immutable nested arrays.return response()->json($normalizer->normalize($dto));
MapperBuilder::withTestingMode() to skip cache and validate edge cases.How can I help you explore Laravel packages today?