Skip to content

Commit

Permalink
feat: Uses transformer class names as service IDs for easier decoration.
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi committed Jan 16, 2024
1 parent d89e617 commit 99ad589
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* fix: Add variance safeguard.
* dx: Clarity.
* style: Exception messages.
* feat: Uses transformer class names as service IDs for easier decoration.

## 0.5.12

Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,15 @@ Other mappers that were considered:

[MicroMapper](https://github.com/SymfonyCasts/micro-mapper/) is a mapper that
requires you to write the mapping code yourself. It is all manual work, but
still working under the mapping framwork, and should be suitable for our
still working within the mapping framework, and should be suitable for our
purpose, as long as we are willing to write the mapping code ourselves. The
mapping code also supports delegating to the main mapper, unlike
AutoMapper-Plus. However, we were way past of contemplating whether to do it
manually, so we did not consider it further.
AutoMapper-Plus. However, at this point, we were way past of contemplating
whether to do it manually, so we did not consider it further.

[Pull request for a future Symfony Mapper](https://github.com/symfony/symfony/pull/51741).
In the form that I saw it, it was too simplistic, and does not provide any
extension points. I (@priyadi) did provide some feedback in the pull request.

## License

MIT
26 changes: 13 additions & 13 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,52 +88,52 @@
# transformers

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

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

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

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

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

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

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

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

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

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

$services
->set('rekalogika.mapper.transformer.object_to_object', ObjectToObjectTransformer::class)
->set(ObjectToObjectTransformer::class)
->args([
'$propertyListExtractor' => service('rekalogika.mapper.property_info'),
'$propertyTypeExtractor' => service('rekalogika.mapper.property_info'),
Expand All @@ -145,11 +145,11 @@
->tag('rekalogika.mapper.transformer', ['priority' => -900]);

$services
->set('rekalogika.mapper.transformer.null', NullTransformer::class)
->set(NullTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -950]);

$services
->set('rekalogika.mapper.transformer.copy', CopyTransformer::class)
->set(CopyTransformer::class)
->tag('rekalogika.mapper.transformer', ['priority' => -1000]);


Expand Down
47 changes: 47 additions & 0 deletions src/Transformer/AbstractTransformerDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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;

use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface;
use Symfony\Component\PropertyInfo\Type;

abstract class AbstractTransformerDecorator implements TransformerInterface
{
public function __construct(
private TransformerInterface $decorated,
) {
}

public function transform(
mixed $source,
mixed $target,
?Type $sourceType,
?Type $targetType,
Context $context
): mixed {
return $this->decorated->transform(
$source,
$target,
$sourceType,
$targetType,
$context
);
}

public function getSupportedTransformation(): iterable
{
return $this->decorated->getSupportedTransformation();
}
}
6 changes: 6 additions & 0 deletions tests/Common/MapperTestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Rekalogika\Mapper\MainTransformer\MainTransformer;
use Rekalogika\Mapper\MapperFactory\MapperFactory;
use Rekalogika\Mapper\Mapping\MappingFactoryInterface;
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface;
use Rekalogika\Mapper\TransformerRegistry\TransformerRegistryInterface;
use Rekalogika\Mapper\TypeResolver\TypeResolverInterface;

Expand Down Expand Up @@ -45,4 +46,9 @@ public function getTransformerRegistry(): TransformerRegistryInterface
{
return parent::getTransformerRegistry();
}

public function getScalarToScalarTransformer(): TransformerInterface
{
return parent::getScalarToScalarTransformer();
}
}
49 changes: 37 additions & 12 deletions tests/Common/TestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,21 @@

namespace Rekalogika\Mapper\Tests\Common;

use Rekalogika\Mapper\Mapping\MappingFactoryInterface;
use Rekalogika\Mapper\MethodMapper\ClassMethodTransformer;
use Rekalogika\Mapper\RekalogikaMapperBundle;
use Rekalogika\Mapper\Transformer\ArrayToObjectTransformer;
use Rekalogika\Mapper\Transformer\CopyTransformer;
use Rekalogika\Mapper\Transformer\DateTimeTransformer;
use Rekalogika\Mapper\Transformer\InheritanceMapTransformer;
use Rekalogika\Mapper\Transformer\NullTransformer;
use Rekalogika\Mapper\Transformer\ObjectToArrayTransformer;
use Rekalogika\Mapper\Transformer\ObjectToObjectTransformer;
use Rekalogika\Mapper\Transformer\ObjectToStringTransformer;
use Rekalogika\Mapper\Transformer\ScalarToScalarTransformer;
use Rekalogika\Mapper\Transformer\StringToBackedEnumTransformer;
use Rekalogika\Mapper\Transformer\TraversableToArrayAccessTransformer;
use Rekalogika\Mapper\Transformer\TraversableToTraversableTransformer;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -58,21 +72,32 @@ public static function getServiceIds(): iterable
yield 'rekalogika.mapper.property_info';
yield 'rekalogika.mapper.cache.property_info';
yield 'rekalogika.mapper.property_info.cache';
yield 'rekalogika.mapper.transformer.scalar_to_scalar';
yield 'rekalogika.mapper.transformer.datetime';
yield 'rekalogika.mapper.transformer.string_to_backed_enum';
yield 'rekalogika.mapper.transformer.object_to_string';
yield 'rekalogika.mapper.transformer.traversable_to_arrayaccess';
yield 'rekalogika.mapper.transformer.traversable_to_traversable';
yield 'rekalogika.mapper.transformer.object_to_array';
yield 'rekalogika.mapper.transformer.array_to_object';
yield 'rekalogika.mapper.transformer.object_to_object';
yield 'rekalogika.mapper.transformer.null';
yield 'rekalogika.mapper.type_resolver';

yield ScalarToScalarTransformer::class;
yield DateTimeTransformer::class;
yield StringToBackedEnumTransformer::class;
yield ClassMethodTransformer::class;
yield ObjectToStringTransformer::class;
yield InheritanceMapTransformer::class;
yield TraversableToArrayAccessTransformer::class;
yield TraversableToTraversableTransformer::class;
yield ObjectToArrayTransformer::class;
yield ArrayToObjectTransformer::class;
yield ObjectToObjectTransformer::class;
yield NullTransformer::class;
yield CopyTransformer::class;

yield 'rekalogika.mapper.mapping_factory';
yield 'rekalogika.mapper.main_transformer';
yield MappingFactoryInterface::class;
yield 'rekalogika.mapper.mapping_factory.caching';
yield 'rekalogika.mapper.type_resolver';
yield 'rekalogika.mapper.type_resolver.caching';
yield 'rekalogika.mapper.transformer_registry';
yield 'rekalogika.mapper.method_mapper.sub_mapper';
yield 'rekalogika.mapper.object_cache_factory';
yield 'rekalogika.mapper.mapper';
yield 'rekalogika.mapper.command.mapping';
yield 'rekalogika.mapper.command.try';
yield 'rekalogika.mapper.command.try_property';
}
}
40 changes: 40 additions & 0 deletions tests/Fixtures/TransformerOverride/OverrideTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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\Tests\Fixtures\TransformerOverride;

