Skip to content

Commit

Permalink
fix: fix list handling (#252)
Browse files Browse the repository at this point in the history
* add test for array mapping

* add test for array mapping

* fix: fix list handling

* fix

---------

Co-authored-by: Vladyslav Yarysh <[email protected]>
  • Loading branch information
priyadi and gabplch authored Nov 12, 2024
1 parent 3c40856 commit d117d4d
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 31 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 1.13.4

* fix: fix list handling

## 1.13.3

* fix: rethrow our exceptions in `writeTargetProperty`, fix confusing error
Expand Down
12 changes: 0 additions & 12 deletions src/Transformer/ArrayLikeMetadata/ArrayLikeMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,9 @@ public function __construct(
private array $targetMemberValueTypes,
private bool $sourceMemberKeyCanBeInt,
private bool $sourceMemberKeyCanBeString,
private bool $sourceMemberKeyCanBeIntOnly,
private bool $sourceMemberKeyCanBeOtherThanIntOrString,
private bool $targetMemberKeyCanBeInt,
private bool $targetMemberKeyCanBeString,
private bool $targetMemberKeyCanBeIntOnly,
private bool $targetMemberKeyCanBeOtherThanIntOrString,
private bool $targetMemberValueIsUntyped,
) {}
Expand Down Expand Up @@ -128,11 +126,6 @@ public function targetMemberKeyCanBeString(): bool
return $this->targetMemberKeyCanBeString;
}

public function targetMemberKeyCanBeIntOnly(): bool
{
return $this->targetMemberKeyCanBeIntOnly;
}

public function targetMemberKeyCanBeOtherThanIntOrString(): bool
{
return $this->targetMemberKeyCanBeOtherThanIntOrString;
Expand Down Expand Up @@ -163,11 +156,6 @@ public function sourceMemberKeyCanBeString(): bool
return $this->sourceMemberKeyCanBeString;
}

public function sourceMemberKeyCanBeIntOnly(): bool
{
return $this->sourceMemberKeyCanBeIntOnly;
}

