Skip to content

Commit 15bed07

Browse files
committed
Add special key support e. g. snake_case
1 parent ac06e78 commit 15bed07

File tree

4 files changed

+192
-21
lines changed

4 files changed

+192
-21
lines changed

src/ImmutableRecordLogic.php

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<?php
2+
23
/**
34
* This file is part of event-engine/php-data.
4-
* (c) 2018-2019 prooph software GmbH <contact@prooph.de>
5+
* (c) 2018-2020 prooph software GmbH <contact@prooph.de>
56
*
67
* For the full copyright and license information, please view the LICENSE
78
* file that was distributed with this source code.
@@ -87,6 +88,12 @@ public function toArray(): array
8788
$arrayPropItemTypeMap = self::getArrayPropItemTypeMapFromMethodOrCache();
8889

8990
foreach (self::$__propTypeMap as $key => [$type, $isNative, $isNullable]) {
91+
$specialKey = $key;
92+
93+
if ($this instanceof SpecialKeySupport) {
94+
$specialKey = $this->convertKeyForArray($key);
95+
}
96+
9097
switch ($type) {
9198
case ImmutableRecord::PHP_TYPE_STRING:
9299
case ImmutableRecord::PHP_TYPE_INT:
@@ -95,23 +102,23 @@ public function toArray(): array
95102
case ImmutableRecord::PHP_TYPE_ARRAY:
96103
if (\array_key_exists($key, $arrayPropItemTypeMap) && ! self::isScalarType($arrayPropItemTypeMap[$key])) {
97104
if ($isNullable && $this->{$key} === null) {
98-
$nativeData[$key] = null;
105+
$nativeData[$specialKey] = null;
99106
continue 2;
100107
}
101108

102-
$nativeData[$key] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) {
109+
$nativeData[$specialKey] = \array_map(function ($item) use ($key, &$arrayPropItemTypeMap) {
103110
return $this->voTypeToNative($item, $key, $arrayPropItemTypeMap[$key]);
104111
}, $this->{$key});
105112
} else {
106-
$nativeData[$key] = $this->{$key};
113+
$nativeData[$specialKey] = $this->{$key};
107114
}
108115
break;
109116
default:
110117
if ($isNullable && (! isset($this->{$key}))) {
111-
$nativeData[$key] = null;
118+
$nativeData[$specialKey] = null;
112119
continue 2;
113120
}
114-
$nativeData[$key] = $this->voTypeToNative($this->{$key}, $key, $type);
121+
$nativeData[$specialKey] = $this->voTypeToNative($this->{$key}, $key, $type);
115122
}
116123
}
117124

@@ -120,7 +127,7 @@ public function toArray(): array
120127

121128
public function equals(ImmutableRecord $other): bool
122129
{
123-
if (get_class($this) !== get_class($other)) {
130+
if (\get_class($this) !== \get_class($other)) {
124131
return false;
125132
}
126133

@@ -130,8 +137,14 @@ public function equals(ImmutableRecord $other): bool
130137
private function setRecordData(array $recordData): void
131138
{
132139
foreach ($recordData as $key => $value) {
133-
$this->assertType($key, $value);
134-
$this->{$key} = $value;
140+
$specialKey = $key;
141+
142+
if ($this instanceof SpecialKeySupport) {
143+
$specialKey = $this->convertKeyForRecord($key);
144+
}
145+
146+
$this->assertType($specialKey, $value);
147+
$this->{$specialKey} = $value;
135148
}
136149
}
137150

@@ -141,17 +154,23 @@ private function setNativeData(array $nativeData): void
141154
$arrayPropItemTypeMap = self::getArrayPropItemTypeMapFromMethodOrCache();
142155

143156
foreach ($nativeData as $key => $val) {
144-
if (! isset(self::$__propTypeMap[$key])) {
157+
$specialKey = $key;
158+
159+
if ($this instanceof SpecialKeySupport) {
160+
$specialKey = $this->convertKeyForRecord($key);
161+
}
162+
163+
if (! isset(self::$__propTypeMap[$specialKey])) {
145164
throw new \InvalidArgumentException(\sprintf(
146-
'Invalid property passed to Record %s. Got property with key ' . $key,
165+
'Invalid property passed to Record %s. Got property with key ' . $specialKey,
147166
\get_called_class()
148167
));
149168
}
150-
[$type, $isNative, $isNullable] = self::$__propTypeMap[$key];
169+
[$type, $isNative, $isNullable] = self::$__propTypeMap[$specialKey];
151170

152171
if ($val === null) {
153172
if (! $isNullable) {
154-
throw new \RuntimeException("Got null for non nullable property $key of Record " . \get_called_class());
173+
throw new \RuntimeException("Got null for non nullable property $specialKey of Record " . \get_called_class());
155174
}
156175

157176
$recordData[$key] = null;

src/SpecialKeySupport.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* This file is part of event-engine/php-data.
5+
* (c) 2018-2020 prooph software GmbH <contact@prooph.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace EventEngine\Data;
14+
15+
/**
16+
* Implement this interface in your ImmutableRecord class if you have special keys like snake_case.
17+
*/
18+
interface SpecialKeySupport
19+
{
20+
/**
21+
* Converts the given key to key name for the record. For example if the key is first_name and you have a class
22+
* property firstName then you have to convert it to camel case.
23+
*
24+
* @param string $key
25+
* @return string
26+
*/
27+
public function convertKeyForRecord(string $key): string;
28+
29+
/**
30+
* Converts the given key to key name for the array data. For example if you have a class property firstName
31+
* and want to have snake case array keys then you have to convert it to first_name.
32+
*
33+
* @param string $key
34+
* @return string
35+
*/
36+
public function convertKeyForArray(string $key): string;
37+
}

