Skip to content

Commit 144d10d

Browse files
authored
Feature: Relative accessors that make it possible to apply an accessor from a certain number of levels upwards in the data structure. (#21)
1 parent 390918b commit 144d10d

File tree

9 files changed

+196
-1
lines changed

9 files changed

+196
-1
lines changed

src/Mapping/AbstractProperty.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public function __construct(
3030
private bool $serialized = false,
3131
private ?string $extractor = null,
3232
private ?string $scenario = ModelMetadata::DEFAULT_SCENARIO,
33+
private ?int $relative = null,
3334
) {
3435
}
3536

@@ -107,4 +108,9 @@ public function getScenario(): string
107108
{
108109
return $this->scenario;
109110
}
111+
112+
public function getRelative(): ?int
113+
{
114+
return $this->relative;
115+
}
110116
}

src/Metadata/PropertyMetadata.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,9 @@ public function getScenario(): string
163163
{
164164
return $this->attribute->getScenario();
165165
}
166+
167+
public function getRelative(): ?int
168+
{
169+
return $this->attribute->getRelative();
170+
}
166171
}

src/Serializer/Normalizer/ObjectNormalizer.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ final class ObjectNormalizer extends AbstractNormalizer implements NormalizerInt
6464
*/
6565
public const ROOT_DATA = 'vom_root_data';
6666