use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithArrayProperty;
use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithArrayPropertyDto;
use Rekalogika\Mapper\Transformer\AbstractTransformerDecorator;
use Rekalogika\Mapper\Transformer\Contracts\TransformerInterface;
use Rekalogika\Mapper\Transformer\Contracts\TypeMapping;
use Rekalogika\Mapper\Util\TypeFactory;

/**
* Forcing scalar to scalar transformer, erroneously
*/
class OverrideTransformer extends AbstractTransformerDecorator
{
public function __construct(TransformerInterface $transformer)
{
parent::__construct($transformer);
}

public function getSupportedTransformation(): iterable
{
yield new TypeMapping(
TypeFactory::objectOfClass(ObjectWithArrayProperty::class),
TypeFactory::objectOfClass(ObjectWithArrayPropertyDto::class),
);
}
}
41 changes: 41 additions & 0 deletions tests/IntegrationTest/TransformerOverrideTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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\Tests\IntegrationTest;

use Rekalogika\Mapper\Exception\InvalidArgumentException;
use Rekalogika\Mapper\Tests\Common\AbstractIntegrationTest;
use Rekalogika\Mapper\Tests\Fixtures\ArrayLike\ObjectWithArrayProperty;
use Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithArrayPropertyDto;
use Rekalogika\Mapper\Tests\Fixtures\TransformerOverride\OverrideTransformer;
use Rekalogika\Mapper\Transformer\ScalarToScalarTransformer;

class TransformerOverrideTest extends AbstractIntegrationTest
{
protected function getAdditionalTransformers(): array
{
$scalarToScalarTransformer = new ScalarToScalarTransformer();

return [
OverrideTransformer::class => new OverrideTransformer($scalarToScalarTransformer),
];
}

public function testTransformerOverride(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Source must be scalar');
$source = new ObjectWithArrayProperty();
$target = $this->mapper->map($source, ObjectWithArrayPropertyDto::class);
}
}

0 comments on commit 99ad589

Please sign in to comment.