Skip to content

Commit

Permalink
Merge pull request #28 from rekalogika:feat/arrayobjecttransformer
Browse files Browse the repository at this point in the history
Deprecate `ArrayToObjectTransformer` & `ObjectToArrayTransformer`, replace with `ArrayObjectTransformer`.
  • Loading branch information
priyadi committed Feb 20, 2024
2 parents 1a07a0e + e9b4c92 commit c38ecad
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 69 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* fix(`PresetMapping`): Support proxied classes, add tests.
* fix: Disallow proxy for objects with dynamic properties, including `stdClass`.
* feat: Dynamic properties (`stdClass` & co) on the target side.
* feat: Deprecate `ArrayToObjectTransformer` & `ObjectToArrayTransformer`,
replace with `ArrayObjectTransformer`.

## 1.0.0

Expand Down
46 changes: 26 additions & 20 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\Implementation\ChainEagerPropertiesResolver;
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\Implementation\DoctrineEagerPropertiesResolver;
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\Implementation\HeuristicsEagerPropertiesResolver;
use Rekalogika\Mapper\Transformer\Implementation\ArrayToObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ArrayObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ClassMethodTransformer;
use Rekalogika\Mapper\Transformer\Implementation\CopyTransformer;
use Rekalogika\Mapper\Transformer\Implementation\DateTimeTransformer;
use Rekalogika\Mapper\Transformer\Implementation\NullToNullTransformer;
use Rekalogika\Mapper\Transformer\Implementation\NullTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectMapperTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToArrayTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToStringTransformer;
use Rekalogika\Mapper\Transformer\Implementation\PresetTransformer;
Expand All @@ -69,8 +68,6 @@
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyWriteInfoExtractorInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
Expand Down Expand Up @@ -122,7 +119,7 @@

$services
->set(ScalarToScalarTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -300]);
->tag('rekalogika.mapper.transformer', ['priority' => -350]);

$services
->set(ObjectMapperTransformer::class)
Expand All @@ -132,57 +129,52 @@
service('rekalogika.mapper.object_mapper.table_factory'),
service('rekalogika.mapper.object_mapper.resolver'),
])
->tag('rekalogika.mapper.transformer', ['priority' => -350]);
->tag('rekalogika.mapper.transformer', ['priority' => -400]);

$services
->set(DateTimeTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -400]);
->tag('rekalogika.mapper.transformer', ['priority' => -450]);

$services
->set(StringToBackedEnumTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -450]);
->tag('rekalogika.mapper.transformer', ['priority' => -500]);

$services
->set(SymfonyUidTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -500]);
->tag('rekalogika.mapper.transformer', ['priority' => -550]);

$services
->set(ObjectToStringTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -550]);
->tag('rekalogika.mapper.transformer', ['priority' => -600]);

$services
->set(PresetTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -600]);
->tag('rekalogika.mapper.transformer', ['priority' => -650]);

$services
->set(ClassMethodTransformer::class)
->args([
service('rekalogika.mapper.sub_mapper.factory'),
])
->tag('rekalogika.mapper.transformer', ['priority' => -650]);
->tag('rekalogika.mapper.transformer', ['priority' => -700]);

$services
->set(TraversableToArrayAccessTransformer::class)
->args([
service('rekalogika.mapper.array_like_metadata_factory')
])
->tag('rekalogika.mapper.transformer', ['priority' => -700]);
->tag('rekalogika.mapper.transformer', ['priority' => -750]);

$services
->set(TraversableToTraversableTransformer::class)
->args([
service('rekalogika.mapper.array_like_metadata_factory')
])
->tag('rekalogika.mapper.transformer', ['priority' => -750]);

$services
->set(ObjectToArrayTransformer::class)
->args([service(NormalizerInterface::class)])
->tag('rekalogika.mapper.transformer', ['priority' => -800]);

$services
->set(ArrayToObjectTransformer::class)
->args([service(DenormalizerInterface::class)])
->set(ArrayObjectTransformer::class)
->args([service('rekalogika.mapper.transformer.array_object.object_to_object_transformer')])
->tag('rekalogika.mapper.transformer', ['priority' => -850]);

$services
Expand Down Expand Up @@ -224,6 +216,20 @@
service('kernel')
]);

# special instance of object to object transformer for ArrayObjectTransformer

$services
->set(
'rekalogika.mapper.transformer.array_object.object_to_object_transformer',
ObjectToObjectTransformer::class
)
->args([
'$objectToObjectMetadataFactory' => service('rekalogika.mapper.object_to_object_metadata_factory'),
'$propertyMapperLocator' => tagged_locator('rekalogika.mapper.property_mapper'),
'$subMapperFactory' => service('rekalogika.mapper.sub_mapper.factory'),
'$proxyFactory' => service('rekalogika.mapper.proxy.factory'),
]);

