Skip to content

Commit ba586a1

Browse files
committed
Type: added mergeMode() [WIP]
TODO: Type should not support the array at all
1 parent f5c9ef2 commit ba586a1

3 files changed

Lines changed: 72 additions & 4 deletions

File tree

src/Schema/Elements/Type.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Nette\Schema\Context;
1111
use Nette\Schema\DynamicParameter;
1212
use Nette\Schema\Helpers;
13+
use Nette\Schema\MergeMode;
1314
use Nette\Schema\Schema;
1415
use function array_key_exists, array_pop, implode, is_array, str_replace, strpos;
1516

@@ -26,6 +27,7 @@ final class Type implements Schema
2627
private array $range = [null, null];
2728
private ?string $pattern = null;
2829
private bool $merge = false;
30+
private ?MergeMode $mergeMode = null;
2931

3032

3133
public function __construct(string $type)
@@ -57,6 +59,16 @@ public function mergeDefaults(bool $state = true): self
5759
}
5860

5961

62+
/**
63+
* Sets how arrays are combined when merging multiple configurations.
64+
*/
65+
public function mergeMode(MergeMode $mode): self
66+
{
67+
$this->mergeMode = $mode;
68+
return $this;
69+
}
70+
71+
6072
/**
6173
* Allows the value to be a DynamicParameter, which is recorded for deferred validation.
6274
*/
@@ -142,19 +154,19 @@ public function normalize(mixed $value, Context $context): mixed
142154

143155
public function merge(mixed $value, mixed $base): mixed
144156
{
145-
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
157+
if ($this->mergeMode === MergeMode::Replace || (is_array($value) && isset($value[Helpers::PreventMerging]))) {
146158
unset($value[Helpers::PreventMerging]);
147159
return $value;
148160
}
149161

150-
if (is_array($value) && is_array($base) && $this->itemsValue) {
151-
$index = 0;
162+
if (is_array($value) && is_array($base) && ($this->itemsValue || $this->mergeMode)) {
163+
$index = $this->mergeMode === MergeMode::OverwriteKeys ? null : 0;
152164
foreach ($value as $key => $val) {
153165
if ($key === $index) {
154166
$base[] = $val;
155167
$index++;
156168
} else {
157-
$base[$key] = array_key_exists($key, $base)
169+
$base[$key] = array_key_exists($key, $base) && $this->itemsValue
158170
? $this->itemsValue->merge($val, $base[$key])
159171
: $val;
160172
}

src/Schema/enums.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
namespace Nette\Schema;
9+
10+
11+
/**
12+
* Defines the strategy for merging array values in Type schemas.
13+
*/
14+
enum MergeMode: int
15+
{
16+
/** Replaces all items with the last one. */
17+
case Replace = 0;
18+
19+
/** Overwrites existing keys. */
20+
case OverwriteKeys = 1;
21+
22+
/** Overwrites existing keys and appends new indexed elements. */
23+
case AppendKeys = 2;
24+
}

tests/Schema/Expect.array.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use Nette\Schema\Expect;
44
use Nette\Schema\Helpers;
5+
use Nette\Schema\MergeMode;
56
use Nette\Schema\Processor;
67
use Tester\Assert;
78

@@ -334,3 +335,34 @@ test('array shape', function () {
334335
(new Processor)->process($schema, []),
335336
);
336337
});
338+
339+
340+
test('merge modes', function () {
341+
$schema = Expect::structure([
342+
'foo1' => Expect::array()->mergeMode(MergeMode::Replace),
343+
'foo2' => Expect::array()->mergeMode(MergeMode::OverwriteKeys),
344+
'foo3' => Expect::array()->mergeMode(MergeMode::AppendKeys),
345+
]);
346+
347+
$processor = new Processor;
348+
349+
Assert::equal(
350+
(object) [
351+
'foo1' => ['key' => 'new'],
352+
'foo2' => ['new', 'key' => 'new'],
353+
'foo3' => ['old', 'new', 'key' => 'new'],
354+
],
355+
$processor->processMultiple($schema, [
356+
[
357+
'foo1' => ['old', 'key' => '1'],
358+
'foo2' => ['old', 'key' => '1'],
359+
'foo3' => ['old', 'key' => '1'],
360+
],
361+
[
362+
'foo1' => ['key' => 'new'],
363+
'foo2' => ['new', 'key' => 'new'],
364+
'foo3' => ['new', 'key' => 'new'],
365+
],
366+
]),
367+
);
368+
});

0 commit comments

Comments
 (0)