tests/ImmutableRecordLogicTest.php

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
<?php
2+
23
/**
34
* This file is part of event-engine/php-data.
45
* (c) 2018-2020 prooph software GmbH <contact@prooph.de>
56
*
67
* For the full copyright and license information, please view the LICENSE
78
* file that was distributed with this source code.
89
*/
10+
911
declare(strict_types=1);
1012

1113
namespace EventEngineTest\Data;
1214

1315
use EventEngineTest\Data\Stub\ImmutableItem;
1416
use EventEngineTest\Data\Stub\ImmutableRecordWithNoTypes;
1517
use EventEngineTest\Data\Stub\ImmutableRecordWithTypedGetters;
18+
use EventEngineTest\Data\Stub\RecordWithSpecialKey;
1619
use EventEngineTest\Data\Stub\RecordWithStringList;
1720
use EventEngineTest\Data\Stub\TypeHintedImmutableRecord;
1821
use EventEngineTest\Data\Stub\TypeHintedImmutableRecordWithValueObjects;
@@ -23,7 +26,6 @@
2326
use EventEngineTest\Data\Stub\ValueObject\Type;
2427
use EventEngineTest\Data\Stub\ValueObject\Version;
2528
use PHPUnit\Framework\TestCase;
26-
use function sprintf;
2729

2830
final class ImmutableRecordLogicTest extends TestCase
2931
{
@@ -121,7 +123,7 @@ public function it_returns_new_record_with_changed_properties()
121123

122124
$changedValueObjects = $valueObjects->with([
123125
'version' => Version::fromInt(2),
124-
'percentage' => Percentage::fromFloat(0.9)
126+
'percentage' => Percentage::fromFloat(0.9),
125127
]);
126128

127129
$this->data['type'] = null;
@@ -152,6 +154,28 @@ public function it_equals_other_record_with_same_values()
152154
$this->assertTrue($valueObjects->equals($other));
153155
}
154156