public function sourceMemberKeyCanBeOtherThanIntOrString(): bool
{
return $this->sourceMemberKeyCanBeOtherThanIntOrString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function createArrayLikeMetadata(
$sourceMemberKeyTypeCanBeString = false;
$sourceMemberKeyTypeCanBeOtherThanIntOrString = false;

foreach ($sourceType->getCollectionKeyTypes() as $sourceMemberKeyType) {
foreach ($sourceMemberKeyTypes as $sourceMemberKeyType) {
if (TypeCheck::isInt($sourceMemberKeyType)) {
$sourceMemberKeyTypeCanBeInt = true;
} elseif (TypeCheck::isString($sourceMemberKeyType)) {
Expand Down Expand Up @@ -123,11 +123,9 @@ public function createArrayLikeMetadata(
targetMemberValueTypes: $targetMemberValueTypes,
sourceMemberKeyCanBeInt: $sourceMemberKeyTypeCanBeInt,
sourceMemberKeyCanBeString: $sourceMemberKeyTypeCanBeString,
sourceMemberKeyCanBeIntOnly: $sourceMemberKeyTypeCanBeInt && !$sourceMemberKeyTypeCanBeString,
sourceMemberKeyCanBeOtherThanIntOrString: $sourceMemberKeyTypeCanBeOtherThanIntOrString,
targetMemberKeyCanBeInt: $targetMemberKeyTypeCanBeInt,
targetMemberKeyCanBeString: $targetMemberKeyTypeCanBeString,
targetMemberKeyCanBeIntOnly: $targetMemberKeyTypeCanBeInt && !$targetMemberKeyTypeCanBeString,
targetMemberKeyCanBeOtherThanIntOrString: $targetMemberKeyTypeCanBeOtherThanIntOrString,
targetMemberValueIsUntyped: $targetMemberValueIsUntyped,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,25 @@ private function eagerTransform(
foreach ($transformed as $value) {
$target = $target->add($value);

if (\is_array($values)) {
if (\is_array($values)) { // allows delete
$values[] = $value;
}
}
} else {
foreach ($transformed as $key => $value) {
if ($key === null) {
$target[] = $value;
if (\is_array($target)) {
if (!\in_array($value, $target, true)) {
$target[] = $value;
}
} else {
$target[] = $value;
}
} else {
$target[$key] = $value;
}

if (\is_array($values)) {
if (\is_array($values)) { // allows delete
$values[] = $value;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Transformer/Model/LazyArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public function offsetGet(mixed $offset): mixed
[$key, $value] = $this->transformMember(
sourceMemberKey: $offset,
sourceMemberValue: $this->source[$offset],
targetIsList: false,
metadata: $this->metadata,
context: $this->context,
);
Expand Down Expand Up @@ -140,6 +141,7 @@ public function getIterator(): \Traversable
[$key, $value] = $this->transformMember(
sourceMemberKey: $sourceMemberKey,
sourceMemberValue: $sourceMemberValue,
targetIsList: false,
metadata: $this->metadata,
context: $this->context,
);
Expand Down
1 change: 1 addition & 0 deletions src/Transformer/Model/LazyList.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public function getIterator(): \Traversable
[, $value] = $this->transformMember(
sourceMemberKey: $sourceMemberKey,
sourceMemberValue: $sourceMemberValue,
targetIsList: true,
metadata: $this->metadata,
context: $this->context,
);
Expand Down
47 changes: 34 additions & 13 deletions src/Transformer/Trait/ArrayLikeTransformerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Transformer\ArrayLikeMetadata\ArrayLikeMetadata;
use Rekalogika\Mapper\Transformer\Model\AdderRemoverProxy;
use Rekalogika\Mapper\Transformer\Model\SplObjectStorageWrapper;
use Rekalogika\Mapper\Util\TypeCheck;
use Symfony\Component\PropertyInfo\Type;
Expand All @@ -38,6 +39,14 @@ private function transformTraversableSource(
$source = new SplObjectStorageWrapper($source);
}

$targetIsList =
(
\is_array($target)
&& $target !== []
&& array_is_list($target)
)
|| $target instanceof AdderRemoverProxy;

$i = 0;

/**
Expand All @@ -53,6 +62,7 @@ private function transformTraversableSource(
counter: $i,
sourceMemberKey: $sourceMemberKey,
sourceMemberValue: $sourceMemberValue,
targetIsList: $targetIsList,
target: $target,
metadata: $metadata,
context: $context,
Expand All @@ -71,6 +81,7 @@ private function transformTraversableSource(
private function transformMember(
mixed $sourceMemberKey,
mixed $sourceMemberValue,
bool $targetIsList,
ArrayLikeMetadata $metadata,
Context $context,
null|\ArrayAccess|array $target = null,
Expand All @@ -82,19 +93,19 @@ private function transformMember(
if (\is_string($sourceMemberKey)) {
// if the key is a string

if ($metadata->targetMemberKeyCanBeIntOnly()) {
if ($metadata->targetMemberKeyCanBeString()) {
// if target has string key type, we use the source key as
// the target key

$targetMemberKey = $sourceMemberKey;
$path = \sprintf('[%s]', $sourceMemberKey);
} elseif ($targetIsList || $metadata->targetMemberKeyCanBeInt()) {
// if target has int key type but the source has string key
// type, we discard the source key & use null key (i.e.
// $target[] = $value)

$targetMemberKey = null;
$path = \sprintf('[%d]', $counter ?? -1);
} elseif ($metadata->targetMemberKeyCanBeString()) {
// if target has string key type, we use the source key as
// the target key and let PHP cast it to string

$targetMemberKey = $sourceMemberKey;
$path = \sprintf('[%s]', $sourceMemberKey);
} else {
// otherwise, the target must be non-int & non-string, so we
// delegate the transformation to the main transformer
Expand All @@ -119,15 +130,25 @@ private function transformMember(
// if the key is an integer

if (
$metadata->targetMemberKeyCanBeInt()
|| $metadata->targetMemberKeyCanBeString()
$targetIsList
) {
// if the target has int or string key type, we use the
// source key as the target key, and let PHP cast it if
// needed
// if the target is a list, we don't use the source key as the
// target key

$targetMemberKey = null;
$path = \sprintf('[%d]', $counter ?? -1);
} elseif ($metadata->targetMemberKeyCanBeInt()) {
// if the target has int key type, we use the source key as the
// target key

$targetMemberKey = $sourceMemberKey;
$path = \sprintf('[%s]', $sourceMemberKey);
} elseif ($metadata->targetMemberKeyCanBeString()) {
// if the target has string key type, we cast source key to
// string

$targetMemberKey = (string) $sourceMemberKey;
$path = \sprintf('[%s]', $sourceMemberKey);
} else {
// otherwise, the target must be non-int & non-string, so we
// delegate the transformation to the main transformer
Expand Down Expand Up @@ -188,7 +209,7 @@ private function transformMember(
// Get the existing member value from the target

try {
if ($metadata->targetMemberKeyCanBeIntOnly()) {
if ($targetIsList) {
$targetMemberValue = null;
} elseif ($target !== null && $targetMemberKey !== null) {
/**
Expand Down
12 changes: 12 additions & 0 deletions tests/config/rekalogika-mapper/generated-mappings.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,18 @@
target: \Rekalogika\Mapper\Tests\Fixtures\ArrayLikeDto\ObjectWithTraversablePropertyDto::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/ArrayToArrayTest.php on line 43
source: \Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithList::class,
target: \Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithList::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/ArrayToArrayTest.php on line 30
source: \Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithListWithAllowDelete::class,
target: \Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithListWithAllowDelete::class
);

$mappingCollection->addObjectMapping(
// tests/src/IntegrationTest/AttributeTest.php on line 42
source: \Rekalogika\Mapper\Tests\Fixtures\Attribute\SomeObject::class,
Expand Down
20 changes: 20 additions & 0 deletions tests/src/Fixtures/Array/ObjectWithList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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\Array;

class ObjectWithList
{
/** @var array<mixed,mixed> */
public array $array = [];
}
23 changes: 23 additions & 0 deletions tests/src/Fixtures/Array/ObjectWithListWithAllowDelete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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\Array;

use Rekalogika\Mapper\Attribute\AllowDelete;

class ObjectWithListWithAllowDelete
{
/** @var array<mixed,mixed> */
#[AllowDelete]
public array $array = [];
}
47 changes: 47 additions & 0 deletions tests/src/IntegrationTest/ArrayToArrayTest.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\Tests\IntegrationTest;

use Rekalogika\Mapper\Tests\Common\FrameworkTestCase;
use Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithList;
use Rekalogika\Mapper\Tests\Fixtures\Array\ObjectWithListWithAllowDelete;

class ArrayToArrayTest extends FrameworkTestCase
{
public function testListToListWithAllowDelete(): void
{
$target = new ObjectWithListWithAllowDelete();
$target->array = ['foo', 'bar'];

$source = new ObjectWithListWithAllowDelete();
$source->array = ['baz'];

$this->mapper->map($source, $target);

$this->assertEquals(['baz'], $target->array);
}

public function testListToListWithoutAllowDelete(): void
{
$target = new ObjectWithList();
$target->array = ['foo', 'bar'];

$source = new ObjectWithList();
$source->array = ['baz'];

$this->mapper->map($source, $target);

$this->assertEquals(['foo', 'bar', 'baz'], $target->array);
}
}

0 comments on commit d117d4d

Please sign in to comment.