# mapping cache warmer

$services
Expand Down
79 changes: 70 additions & 9 deletions src/MapperFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,18 @@
use Rekalogika\Mapper\Transformer\ArrayLikeMetadata\Implementation\ArrayLikeMetadataFactory;
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\EagerPropertiesResolverInterface;
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\Implementation\HeuristicsEagerPropertiesResolver;
use Rekalogika\Mapper\Transformer\Implementation\ArrayObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ArrayToObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ClassMethodTransformer;
use Rekalogika\Mapper\Transformer\Implementation\CopyTransformer;
use Rekalogika\Mapper\Transformer\Implementation\DateTimeTransformer;
use Rekalogika\Mapper\Transformer\Implementation\NullToNullTransformer;
use Rekalogika\Mapper\Transformer\Implementation\NullTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectMapperTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToArrayTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToObjectTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ObjectToStringTransformer;
use Rekalogika\Mapper\Transformer\Implementation\PresetTransformer;
use Rekalogika\Mapper\Transformer\Implementation\ScalarToScalarTransformer;
use Rekalogika\Mapper\Transformer\Implementation\StringToBackedEnumTransformer;
use Rekalogika\Mapper\Transformer\Implementation\SymfonyUidTransformer;
Expand Down Expand Up @@ -111,6 +114,7 @@ class MapperFactory

private ?Serializer $serializer = null;

private ?NullToNullTransformer $nullToNullTransformer = null;
private ?NullTransformer $nullTransformer = null;
private ?ObjectToObjectTransformer $objectToObjectTransformer = null;
private ?ObjectToStringTransformer $objectToStringTransformer = null;
Expand All @@ -119,12 +123,14 @@ class MapperFactory
private ?StringToBackedEnumTransformer $stringToBackedEnumTransformer = null;
private ?ArrayToObjectTransformer $arrayToObjectTransformer = null;
private ?ObjectToArrayTransformer $objectToArrayTransformer = null;
private ?ArrayObjectTransformer $arrayObjectTransformer = null;
private ?DateTimeTransformer $dateTimeTransformer = null;
private ?TraversableToArrayAccessTransformer $traversableToArrayAccessTransformer = null;
private ?TraversableToTraversableTransformer $traversableToTraversableTransformer = null;
private ?CopyTransformer $copyTransformer = null;
private ?ClassMethodTransformer $classMethodTransformer = null;
private ?SymfonyUidTransformer $symfonyUidTransformer = null;
private ?PresetTransformer $presetTransformer = null;

private CacheItemPoolInterface $propertyInfoExtractorCache;
private null|(PropertyInfoExtractorInterface&PropertyInitializableExtractorInterface) $propertyInfoExtractor = null;
Expand Down Expand Up @@ -321,6 +327,15 @@ private function getDenormalizer(): DenormalizerInterface
// transformers
//

protected function getNullToNullTransformer(): TransformerInterface
{
if (null === $this->nullToNullTransformer) {
$this->nullToNullTransformer = new NullToNullTransformer();
}

return $this->nullToNullTransformer;
}

