diff --git a/CHANGELOG.md b/CHANGELOG.md index fed09e75..b8f2f8e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,25 +2,28 @@ ## 0.5.4 -* Add a caching layer for `TypeResolver` -* `TraversableToTraversableTransformer` now accepts `Generator` as a target type -* Constructor arguments +* perf: Add a caching layer for `TypeResolver` +* feat: `TraversableToTraversableTransformer` now accepts `Generator` as a + target type +* feat: Constructor arguments +* test: Custom transformer ## 0.5.3 -* Use `MappingFactoryInterface` everywhere instead of `MappingFactory` -* Move some `TypeUtil` methods to `TypeResolver` for optimization opportunities -* use `ObjectCacheFactory` to generate `ObjectCache` instances -* Update `MapperFactory` to reflect framework usage -* Use property info caching in non-framework usage -* Add mapping caching -* Change console command to use `rekalogika` prefix +* refactor: Use `MappingFactoryInterface` everywhere instead of `MappingFactory` +* perf: Move some `TypeUtil` methods to `TypeResolver` for optimization + opportunities +* refactor: use `ObjectCacheFactory` to generate `ObjectCache` instances +* chore: Update `MapperFactory` to reflect framework usage +* perf: Use property info caching in non-framework usage +* perf: Add mapping caching +* style: Change console command to use `rekalogika` prefix ## 0.5.2 -* Support Symfony 7 -* Fix service definition for `MapperFactory` +* feat: Support Symfony 7 +* fix: Fix service definition for `MapperFactory` ## 0.5.0 -* Initial release +* build: Initial release diff --git a/composer.json b/composer.json index db6fa5fe..02331a42 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,8 @@ "symfony/var-dumper": "^6.4 || ^7.0", "vimeo/psalm": "^5.18", "dave-liddament/php-language-extensions": "^0.6.0", - "dave-liddament/phpstan-php-language-extensions": "^0.5.0" + "dave-liddament/phpstan-php-language-extensions": "^0.5.0", + "brick/money": "^0.9.0" }, "autoload": { "psr-4": { diff --git a/tests/Common/AbstractIntegrationTest.php b/tests/Common/AbstractIntegrationTest.php index 3e23bdb3..a81595a6 100644 --- a/tests/Common/AbstractIntegrationTest.php +++ b/tests/Common/AbstractIntegrationTest.php @@ -14,6 +14,7 @@ namespace Rekalogika\Mapper\Tests\Common; use PHPUnit\Framework\TestCase; +use Rekalogika\Mapper\Contracts\TransformerInterface; use Rekalogika\Mapper\MainTransformer; use Rekalogika\Mapper\MapperInterface; @@ -25,8 +26,18 @@ abstract class AbstractIntegrationTest extends TestCase public function setUp(): void { - $this->factory = new MapperTestFactory(); + $this->factory = new MapperTestFactory( + additionalTransformers: $this->getAdditionalTransformers() + ); $this->mapper = $this->factory->getMapper(); $this->mainTransformer = $this->factory->getMainTransformer(); } + + /** + * @return array + */ + protected function getAdditionalTransformers(): array + { + return []; + } } diff --git a/tests/Fixtures/Money/MoneyDto.php b/tests/Fixtures/Money/MoneyDto.php new file mode 100644 index 00000000..78d8dea1 --- /dev/null +++ b/tests/Fixtures/Money/MoneyDto.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\Money; + +class MoneyDto +{ + public function __construct( + private string $amount, + private string $currency, + ) { + } + + public function getAmount(): string + { + return $this->amount; + } + + public function getCurrency(): string + { + return $this->currency; + } +} diff --git a/tests/Fixtures/Transformer/MoneyToMoneyDtoTransformer.php b/tests/Fixtures/Transformer/MoneyToMoneyDtoTransformer.php new file mode 100644 index 00000000..c0065bed --- /dev/null +++ b/tests/Fixtures/Transformer/MoneyToMoneyDtoTransformer.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE file + * that was distributed with this source code. + */ + +namespace Rekalogika\Mapper\Tests\Fixtures\Transformer; + +use Brick\Money\Money; +use Rekalogika\Mapper\Contracts\TransformerInterface; +use Rekalogika\Mapper\Contracts\TypeMapping; +use Rekalogika\Mapper\Exception\InvalidArgumentException; +use Rekalogika\Mapper\Tests\Fixtures\Money\MoneyDto; +use Rekalogika\Mapper\Util\TypeCheck; +use Rekalogika\Mapper\Util\TypeFactory; +use Symfony\Component\PropertyInfo\Type; + +class MoneyToMoneyDtoTransformer implements TransformerInterface +{ + // This tells the library that this transformer supports the transformation + // from the Money object to the MoneyDto object, and vice versa. + // + // The TypeFactory methods are convenience methods for creating the + // PropertyInfo Type objects. + + public function getSupportedTransformation(): iterable + { + + yield new TypeMapping( + TypeFactory::objectOfClass(Money::class), + TypeFactory::objectOfClass(MoneyDto::class) + ); + + yield new TypeMapping( + TypeFactory::objectOfClass(MoneyDto::class), + TypeFactory::objectOfClass(Money::class) + ); + } + + // This method is called when the mapper is trying to transform Money to + // MoneyDto, and vice versa. + // + // The $source and $target parameters are the source and target objects, + // respectively. $target is usually null, unless there is already an + // existing value in the target object. + // + // $sourceType and $targetType are the types of the source and target, in + // the form of PropertyInfo Type object. + // + // The TypeCheck class is a convenience class for verifying the type + // specified by a Type object. + + public function transform( + mixed $source, + mixed $target, + Type $sourceType, + Type $targetType, + array $context + ): mixed { + if ( + $source instanceof Money + && TypeCheck::isObjectOfType($targetType, MoneyDto::class) + ) { + return new MoneyDto( + amount: $source->getAmount()->__toString(), + currency: $source->getCurrency()->getCurrencyCode(), + ); + } + + if ( + $source instanceof MoneyDto + && TypeCheck::isObjectOfType($targetType, Money::class) + ) { + return Money::of( + $source->getAmount(), + $source->getCurrency() + ); + } + + throw new InvalidArgumentException('Unsupported transformation'); + } +} diff --git a/tests/IntegrationTest/CustomTransformerTest.php b/tests/IntegrationTest/CustomTransformerTest.php new file mode 100644 index 00000000..f032d02c --- /dev/null +++ b/tests/IntegrationTest/CustomTransformerTest.php @@ -0,0 +1,47 @@ + + * + * 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 Brick\Money\Money; +use Rekalogika\Mapper\Tests\Common\AbstractIntegrationTest; +use Rekalogika\Mapper\Tests\Fixtures\Money\MoneyDto; +use Rekalogika\Mapper\Tests\Fixtures\Transformer\MoneyToMoneyDtoTransformer; + +class CustomTransformerTest extends AbstractIntegrationTest +{ + protected function getAdditionalTransformers(): array + { + return [ + 'MoneyToMoneyDtoTransformer' => new MoneyToMoneyDtoTransformer(), + ]; + } + + public function testMoneyToMoneyDto(): void + { + $money = Money::of("100000.00", 'IDR'); + $moneyDto = $this->mapper->map($money, MoneyDto::class); + + $this->assertSame('100000.00', $moneyDto->getAmount()); + $this->assertSame('IDR', $moneyDto->getCurrency()); + } + + public function testMoneyDtoToMoney(): void + { + $moneyDto = new MoneyDto('100000.00', 'IDR'); + $money = $this->mapper->map($moneyDto, Money::class); + + $this->assertSame('100000.00', $money->getAmount()->__toString()); + $this->assertSame('IDR', $money->getCurrency()->getCurrencyCode()); + } +}