Skip to content

Commit

Permalink
the feature
Browse files Browse the repository at this point in the history
  • Loading branch information
priyadi committed May 1, 2024
1 parent 790880f commit d72143f
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 2 deletions.
19 changes: 19 additions & 0 deletions src/Attribute/AllowDelete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?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\Attribute;

#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
final readonly class AllowDelete
{
}
7 changes: 7 additions & 0 deletions src/Transformer/Implementation/ObjectToObjectTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Rekalogika\Mapper\Transformer\Implementation;

use Psr\Container\ContainerInterface;
use Rekalogika\Mapper\Attribute\AllowDelete;
use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Context\MapperOptions;
use Rekalogika\Mapper\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -547,6 +548,12 @@ private function transformValue(
$guessedSourceType = TypeGuesser::guessTypeFromVariable($sourcePropertyValue);
$sourceType = $propertyMapping->getCompatibleSourceType($guessedSourceType);

// add AllowDelete to context if target allows deletion

if ($propertyMapping->targetAllowsDelete()) {
$context = $context->with(new AllowDelete());
}

// transform the value

/** @var mixed */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Rekalogika\Mapper\Attribute\AllowDelete;
use Rekalogika\Mapper\CollectionInterface;
use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -99,6 +100,7 @@ public function transform(
context: $context,
);
}