protected function getNullTransformer(): TransformerInterface
{
if (null === $this->nullTransformer) {
Expand Down Expand Up @@ -385,6 +400,9 @@ protected function getStringToBackedEnumTransformer(): TransformerInterface
return $this->stringToBackedEnumTransformer;
}

/**
* @deprecated
*/
protected function getArrayToObjectTransformer(): TransformerInterface
{
if (null === $this->arrayToObjectTransformer) {
Expand All @@ -396,6 +414,9 @@ protected function getArrayToObjectTransformer(): TransformerInterface
return $this->arrayToObjectTransformer;
}

/**
* @deprecated
*/
protected function getObjectToArrayTransformer(): TransformerInterface
{
if (null === $this->objectToArrayTransformer) {
Expand All @@ -407,6 +428,20 @@ protected function getObjectToArrayTransformer(): TransformerInterface
return $this->objectToArrayTransformer;
}

protected function getArrayObjectTransformer(): TransformerInterface
{
$objectToObjectTransformer = $this->getObjectToObjectTransformer();
assert($objectToObjectTransformer instanceof ObjectToObjectTransformer);

if (null === $this->arrayObjectTransformer) {
$this->arrayObjectTransformer = new ArrayObjectTransformer(
$objectToObjectTransformer
);
}

return $this->arrayObjectTransformer;
}

protected function getDateTimeTransformer(): TransformerInterface
{
if (null === $this->dateTimeTransformer) {
Expand Down Expand Up @@ -473,6 +508,15 @@ protected function getSymfonyUidTransformer(): SymfonyUidTransformer
return $this->symfonyUidTransformer;
}

protected function getPresetTransformer(): PresetTransformer
{
if (null === $this->presetTransformer) {
$this->presetTransformer = new PresetTransformer();
}

return $this->presetTransformer;
}

//
// other services
//
Expand Down Expand Up @@ -525,7 +569,10 @@ protected function getArrayLikeMetadataFactory(): ArrayLikeMetadataFactoryInterf
*/
protected function getTransformersIterator(): iterable
{
yield 'NullToNullTransformer' => $this->getNullToNullTransformer();

yield from $this->additionalTransformers;

yield 'ScalarToScalarTransformer'
=> $this->getScalarToScalarTransformer();
yield 'ObjectMapperTransformer'
Expand All @@ -534,11 +581,6 @@ protected function getTransformersIterator(): iterable
=> $this->getDateTimeTransformer();
yield 'StringToBackedEnumTransformer'
=> $this->getStringToBackedEnumTransformer();
/**
* @psalm-suppress DeprecatedMethod
* @phpstan-ignore-next-line
*/
yield 'ClassMethodTransformer' => $this->getClassMethodTransformer();

if (class_exists(UuidFactory::class)) {
yield 'SymfonyUidTransformer'
Expand All @@ -547,14 +589,33 @@ protected function getTransformersIterator(): iterable

yield 'ObjectToStringTransformer'
=> $this->getObjectToStringTransformer();

yield 'PresetTransformer'
=> $this->getPresetTransformer();

/**
* @psalm-suppress DeprecatedMethod
* @phpstan-ignore-next-line
*/
yield 'ClassMethodTransformer' => $this->getClassMethodTransformer();

yield 'TraversableToArrayAccessTransformer'
=> $this->getTraversableToArrayAccessTransformer();
yield 'TraversableToTraversableTransformer'
=> $this->getTraversableToTraversableTransformer();
yield 'ObjectToArrayTransformer'
=> $this->getObjectToArrayTransformer();
yield 'ArrayToObjectTransformer'
=> $this->getArrayToObjectTransformer();

/**
* @psalm-suppress DeprecatedMethod
* @phpstan-ignore-next-line
*/
yield 'ObjectToArrayTransformer' => $this->getObjectToArrayTransformer();

/**
* @psalm-suppress DeprecatedMethod
* @phpstan-ignore-next-line
*/
yield 'ArrayToObjectTransformer' => $this->getArrayToObjectTransformer();

yield 'ObjectToObjectTransformer'
=> $this->getObjectToObjectTransformer();
yield 'NullTransformer'
Expand Down
87 changes: 87 additions & 0 deletions src/Transformer/Implementation/ArrayObjectTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

/*
* This file is part of rekalogika/mapper package.
*
* (c) Priyadi Iman Nurcahyo <https://rekalogika.dev>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

namespace Rekalogika\Mapper\Transformer\Implementation;

use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\MainTransformer\MainTransformerInterface;
use Rekalogika\Mapper\Transformer\MainTransformerAwareInterface;
use Rekalogika\Mapper\Transformer\TransformerInterface;
use Rekalogika\Mapper\Transformer\TypeMapping;
use Rekalogika\Mapper\Util\TypeCheck;
use Rekalogika\Mapper\Util\TypeFactory;
use Symfony\Component\PropertyInfo\Type;

/**
* Do object to array, array to object, and array to array transformation by
* converting array to stdClass & passing the task to ObjectToObjectTransformer.
*/
final readonly class ArrayObjectTransformer implements
TransformerInterface,
MainTransformerAwareInterface
{
public function __construct(
private ObjectToObjectTransformer $objectToObjectTransformer,
) {
}

public function withMainTransformer(MainTransformerInterface $mainTransformer): static
{
$objectToObjectTransformer = $this->objectToObjectTransformer
->withMainTransformer($mainTransformer);

return new self($objectToObjectTransformer);
}

public function transform(
mixed $source,
mixed $target,
?Type $sourceType,
?Type $targetType,
Context $context
): mixed {
$originalTargetType = $targetType;

if (TypeCheck::isArray($sourceType)) {
$source = (object) $source;
$sourceType = TypeFactory::objectOfClass(\stdClass::class);
}

if (TypeCheck::isArray($targetType)) {
$target = (object) $target;
$targetType = TypeFactory::objectOfClass(\stdClass::class);
}

/** @var mixed */
$result = $this->objectToObjectTransformer->transform(
source: $source,
target: $target,
sourceType: $sourceType,
targetType: $targetType,
context: $context
);

if (TypeCheck::isArray($originalTargetType)) {
return (array) $result;
}

return $result;
}

public function getSupportedTransformation(): iterable
{
yield new TypeMapping(TypeFactory::array(), TypeFactory::object(), true);
yield new TypeMapping(TypeFactory::object(), TypeFactory::array());
yield new TypeMapping(TypeFactory::array(), TypeFactory::array());
}
}
Loading

0 comments on commit c38ecad

Please sign in to comment.