157+
/**
158+
* @test
159+
*/
160+
public function it_supports_special_keys(): void
161+
{
162+
// emulates snake_case
163+
$recordArray = [
164+
RecordWithSpecialKey::BANK_ACCOUNT => '12324434',
165+
RecordWithSpecialKey::SUCCESS_RATE => 33.33,
166+
RecordWithSpecialKey::ITEM_LIST => [['name' => 'Awesome tester'], ['name' => 'John Smith']],
167+
];
168+
$specialKey = RecordWithSpecialKey::fromArray($recordArray);
169+
$this->assertSame($recordArray, $specialKey->toArray());
170+
171+
$specialKey = RecordWithSpecialKey::fromRecordData([
172+
RecordWithSpecialKey::BANK_ACCOUNT => $recordArray[RecordWithSpecialKey::BANK_ACCOUNT],
173+
RecordWithSpecialKey::SUCCESS_RATE => Percentage::fromFloat($recordArray[RecordWithSpecialKey::SUCCESS_RATE]),
174+
RecordWithSpecialKey::ITEM_LIST => ItemList::fromArray($recordArray[RecordWithSpecialKey::ITEM_LIST]),
175+
]);
176+
$this->assertSame($recordArray, $specialKey->toArray());
177+
}
178+
155179
/**
156180
* @test
157181
*/
@@ -183,7 +207,7 @@ public function it_throws_exception_if_non_nullable_prop_should_be_set_to_null()
183207
{
184208
$this->data['version'] = null;
185209

186-
$this->expectExceptionMessage("Got null for non nullable property version of Record " . TypeHintedImmutableRecord::class);
210+
$this->expectExceptionMessage('Got null for non nullable property version of Record ' . TypeHintedImmutableRecord::class);
187211

188212
TypeHintedImmutableRecord::fromArray($this->data);
189213
}
@@ -195,8 +219,8 @@ public function it_throws_exception_if_property_value_has_wrong_type()
195219
{
196220
$this->data['version'] = 'v1';
197221

198-
$this->expectExceptionMessage(sprintf(
199-
"Record %s data contains invalid value for property version. Expected type is int. Got type string.",
222+
$this->expectExceptionMessage(\sprintf(
223+
'Record %s data contains invalid value for property version. Expected type is int. Got type string.',
200224
TypeHintedImmutableRecord::class
201225
));
202226

@@ -208,10 +232,10 @@ public function it_throws_exception_if_property_value_has_wrong_type()
208232
*/
209233
public function it_throws_exception_if_array_property_contains_invalid_value()
210234
{
211-
$stringList = ["abc", 123, "def"];
235+
$stringList = ['abc', 123, 'def'];
212236

213-
$this->expectExceptionMessage(sprintf(
214-
"Record %s data contains invalid value for property stringList. Value should be an array of string, but at least one item of the array has the wrong type.",
237+
$this->expectExceptionMessage(\sprintf(
238+
'Record %s data contains invalid value for property stringList. Value should be an array of string, but at least one item of the array has the wrong type.',
215239
RecordWithStringList::class
216240
));
217241

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/**
4+
* This file is part of event-engine/php-data.
5+
* (c) 2018-2020 prooph software GmbH <contact@prooph.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace EventEngineTest\Data\Stub;
14+
15+
use EventEngine\Data\ImmutableRecord;
16+
use EventEngine\Data\ImmutableRecordLogic;
17+
use EventEngine\Data\SpecialKeySupport;
18+
use EventEngineTest\Data\Stub\ValueObject\ItemList;
19+
use EventEngineTest\Data\Stub\ValueObject\Percentage;
20+
21+
final class RecordWithSpecialKey implements ImmutableRecord, SpecialKeySupport
22+
{
23+
use ImmutableRecordLogic;
24+
25+
public const BANK_ACCOUNT = 'bank_account';
26+
public const SUCCESS_RATE = 'success_rate';
27+
public const ITEM_LIST = 'item_list';
28+
29+
/**
30+
* @var string
31+
*/
32+
private $bankAccount;
33+
34+
/**
35+
* @var Percentage
36+
*/
37+
private $successRate;
38+
39+
/**
40+
* @var ItemList
41+
*/
42+
private $itemList;
43+
44+
/**
45+
* @return mixed
46+
*/
47+
public function bankAccount(): string
48+
{
49+
return $this->bankAccount;
50+
}
51+
52+
/**
53+
* @return Percentage
54+
*/
55+
public function successRate(): Percentage
56+
{
57+
return $this->successRate;
58+
}
59+
60+
/**
61+
* @return ItemList
62+
*/
63+
public function itemList(): ItemList
64+
{
65+
return $this->itemList;
66+
}
67+
68+
public function convertKeyForRecord(string $key): string
69+
{
70+
switch ($key) {
71+
case self::SUCCESS_RATE:
72+
return 'successRate';
73+
case self::ITEM_LIST:
74+
return 'itemList';
75+
default:
76+
return 'bankAccount';
77+
}
78+
}
79+
80+
public function convertKeyForArray(string $key): string
81+
{
82+
switch ($key) {
83+
case 'successRate':
84+
return self::SUCCESS_RATE;
85+
case 'itemList':
86+
return self::ITEM_LIST;
87+
default:
88+
return self::BANK_ACCOUNT;
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)