return $this->eagerTransform(
source: $source,
target: $target,
Expand Down Expand Up @@ -147,6 +149,14 @@ private function eagerTransform(
target: $target,
);

// determine if target allows deletion

$allowDelete = $context(AllowDelete::class) !== null;

if ($allowDelete) {
$context = $context->without(AllowDelete::class);
}

// Transform the source

$transformed = $this->transformTraversableSource(
Expand All @@ -156,12 +166,33 @@ private function eagerTransform(
context: $context,
);

if ($allowDelete) {
$values = [];
} else {
$values = null;
}

foreach ($transformed as $key => $value) {
if ($key === null) {
$target[] = $value;
} else {
$target[$key] = $value;
}

if (is_array($values)) {
$values[] = $value;
}
}

// if target allows delete, remove values in the target that are not in
// the values array

if (is_array($values) && is_iterable($target)) {
foreach ($target as $key => $value) {
if (!in_array($value, $values, true)) {
unset($target[$key]);
}
}
}

return $target;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation;

use Rekalogika\Mapper\Attribute\AllowDelete;
use Rekalogika\Mapper\Attribute\InheritanceMap;
use Rekalogika\Mapper\CustomMapper\PropertyMapperResolverInterface;
use Rekalogika\Mapper\Proxy\Exception\ProxyNotSupportedException;
Expand Down Expand Up @@ -142,11 +143,17 @@ public function createObjectToObjectMetadata(
foreach ($propertiesToMap as $targetProperty) {
$sourceProperty = $targetProperty;

// determine if a property mapper is defined for the property

$serviceMethodSpecification = $this->propertyMapperResolver
->getPropertyMapper($sourceClass, $targetClass, $targetProperty);

// get reflection for target property

try {
$targetPropertyReflection = $targetReflection->getProperty($targetProperty);
} catch (\ReflectionException) {
$targetPropertyReflection = null;
}

// get read & write info for source and target properties

$sourceReadInfo = $this->propertyReadInfoExtractor
Expand All @@ -158,6 +165,14 @@ public function createObjectToObjectMetadata(
$targetSetterWriteInfo = $this
->getSetterWriteInfo($targetClass, $targetProperty);

// determine if target allows delete

if ($targetPropertyReflection === null) {
$targetAllowsDelete = false;
} else {
$targetAllowsDelete = count($targetPropertyReflection->getAttributes(AllowDelete::class)) > 0;
}

// process source read mode

if ($sourceReadInfo === null) {
Expand Down Expand Up @@ -368,6 +383,7 @@ public function createObjectToObjectMetadata(
propertyMapper: $serviceMethodSpecification,
sourceLazy: $sourceLazy,
targetCanAcceptNull: $targetCanAcceptNull,
targetAllowsDelete: $targetAllowsDelete,
);

$propertyMappings[] = $propertyMapping;
Expand Down
6 changes: 6 additions & 0 deletions src/Transformer/ObjectToObjectMetadata/PropertyMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function __construct(
private ?ServiceMethodSpecification $propertyMapper,
private bool $sourceLazy,
private bool $targetCanAcceptNull,
private bool $targetAllowsDelete,
) {
$this->sourceTypes = array_values($sourceTypes);
$this->targetTypes = array_values($targetTypes);
Expand Down Expand Up @@ -180,4 +181,9 @@ public function targetCanAcceptNull(): bool
{
return $this->targetCanAcceptNull;
}

public function targetAllowsDelete(): bool
{
return $this->targetAllowsDelete;
}
}
3 changes: 3 additions & 0 deletions tests/Fixtures/Remove/ObjectWithArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@

namespace Rekalogika\Mapper\Tests\Fixtures\Remove;

use Rekalogika\Mapper\Attribute\AllowDelete;

class ObjectWithArray
{
/**
* @var array<int,Member>
*/
#[AllowDelete]
public array $members = [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?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\Remove;

class ObjectWithArrayWithoutAllowDeleteAttribute
{
/**
* @var array<int,Member>
*/
public array $members = [];
}
26 changes: 26 additions & 0 deletions tests/IntegrationTest/RemoveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Rekalogika\Mapper\Tests\Fixtures\Remove\MemberRepository;
use Rekalogika\Mapper\Tests\Fixtures\Remove\ObjectWithArray;
use Rekalogika\Mapper\Tests\Fixtures\Remove\ObjectWithArrayDto;
use Rekalogika\Mapper\Tests\Fixtures\Remove\ObjectWithArrayWithoutAllowDeleteAttribute;

/** @psalm-suppress MissingConstructor */
class RemoveTest extends FrameworkTestCase
Expand Down Expand Up @@ -63,6 +64,7 @@ public function testRemove(): void
$objectWithArray->members[] = $this->repository->get('1');
$objectWithArray->members[] = $this->repository->get('2');
$objectWithArray->members[] = $this->repository->get('3');
$this->assertCount(3, $objectWithArray->members);

$this->mapper->map($objectWithArrayDto, $objectWithArray);

Expand All @@ -72,4 +74,28 @@ public function testRemove(): void
$this->assertSame($this->repository->get('1'), $objectWithArray->members[0]);
$this->assertSame($this->repository->get('2'), $objectWithArray->members[1]);
}

public function testNoRemovalWithoutAllowDeleteAttribute(): void
{
$objectWithArrayDto = new ObjectWithArrayDto();
$objectWithArrayDto->members[] = new MemberDto('1');
$objectWithArrayDto->members[] = new MemberDto('2');
// 3 is missing, and this should remove 3 from the target object

$objectWithArray = new ObjectWithArrayWithoutAllowDeleteAttribute();
$objectWithArray->members[] = $this->repository->get('1');
$objectWithArray->members[] = $this->repository->get('2');
$objectWithArray->members[] = $this->repository->get('3');
$this->assertCount(3, $objectWithArray->members);

$this->mapper->map($objectWithArrayDto, $objectWithArray);

$this->assertCount(3, $objectWithArray->members);
$this->assertSame('1', $objectWithArray->members[0]->getId());
$this->assertSame('2', $objectWithArray->members[1]->getId());
$this->assertSame('3', $objectWithArray->members[2]->getId());
$this->assertSame($this->repository->get('1'), $objectWithArray->members[0]);
$this->assertSame($this->repository->get('2'), $objectWithArray->members[1]);
$this->assertSame($this->repository->get('3'), $objectWithArray->members[2]);
}
}

0 comments on commit d72143f

Please sign in to comment.