Skip to content

Commit

Permalink
Merge pull request #33 from rekalogika:work/stdclass
Browse files Browse the repository at this point in the history
feat: `stdClass` to `stdClass` mapping should work correctly.
  • Loading branch information
priyadi committed Feb 20, 2024
2 parents d094695 + e578163 commit 5f787bb
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* fix: Fix dynamic properties in Symfony profiler panel.
* fix: Fix `PresetTransformer`.
* fix: mapping to object extending `stdClass` to property with no setter.
* feat: `stdClass` to `stdClass` mapping should work correctly.

## 1.0.0

Expand Down
35 changes: 35 additions & 0 deletions src/Transformer/Implementation/ObjectToObjectTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ public function transform(
// map properties if it is not a proxy

if (!$canUseTargetProxy) {
// map dynamic properties if both are stdClass or allow dynamic
// properties

if (
$objectToObjectMetadata->sourceAllowsDynamicProperties()
&& $objectToObjectMetadata->targetAllowsDynamicProperties()
) {
$this->mapDynamicProperties(
source: $source,
target: $target,
objectToObjectMetadata: $objectToObjectMetadata,
context: $context
);
}

$this->readSourceAndWriteTarget(
source: $source,
target: $target,
Expand Down Expand Up @@ -552,6 +567,26 @@ private function transformValue(
return $targetPropertyValue;
}

private function mapDynamicProperties(
object $source,
object $target,
ObjectToObjectMetadata $objectToObjectMetadata,
Context $context
): void {
$sourceProperties = $objectToObjectMetadata->getSourceProperties();

/** @var mixed $sourcePropertyValue */
foreach (get_object_vars($source) as $sourceProperty => $sourcePropertyValue) {
if (!in_array($sourceProperty, $sourceProperties, true)) {
try {
$target->{$sourceProperty} = $sourcePropertyValue;
} catch (\Error) {
// ignore
}
}
}
}

public function getSupportedTransformation(): iterable
{
yield new TypeMapping(TypeFactory::object(), TypeFactory::object(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public function createObjectToObjectMetadata(

// iterate over properties to map

$effectivePropertiesToMap = [];

foreach ($propertiesToMap as $targetProperty) {
$sourceProperty = $targetProperty;

Expand Down Expand Up @@ -349,6 +351,7 @@ public function createObjectToObjectMetadata(
);

$propertyMappings[] = $propertyMapping;
$effectivePropertiesToMap[] = $targetProperty;
}

$objectToObjectMetadata = new ObjectToObjectMetadata(
Expand All @@ -357,6 +360,7 @@ public function createObjectToObjectMetadata(
providedTargetClass: $providedTargetClass,
sourceAllowsDynamicProperties: $sourceAllowsDynamicProperties,
targetAllowsDynamicProperties: $targetAllowsDynamicProperties,
sourceProperties: $effectivePropertiesToMap,
allPropertyMappings: $propertyMappings,
instantiable: $instantiable,
cloneable: $cloneable,
Expand Down
16 changes: 14 additions & 2 deletions src/Transformer/ObjectToObjectMetadata/ObjectToObjectMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@
* @param array<int,PropertyMapping> $allPropertyMappings
* @param array<int,string> $initializableTargetPropertiesNotInSource
* @param array<string,true> $targetProxySkippedProperties
* @param array<int,string> $sourceProperties
*/
public function __construct(
private string $sourceClass,
private string $targetClass,
private string $providedTargetClass,
private bool $sourceAllowsDynamicProperties,
private bool $targetAllowsDynamicProperties,
private array $sourceProperties,
array $allPropertyMappings,
private bool $instantiable,
private bool $cloneable,
Expand Down Expand Up @@ -111,6 +113,7 @@ public function withTargetProxy(
providedTargetClass: $this->providedTargetClass,
sourceAllowsDynamicProperties: $this->sourceAllowsDynamicProperties,
targetAllowsDynamicProperties: $this->targetAllowsDynamicProperties,
sourceProperties: $this->sourceProperties,
allPropertyMappings: $this->allPropertyMappings,
instantiable: $this->instantiable,
cloneable: $this->cloneable,
Expand All @@ -133,6 +136,7 @@ public function withReasonCannotUseProxy(
providedTargetClass: $this->providedTargetClass,
sourceAllowsDynamicProperties: $this->sourceAllowsDynamicProperties,
targetAllowsDynamicProperties: $this->targetAllowsDynamicProperties,
sourceProperties: $this->sourceProperties,
allPropertyMappings: $this->allPropertyMappings,
instantiable: $this->instantiable,
cloneable: $this->cloneable,
Expand Down Expand Up @@ -284,13 +288,21 @@ public function constructorIsEager(): bool
return $this->constructorIsEager;
}

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

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

/**
* @return array<int,string>
*/
public function getSourceProperties(): array
{
return $this->sourceProperties;
}
}
10 changes: 10 additions & 0 deletions tests/Common/FrameworkTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Doctrine\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase;
use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Debug\MapperDataCollector;
use Rekalogika\Mapper\Debug\TraceableTransformer;
use Rekalogika\Mapper\MapperInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand Down Expand Up @@ -115,4 +116,13 @@ public function getEntityManager(): EntityManagerInterface

return $this->entityManager = $this->doctrineInit();
}

public function getDataCollector(): MapperDataCollector
{
$result = $this->get('rekalogika.mapper.data_collector');

$this->assertInstanceOf(MapperDataCollector::class, $result);

return $result;
}
}
18 changes: 18 additions & 0 deletions tests/Fixtures/DynamicProperty/AnotherObjectExtendingStdClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?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\DynamicProperty;

class AnotherObjectExtendingStdClass extends \stdClass
{
}
16 changes: 14 additions & 2 deletions tests/IntegrationTest/DynamicPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Rekalogika\Mapper\Tests\IntegrationTest;

use Rekalogika\Mapper\Tests\Common\FrameworkTestCase;
use Rekalogika\Mapper\Tests\Fixtures\DynamicProperty\AnotherObjectExtendingStdClass;
use Rekalogika\Mapper\Tests\Fixtures\DynamicProperty\ObjectExtendingStdClass;
use Rekalogika\Mapper\Tests\Fixtures\DynamicProperty\ObjectExtendingStdClassWithProperties;
use Rekalogika\Mapper\Tests\Fixtures\Scalar\ObjectWithScalarProperties;
Expand Down Expand Up @@ -127,18 +128,26 @@ public function testObjectToObjectExtendingStdClass(): void

public function testStdClassToStdClass(): void
{
$source = new \stdClass();
$source = new ObjectExtendingStdClass();
/** @psalm-suppress UndefinedPropertyAssignment */
$source->a = 1;
/** @psalm-suppress UndefinedPropertyAssignment */
$source->b = 'string';
/** @psalm-suppress UndefinedPropertyAssignment */
$source->c = true;
/** @psalm-suppress UndefinedPropertyAssignment */
$source->d = 1.1;

$target = $this->mapper->map($source, \stdClass::class);
$target = $this->mapper->map($source, AnotherObjectExtendingStdClass::class);

$this->assertInstanceOf(\stdClass::class, $target);
/** @psalm-suppress UndefinedPropertyFetch */
$this->assertSame(1, $target->a);
/** @psalm-suppress UndefinedPropertyFetch */
$this->assertSame('string', $target->b);
/** @psalm-suppress UndefinedPropertyFetch */
$this->assertTrue($target->c);
/** @psalm-suppress UndefinedPropertyFetch */
$this->assertSame(1.1, $target->d);
}

Expand All @@ -148,12 +157,15 @@ public function testStdClassToStdClassWithExplicitProperties(): void
$source->public = 'public';
$source->private = 'private';
$source->constructor = 'constructor';
$source->dynamic = 'dynamic';

$target = $this->mapper->map($source, ObjectExtendingStdClassWithProperties::class);

$this->assertInstanceOf(\stdClass::class, $target);
$this->assertSame('public', $target->public);
$this->assertNull($target->getPrivate());
$this->assertEquals('constructor', $target->getConstructor());
/** @psalm-suppress UndefinedPropertyFetch */
$this->assertSame('dynamic', $target->dynamic);
}
}

0 comments on commit 5f787bb

Please sign in to comment.