Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate ArrayToObjectTransformer & ObjectToArrayTransformer, replace with ArrayObjectTransformer. #28

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading