Skip to content

Commit

Permalink
feat: Map(property: null) ignores the mapping (#239)
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi authored Oct 16, 2024
1 parent 431c16b commit 20195b1
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 14 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# CHANGELOG

## 1.11.1
## 1.13.0

* feat: `Map(property: null)` ignores the mapping

## 1.12.0

* refactor: refactor `ObjectToObjectTransformer` for future optimization
* fix: remove unalterable check in `ObjectProcessor` to allow transformers to
Expand Down
2 changes: 1 addition & 1 deletion src/Attribute/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* @param class-string|null $class
*/
public function __construct(
public string $property,
public ?string $property = null,
public ?string $class = null,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function getPropertiesToMap(
$targetProperties = $this->listProperties($targetClass);

$targetPropertyToSourceProperty = [];
$skippedTargetProperties = [];

foreach ($targetProperties as $targetProperty) {
$sourceProperty = $this->determinePairedProperty(
Expand All @@ -47,6 +48,11 @@ class: $targetClass,
pairedClassProperties: $sourceProperties,
);

if ($sourceProperty === null) {
$skippedTargetProperties[$targetProperty] = true;
continue;
}

$targetPropertyToSourceProperty[$targetProperty] = $sourceProperty;
}

Expand All @@ -58,6 +64,18 @@ class: $sourceClass,
pairedClassProperties: $targetProperties,
);

if (isset($skippedTargetProperties[$targetProperty])) {
continue;
}

if ($targetProperty === null) {
if (isset($targetPropertyToSourceProperty[$sourceProperty])) {
unset($targetPropertyToSourceProperty[$sourceProperty]);
}

continue;
}

$targetPropertyToSourceProperty[$targetProperty] = $sourceProperty;
}

Expand Down Expand Up @@ -93,7 +111,7 @@ private function determinePairedProperty(
string $property,
string $pairedClass,
array $pairedClassProperties,
): string {
): ?string {
$attributes = ClassUtil::getPropertyAttributes(
class: $class,
property: $property,
Expand All @@ -112,6 +130,10 @@ class: $class,
if (\count($attributesWithClass) >= 1) {
$pairedProperty = $attributesWithClass[0]->property;

if ($pairedProperty === null) {
return null;
}

if (
!$this->isPropertyPath($pairedProperty)
&& !\in_array($pairedProperty, $pairedClassProperties, true)
Expand All @@ -135,7 +157,25 @@ class: $class,
);

if (\count($attributesWithoutClass) >= 1) {
return $attributesWithoutClass[0]->property;
$pairedProperty = $attributesWithoutClass[0]->property;

if ($pairedProperty === null) {
return null;
}

if (
!$this->isPropertyPath($pairedProperty)
&& !\in_array($pairedProperty, $pairedClassProperties, true)
) {
throw new PairedPropertyNotFoundException(
class: $class,
property: $property,
pairedClass: $pairedClass,
pairedProperty: $pairedProperty,
);
}

return $pairedProperty;
}

// if not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface PropertyMappingResolverInterface
/**
* @param class-string $sourceClass
* @param class-string $targetClass
* @return list<array{string,string}>
* @return list<array{?string,string}>
*/
public function getPropertiesToMap(
string $sourceClass,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ public function createObjectToObjectMetadata(
$serviceMethodSpecification = $this->propertyMapperResolver
->getPropertyMapper($sourceClass, $targetClass, $targetProperty);

// if source property is null, then skip

if ($sourceProperty === null) {
continue;
}

// generate source & target property metadata

$sourcePropertyMetadata = $this->propertyMetadataFactory
Expand Down
36 changes: 27 additions & 9 deletions tests/config/rekalogika-mapper/generated-mappings.php
Original file line number Diff line number Diff line change
Expand Up @@ -538,59 +538,77 @@
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on lines 92, 112
// tests/src/IntegrationTest/MapAttributeTest.php on lines 95, 115
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\OtherObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 52
// tests/src/IntegrationTest/MapAttributeTest.php on line 55
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\ObjectExtendingSomeObjectDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 72
// tests/src/IntegrationTest/MapAttributeTest.php on line 75
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\ObjectOverridingSomeObjectDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 32
// tests/src/IntegrationTest/MapAttributeTest.php on line 35
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 141
// tests/src/IntegrationTest/MapAttributeTest.php on line 161
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectSkippingMappingDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 144
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithInvalidTargetDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 132
// tests/src/IntegrationTest/MapAttributeTest.php on line 151
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithSamePropertyNameDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 135
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithUnpromotedConstructorDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 122
// tests/src/IntegrationTest/MapAttributeTest.php on line 125
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\ObjectExtendingOtherObject::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 102
// tests/src/IntegrationTest/MapAttributeTest.php on line 105
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\OtherObject::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on lines 42, 62, 82
// tests/src/IntegrationTest/MapAttributeTest.php on lines 45, 65, 85
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapAttributeTest.php on line 171
source: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectSkippingMapping::class,
target: \Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithSamePropertyNameDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/MapPropertyPathTest.php on line 297
source: \Rekalogika\Mapper\Tests\Fixtures\MapPropertyPathDto\BookDto::class,
Expand Down
38 changes: 38 additions & 0 deletions tests/src/Fixtures/MapAttribute/SomeObjectSkippingMapping.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?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\MapAttribute;

use Rekalogika\Mapper\Attribute\Map;

class SomeObjectSkippingMapping
{
#[Map(property: null)]
public ?string $sourcePropertyA = null;

#[Map(property: null)]
public ?string $sourcePropertyB = null;

#[Map(property: null)]
public ?string $sourcePropertyC = null;

public static function preinitialized(): self
{
$object = new self();
$object->sourcePropertyA = 'sourcePropertyA';
$object->sourcePropertyB = 'sourcePropertyB';
$object->sourcePropertyC = 'sourcePropertyC';

return $object;
}
}
29 changes: 29 additions & 0 deletions tests/src/Fixtures/MapAttribute/SomeObjectSkippingMappingDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?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\MapAttribute;

use Rekalogika\Mapper\Attribute\Map;

class SomeObjectSkippingMappingDto
{
#[Map(property: null)]
public ?string $sourcePropertyA = null;

#[Map(property: null)]
public ?string $sourcePropertyB = null;

#[Map(property: null)]
public ?string $sourcePropertyC = null;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?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\MapAttribute;

class SomeObjectWithSamePropertyNameDto
{
public ?string $sourcePropertyA = null;

public ?string $sourcePropertyB = null;

public ?string $sourcePropertyC = null;

}
33 changes: 33 additions & 0 deletions tests/src/IntegrationTest/MapAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\OtherObject;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObject;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectDto;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectSkippingMapping;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectSkippingMappingDto;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithInvalidTargetDto;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithSamePropertyNameDto;
use Rekalogika\Mapper\Tests\Fixtures\MapAttribute\SomeObjectWithUnpromotedConstructorDto;
use Rekalogika\Mapper\Transformer\Exception\PairedPropertyNotFoundException;

Expand Down Expand Up @@ -141,4 +144,34 @@ public function testMapAttributeWithInvalidProperty(): void
$target = $this->mapper->map($source, SomeObjectWithInvalidTargetDto::class);

}

public function testToSameProperty(): void
{
$source = SomeObject::preinitialized();
$target = $this->mapper->map($source, SomeObjectWithSamePropertyNameDto::class);

$this->assertEquals('sourcePropertyA', $target->sourcePropertyA);
$this->assertEquals('sourcePropertyB', $target->sourcePropertyB);
$this->assertEquals('sourcePropertyC', $target->sourcePropertyC);
}

public function testToSamePropertyButUnmapped(): void
{
$source = SomeObject::preinitialized();
$target = $this->mapper->map($source, SomeObjectSkippingMappingDto::class);

$this->assertNull($target->sourcePropertyA);
$this->assertNull($target->sourcePropertyB);
$this->assertNull($target->sourcePropertyC);
}

public function testIgnoringFromSourceSide(): void
{
$source = SomeObjectSkippingMapping::preinitialized();
$target = $this->mapper->map($source, SomeObjectWithSamePropertyNameDto::class);

$this->assertNull($target->sourcePropertyA);
$this->assertNull($target->sourcePropertyB);
$this->assertNull($target->sourcePropertyC);
}
}

0 comments on commit 20195b1

Please sign in to comment.