phrity/util-transformer
Lightweight PHP utility for transforming values between types. Provides transformers with canTransform()/transform(), plus resolvers to chain and recurse converters. Includes JSON/flatten decoders and converters for basic types, DateTime, enums, Stringable, Throwable and more.
Installation:
composer require phrity/util-transformer
First Use Case:
Transform a simple value (e.g., DateTime to string):
use Phrity\Util\Transformer\Converters\DateTimeConverter;
use Phrity\Util\Transformer\Types;
$dateTime = new \DateTime('2025-07-29 14:13');
$converter = new DateTimeConverter();
if ($converter->canTransform($dateTime, Types::STRING)) {
$result = $converter->transform($dateTime, Types::STRING);
// $result = "2025-07-29T14:13:00+00:00" (default ISO 8601 format)
}
Where to Look First:
FirstMatchResolver or RecursionResolver.Check Compatibility:
Always use canTransform($subject, $targetType = null) before calling transform() to avoid runtime errors.
if ($transformer->canTransform($data, Types::ARRAY)) {
$arrayData = $transformer->transform($data, Types::ARRAY);
}
Chaining Transformers:
Use FirstMatchResolver to chain transformers for fallback behavior:
use Phrity\Util\Transformer\Resolvers\FirstMatchResolver;
$resolver = new FirstMatchResolver([
new DateTimeConverter(),
new EnumConverter(),
new BasicTypeConverter(), // Fallback
]);
Recursive Transformations:
Apply transformations recursively to nested structures (arrays/objects) using RecursionResolver:
use Phrity\Util\Transformer\Resolvers\RecursionResolver;
$recursiveTransformer = new RecursionResolver(new BasicTypeConverter());
$result = $recursiveTransformer->transform($nestedData);
String Conversion:
For universal string conversion, use StringResolver:
use Phrity\Util\Transformer\Resolvers\StringResolver;
$stringResolver = new StringResolver();
$stringOutput = $stringResolver->transform($anything);
Laravel Service Providers:
Register transformers as singletons in AppServiceProvider for reuse:
public function register()
{
$this->app->singleton(DateTimeConverter::class, fn() => new DateTimeConverter());
}
API Responses:
Use RecursionResolver with BasicTypeConverter to normalize Eloquent models:
$modelTransformer = new RecursionResolver(new BasicTypeConverter());
return response()->json($modelTransformer->transform($user));
Form Request Validation:
Decode flattened arrays (e.g., from API payloads) with FlattenDecoder:
$decoder = new FlattenDecoder('_');
$nestedData = $decoder->transform($request->all());
Error Handling:
Wrap throwables in ThrowableConverter for debugging:
$errorTransformer = new ThrowableConverter();
$errorMessage = $errorTransformer->transform($exception, Types::STRING);
Symfony Normalizers:
Integrate with Symfony’s NormalizerInterface via SymfonyNormalizerWrapper:
$normalizer = new \Symfony\Component\Serializer\Normalizer\PropertyNormalizer();
$wrapper = new SymfonyNormalizerWrapper($normalizer);
$arrayData = $wrapper->transform($object);
Type Mismatches:
canTransform() may return true for a target type, but transform() might fail if the input cannot logically be converted (e.g., converting a DateTime to Types::INTEGER).Recursion Depth:
RecursionResolver may hit PHP’s recursion limit for deeply nested structures.setMaxDepth() or limit recursion manually:
$recursiveTransformer->setMaxDepth(10);
JSON Decoding:
JsonDecoder defaults to decoding objects as stdClass. To force associative arrays, pass true:
$decoder = new JsonDecoder(true); // Decodes to array
Symfony Dependencies:
SymfonyNormalizerWrapper requires symfony/serializer and optionally symfony/property-access. Install manually:
composer require symfony/serializer symfony/property-access
Custom Types:
Type constants requires defining new constants in your codebase. Ensure consistency across transformers.Log Transformations:
Override transform() in a custom transformer to log inputs/outputs:
class DebugTransformer extends BasicTypeConverter {
public function transform($subject, $targetType = null) {
\Log::debug("Transforming: " . print_r($subject, true));
return parent::transform($subject, $targetType);
}
}
Check canTransform():
Always verify compatibility before transformation to avoid silent failures.
Custom Transformers:
Implement the TransformerInterface to create domain-specific transformers:
class MyCustomTransformer implements TransformerInterface {
public function canTransform($subject, $targetType = null): bool {
return is_a($subject, MyClass::class);
}
public function transform($subject, $targetType = null) {
return $subject->toArray();
}
}
Override Defaults:
Customize BasicTypeConverter defaults (e.g., force OBJECT to ARRAY):
$converter = new BasicTypeConverter([
Types::OBJECT => Types::ARRAY,
]);
Resolver Strategies: Combine resolvers for complex pipelines:
$pipeline = new ChainedResolver([
new FlattenDecoder('_'),
new RecursionResolver(new FirstMatchResolver([
new DateTimeConverter(),
new BasicTypeConverter(),
])),
]);
Performance: Cache transformers for repeated use (e.g., in Laravel’s service container):
$this->app->singleton(RecursionResolver::class, fn() => new RecursionResolver(new BasicTypeConverter()));
Empty Strings:
BasicTypeConverter converts empty strings to false for booleans and 0 for integers/numbers. Handle edge cases explicitly if needed.
Null Handling:
canTransform(null) may return true for some transformers (e.g., BasicTypeConverter), but transform(null) will return null or an empty structure. Validate outputs for null inputs.
DateTime Formatting:
DateTimeConverter uses ISO 8601 (c) by default. Customize with dateTimeFormat:
new DateTimeConverter(dateTimeFormat: 'Y-m-d H:i:s');
How can I help you explore Laravel packages today?