67+
/**
68+
* Context key where the normalizer stores the all accessors
69+
* that have been used to reach the current nesting level.
70+
* Required for relative accessors.
71+
*/
72+
public const NESTING_PATH = 'vom_nesting_path';
73+
6774
/**
6875
* Flag to control whether fields with the value `null` should be output
6976
* when normalizing or omitted.
@@ -317,7 +324,16 @@ private function denormalizeProperty(string $type, mixed $data, PropertyMetadata
317324
}
318325
} else {
319326
if ($accessor = $property->getAccessor()) {
320-
$value = $this->propertyAccessor->getValue($data, $accessor);
327+
if (($relative = $property->getRelative()) && isset($context[self::NESTING_PATH])) {
328+
$parts = \array_slice($context[self::NESTING_PATH], 0, -$relative);
329+
$parts[] = $accessor;
330+
$fromTheRoot = implode('', $parts);
331+
$value = $this->propertyAccessor->getValue($context[self::ROOT_DATA], $fromTheRoot);
332+
} else {
333+
$context[self::NESTING_PATH] ??= [];
334+
$context[self::NESTING_PATH][] = $accessor;
335+
$value = $this->propertyAccessor->getValue($data, $accessor);
336+
}
321337
} else {
322338
$value = $data;
323339
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the VOM package.
7+
*
8+
* (c) Andreas Linden <zlx@gmx.de>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zolex\VOM\Test\Fixtures;
15+
16+
use Zolex\VOM\Mapping as VOM;
17+
18+
#[VOM\Model]
19+
class RelativeNestingLevel0
20+
{
21+
#[VOM\Property]
22+
public int $LEVEL_ZERO_VALUE;
23+
24+
#[VOM\Property]
25+
public RelativeNestingLevel1 $LEVEL_ONE;
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the VOM package.
7+
*
8+
* (c) Andreas Linden <zlx@gmx.de>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zolex\VOM\Test\Fixtures;
15+
16+
use Zolex\VOM\Mapping as VOM;
17+
18+
#[VOM\Model]
19+
class RelativeNestingLevel1
20+
{
21+
#[VOM\Property(accessor: '[LEVEL_ZERO_VALUE]', relative: 1)]
22+
public int $LEVEL_ONE_VALUE;
23+
24+
#[VOM\Property]
25+
public RelativeNestingLevel2 $LEVEL_TWO;
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the VOM package.
7+
*
8+
* (c) Andreas Linden <zlx@gmx.de>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zolex\VOM\Test\Fixtures;
15+
16+
use Zolex\VOM\Mapping as VOM;
17+
18+
#[VOM\Model]
19+
class RelativeNestingLevel2
20+
{
21+
#[VOM\Property]
22+
public int $LEVEL_TWO_VALUE;
23+
24+
#[VOM\Property(accessor: '[LEVEL_TWO]', relative: 1)]
25+
public RelativeNestingLevel3 $LEVEL_THREE;
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the VOM package.
7+
*
8+
* (c) Andreas Linden <zlx@gmx.de>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zolex\VOM\Test\Fixtures;
15+
16+
use Zolex\VOM\Mapping as VOM;
17+
18+
#[VOM\Model]
19+
class RelativeNestingLevel3
20+
{
21+
#[VOM\Property(accessor: '[LEVEL_TWO_VALUE]')]
22+
public int $LEVEL_THREE_VALUE;
23+
24+
#[VOM\Property(accessor: '[LEVEL_THREE]')]
25+
public RelativeNestingLevel4 $LEVEL_FOUR;
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the VOM package.
7+
*
8+
* (c) Andreas Linden <zlx@gmx.de>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zolex\VOM\Test\Fixtures;
15+
16+
use Zolex\VOM\Mapping as VOM;
17+
18+
#[VOM\Model]
19+
class RelativeNestingLevel4
20+
{
21+
#[VOM\Property(accessor: '[LEVEL_ONE_VALUE]', relative: 2)]
22+
public int $LEVEL_FOUR_VALUE;
23+
}

tests/Serializer/VersatileObjectMapperTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@
7777
use Zolex\VOM\Test\Fixtures\PropertyPromotion;
7878
use Zolex\VOM\Test\Fixtures\RegexpExtractorModel;
7979
use Zolex\VOM\Test\Fixtures\RegexpExtractorProperty;
80+
use Zolex\VOM\Test\Fixtures\RelativeNestingLevel0;
81+
use Zolex\VOM\Test\Fixtures\RelativeNestingLevel1;
82+
use Zolex\VOM\Test\Fixtures\RelativeNestingLevel2;
83+
use Zolex\VOM\Test\Fixtures\RelativeNestingLevel3;
84+
use Zolex\VOM\Test\Fixtures\RelativeNestingLevel4;
8085
use Zolex\VOM\Test\Fixtures\ScenarioConstructorArguments;
8186
use Zolex\VOM\Test\Fixtures\ScenarioConstructorPropertyPromotion;
8287
use Zolex\VOM\Test\Fixtures\ScenarioDenormalizer;
@@ -1305,6 +1310,42 @@ public function testRootWithNestedAndAccessors(): void
13051310
$this->assertEquals($data, $normalized);
13061311
}
13071312

1313+
public function testRelativeNesting(): void
1314+
{
1315+
$data = [
1316+
'LEVEL_ZERO_VALUE' => 0,
1317+
'LEVEL_ONE' => [
1318+
'LEVEL_ONE_VALUE' => 1,
1319+
'LEVEL_TWO' => [
1320+
'LEVEL_TWO_VALUE' => 2,
1321+
'LEVEL_THREE' => [
1322+
'LEVEL_THREE_VALUE' => 3,
1323+
'LEVEL_FOUR' => [
1324+
'LEVEL_FOUR_VALUE' => 4,
1325+
],
1326+
],
1327+
],
1328+
],
1329+
];
1330+
1331+
$relativeBrainfuck = self::$serializer->denormalize($data, RelativeNestingLevel0::class);
1332+
1333+
$this->assertInstanceOf(RelativeNestingLevel0::class, $relativeBrainfuck);
1334+
$this->assertEquals(0, $relativeBrainfuck->LEVEL_ZERO_VALUE);
1335+
1336+
$this->assertInstanceOf(RelativeNestingLevel1::class, $relativeBrainfuck->LEVEL_ONE);
1337+
$this->assertEquals(0, $relativeBrainfuck->LEVEL_ONE->LEVEL_ONE_VALUE);
1338+
1339+
$this->assertInstanceOf(RelativeNestingLevel2::class, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO);
1340+
$this->assertEquals(2, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO->LEVEL_TWO_VALUE);
1341+
1342+
$this->assertInstanceOf(RelativeNestingLevel3::class, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO->LEVEL_THREE);
1343+
$this->assertEquals(2, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO->LEVEL_THREE->LEVEL_THREE_VALUE);
1344+
1345+
$this->assertInstanceOf(RelativeNestingLevel4::class, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO->LEVEL_THREE->LEVEL_FOUR);
1346+
$this->assertEquals(1, $relativeBrainfuck->LEVEL_ONE->LEVEL_TWO->LEVEL_THREE->LEVEL_FOUR->LEVEL_FOUR_VALUE);
1347+
}
1348+
13081349
public function testObjectToPopulate(): void
13091350
{
13101351
$person = Person::create(id: 666);

0 commit comments

Comments
 (0)