Skip to content

Commit

Permalink
feat: ValueObject attribute to explicitly mark classes as value obj…
Browse files Browse the repository at this point in the history
…ects
  • Loading branch information
priyadi committed Sep 27, 2024
1 parent 372c1f5 commit c61aeb4
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* test: immutable setter without setter on parent object error
* test: immutable setter now works using 'wither' method
* fix: readonly objects no longer assumed to be value objects
* feat: `ValueObject` attribute to explicitly mark classes as value objects

## 1.8.0

Expand Down
17 changes: 17 additions & 0 deletions src/Attribute/ValueObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?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_CLASS)]
final readonly class ValueObject {}
5 changes: 4 additions & 1 deletion src/Transformer/Implementation/ObjectToObjectTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ public function transform(
// disregard target if target is read only or target value reading is
// disabled

if ($context(MapperOptions::class)?->readTargetValue !== true) {
if (
$objectToObjectMetadata->isTargetValueObject()
|| $context(MapperOptions::class)?->readTargetValue !== true
) {
$target = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function __construct(
private bool $instantiable,
private bool $cloneable,
private bool $readonly,
private bool $valueObject,
private bool $readableDynamicProperties,
private bool $writableDynamicProperties,
private array $attributes,
Expand Down Expand Up @@ -54,6 +55,11 @@ public function isReadonly(): bool
return $this->readonly;
}

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

public function hasReadableDynamicProperties(): bool
{
return $this->readableDynamicProperties;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ class: $targetClass,
allPropertyMappings: $propertyMappings,
instantiable: $targetClassMetadata->isInstantiable(),
cloneable: $targetClassMetadata->isCloneable(),
targetValueObject: $targetClassMetadata->isValueObject(),
sourceModifiedTime: $sourceClassMetadata->getLastModified(),
targetModifiedTime: $targetClassMetadata->getLastModified(),
targetReadOnly: $targetClassMetadata->isReadonly(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util;

use Rekalogika\Mapper\Attribute\ValueObject;
use Rekalogika\Mapper\Transformer\EagerPropertiesResolver\EagerPropertiesResolverInterface;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Model\ClassMetadata;
use Rekalogika\Mapper\Util\ClassUtil;
Expand Down Expand Up @@ -49,6 +50,7 @@ public function createClassMetadata(string $class): ClassMetadata
$instantiable = $reflection->isInstantiable();
$cloneable = $reflection->isCloneable();
$readOnly = $reflection->isReadOnly();
$valueObject = $this->isValueObject($attributes);

$eagerProperties = $this->eagerPropertiesResolver
->getEagerProperties($class);
Expand All @@ -60,6 +62,7 @@ public function createClassMetadata(string $class): ClassMetadata
instantiable: $instantiable,
cloneable: $cloneable,
readonly: $readOnly,
valueObject: $valueObject,
readableDynamicProperties: $hasReadableDynamicProperties,
writableDynamicProperties: $hasWritableDynamicProperties,
attributes: $attributes,
Expand All @@ -81,4 +84,19 @@ private function allowsDynamicProperties(\ReflectionClass $class): bool

return false;
}

/**
* @param list<object> $attributes
*/
private function isValueObject(
array $attributes,
): bool {
foreach ($attributes as $attribute) {
if ($attribute instanceof ValueObject) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public function __construct(
array $allPropertyMappings,
private bool $instantiable,
private bool $cloneable,
private bool $targetValueObject,
private int $sourceModifiedTime,
private int $targetModifiedTime,
private bool $targetReadOnly,
Expand Down Expand Up @@ -125,6 +126,7 @@ public function withTargetProxy(
allPropertyMappings: $this->allPropertyMappings,
instantiable: $this->instantiable,
cloneable: $this->cloneable,
targetValueObject: $this->targetValueObject,
sourceModifiedTime: $this->sourceModifiedTime,
targetModifiedTime: $this->targetModifiedTime,
targetReadOnly: $this->targetReadOnly,
Expand All @@ -149,6 +151,7 @@ public function withReasonCannotUseProxy(
allPropertyMappings: $this->allPropertyMappings,
instantiable: $this->instantiable,
cloneable: $this->cloneable,
targetValueObject: $this->targetValueObject,
sourceModifiedTime: $this->sourceModifiedTime,
targetModifiedTime: $this->targetModifiedTime,
targetReadOnly: $this->targetReadOnly,
Expand Down Expand Up @@ -194,6 +197,11 @@ public function isCloneable(): bool
return $this->cloneable;
}

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

/**
* @return list<PropertyMapping>
*/
Expand Down
3 changes: 3 additions & 0 deletions tests/src/Fixtures/UninitializedProperty/ValueObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

namespace Rekalogika\Mapper\Tests\Fixtures\UninitializedProperty;

use Rekalogika\Mapper\Attribute\ValueObject as AttributeValueObject;

#[AttributeValueObject]
final readonly class ValueObject
{
public function __construct(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

namespace Rekalogika\Mapper\Tests\Fixtures\UninitializedPropertyDto;

use Rekalogika\Mapper\Attribute\ValueObject;

#[ValueObject]
final readonly class ValueObjectDto
{
public function __construct(
Expand Down
20 changes: 8 additions & 12 deletions tests/src/IntegrationTest/UninitializedPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,18 @@ class UninitializedPropertyTest extends FrameworkTestCase

public function testInitializedToInitialized(): void
{
$this->markTestSkipped('Revisit this test. A readonly object no longer assumed a value object');

// $object = new ObjectWithInitializedProperty();
// $dto = $this->mapper->map($object, ObjectWithInitializedPropertyDto::class);
// $this->initialize($dto);
// $this->assertSame('foo', $dto->property->name);
$object = new ObjectWithInitializedProperty();
$dto = $this->mapper->map($object, ObjectWithInitializedPropertyDto::class);
$this->initialize($dto);
$this->assertSame('foo', $dto->property->name);
}

public function testInitializedToFinalInitialized(): void
{
$this->markTestSkipped('Revisit this test. A readonly object no longer assumed a value object');

// $object = new ObjectWithInitializedProperty();
// $dto = $this->mapper->map($object, FinalObjectWithInitializedPropertyDto::class);
// $this->initialize($dto);
// $this->assertSame('foo', $dto->property->name);
$object = new ObjectWithInitializedProperty();
$dto = $this->mapper->map($object, FinalObjectWithInitializedPropertyDto::class);
$this->initialize($dto);
$this->assertSame('foo', $dto->property->name);
}

public function testInitializedToUnitialized(): void
Expand Down

0 comments on commit c61aeb4

Please sign in to comment.