Skip to content

Commit 6388ce0

Browse files
committed
Simplify Record
1 parent aa3c049 commit 6388ce0

5 files changed

Lines changed: 211 additions & 125 deletions

File tree

src/RecordInterface.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,19 @@
77
use Windwalker\Utilities\Contract\ArrayAccessibleInterface;
88
use Windwalker\Utilities\Contract\DumpableInterface;
99

10-
interface RecordInterface extends
11-
ArrayAccessibleInterface,
12-
DumpableInterface,
13-
\IteratorAggregate,
14-
\Stringable,
15-
\JsonSerializable
10+
interface RecordInterface extends DumpableInterface, \JsonSerializable
1611
{
17-
public static function wrapWith(...$data): static;
12+
public static function wrapWith(...$values): static;
1813

1914
public static function tryWrapWith(...$data): ?static;
2015

21-
public static function wrap(mixed $data): static;
16+
public static function wrap(mixed $values): static;
2217

2318
public static function tryWrap(mixed $data): ?static;
2419

2520
public function fill(mixed $data): static;
2621

27-
public function fillWith(...$data): static;
22+
public function with(...$data): static;
2823

2924
/**
3025
* @template T

src/RecordTrait.php

Lines changed: 66 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,30 @@
44

55
namespace Windwalker\Data;
66

7-
use Windwalker\Attributes\AttributesAccessor;
8-
use Windwalker\ORM\Attributes\Cast;
9-
use Windwalker\ORM\Attributes\CastNullable;
107
use Windwalker\ORM\Attributes\JsonNoSerialize;
118
use Windwalker\ORM\Attributes\JsonSerializerInterface;
12-
use Windwalker\Utilities\StrNormalize;
9+
use Windwalker\Utilities\Arr;
10+
use Windwalker\Utilities\Attributes\AttributesAccessor;
11+
use Windwalker\Utilities\Classes\TraitHelper;
1312
use Windwalker\Utilities\TypeCast;
1413

15-
use function Windwalker\get_object_values;
16-
1714
trait RecordTrait
1815
{
19-
public static function wrapWith(...$data): static
16+
public static function wrap(mixed $values, bool $clone = false): static
2017
{
21-
return static::wrap($data);
22-
}
18+
if ($values instanceof static) {
19+
if ($clone) {
20+
$values = clone $values;
21+
}
2322

24-
public static function tryWrapWith(...$data): ?static
25-
{
26-
return static::tryWrap($data);
27-
}
23+
return $values;
24+
}
2825

29-
public static function wrap(mixed $data): static
30-
{
31-
if ($data instanceof static) {
32-
return $data;
26+
if ($values === null) {
27+
$values = [];
3328
}
3429

35-
$data = TypeCast::toArray($data);
30+
$values = TypeCast::toArray($values);
3631
$args = [];
3732

3833
$ref = new \ReflectionClass(static::class);
@@ -42,87 +37,94 @@ public static function wrap(mixed $data): static
4237
foreach ($constructor->getParameters() as $parameter) {
4338
$name = $parameter->getName();
4439

45-
if (array_key_exists($name, $data)) {
46-
$args[$name] = $data[$name];
40+
if (array_key_exists($name, $values)) {
41+
$args[$name] = $values[$name];
4742

48-
unset($data[$name]);
43+
unset($values[$name]);
4944
}
5045
}
5146
}
5247

53-
return new static(...$args)->fill($data);
48+
return new static(...$args)->merge($values);
49+
}
50+
51+
public static function wrapWith(...$values): static
52+
{
53+
return static::wrap($values, true);
5454
}
5555

56-
public static function tryWrap(mixed $data): ?static
56+
public static function tryWrap(mixed $values, bool $clone = false): ?static
5757
{
58-
if ($data === null) {
58+
if ($values === null) {
5959
return null;
6060
}
6161

62-
return static::wrap($data);
62+
return static::wrap($values, $clone);
6363
}
6464

65-
public function fill(mixed $data): static
65+
public static function tryWrapWith(...$data): ?static
6666
{
67-
if (is_string($data) && is_json($data)) {
68-
$data = json_decode($data, true);
69-
}
67+
return static::tryWrap($data, true);
68+
}
7069

70+
public function fill(mixed $data): static
71+
{
7172
$values = TypeCast::toArray($data);
7273

7374
foreach ($values as $key => $value) {
74-
// Hydrating
75-
$this->hydrateField($key, $value);
75+
$this->$key = $value;
7676
}
7777

7878
return $this;
7979
}
8080

81-
private function hydrateField(string $key, mixed $value): void
81+
public function merge(mixed $values, bool $recursive = false, bool $ignoreNulls = false): static
8282
{
83-
$setter = 'set' . StrNormalize::toPascalCase($key);
83+
$values = TypeCast::toArray($values);
8484

85-
if (method_exists($this, $setter)) {
86-
$this->$setter($value);
87-
return;
88-
}
89-
90-
if (!property_exists($this, $key)) {
91-
$this->$key = $value;
92-
return;
85+
foreach ($values as $key => $value) {
86+
if (
87+
$recursive
88+
&& is_object($this->$key ?? null)
89+
&& TraitHelper::uses($this->$key, RecordTrait::class)
90+
) {
91+
$this->$key = $this->$key->withMerge($value, true, $ignoreNulls);
92+
} elseif ($recursive && is_array($this->$key ?? null) && is_array($value)) {
93+
$this->$key = Arr::mergeRecursive($this->$key, $value);
94+
} elseif ($ignoreNulls && $value === null) {
95+
continue;
96+
} else {
97+
$this->$key = $value;
98+
}
9399
}
94100

95-
$attrs = AttributesAccessor::getAttributesFromAny(
96-
new \ReflectionProperty($this, $key),
97-
Cast::class,
98-
\ReflectionAttribute::IS_INSTANCEOF
99-
);
101+
return $this;
102+
}
100103

101-
foreach ($attrs as $attr) {
102-
/**
103-
* @var Cast $attrInstance
104-
*/
105-
$attrInstance = $attr->newInstance();
104+
public function withMerge(mixed $values, bool $recursive = false, bool $ignoreNulls = false): static
105+
{
106+
return (clone $this)->merge($values, $recursive, $ignoreNulls);
107+
}
106108

107-
if ($attrInstance instanceof CastNullable && $value === null) {
108-
continue;
109-
}
109+
public function defaults(mixed $values, bool $recursive = false): static
110+
{
111+
$new = clone $this;
110112

111-
$hydrateTarget = $attrInstance->getHydrate();
113+
return $this->merge($values, $recursive)->merge($new, $recursive, true);
114+
}
112115

113-
if (class_exists($hydrateTarget)) {
114-
$value = new $hydrateTarget($value);
115-
} elseif (is_callable($hydrateTarget)) {
116-
$value = $hydrateTarget($value);
117-
}
118-
}
116+
public function withDefaults(mixed $values, bool $recursive = false): static
117+
{
118+
$new = clone $this;
119119

120-
$this->$key = $value;
120+
return (clone $this)->merge($values, $recursive)->merge($new, $recursive, true);
121121
}
122122

123-
public function fillWith(...$data): static
123+
public function with(...$data): static
124124
{
125-
return $this->fill($data);
125+
$new = clone $this;
126+
127+
return $new->fill($data);
126128
}
127129

128130
public function dump(bool $recursive = false, bool $onlyDumpable = false): array
@@ -146,46 +148,6 @@ public function as(string $className): object
146148
return $className::wrap($this);
147149
}
148150

149-
/**
150-
* @inheritDoc
151-
*/
152-
public function offsetExists(mixed $key): bool
153-
{
154-
return property_exists($this, $key);
155-
}
156-
157-
/**
158-
* @inheritDoc
159-
*/
160-
public function &offsetGet(mixed $key): mixed
161-
{
162-
return $this->$key;
163-
}
164-
165-
/**
166-
* @inheritDoc
167-
*/
168-
public function offsetSet(mixed $key, mixed $value): void
169-
{
170-
$this->$key = $value;
171-
}
172-
173-
/**
174-
* @inheritDoc
175-
*/
176-
public function offsetUnset(mixed $key): void
177-
{
178-
unset($this->$key);
179-
}
180-
181-
/**
182-
* @inheritDoc
183-
*/
184-
public function getIterator(): \Traversable
185-
{
186-
return new \ArrayIterator($this->dump());
187-
}
188-
189151
/**
190152
* @inheritDoc
191153
*/
@@ -218,9 +180,4 @@ public function jsonSerialize(): mixed
218180

219181
return $item;
220182
}
221-
222-
public function __toString(): string
223-
{
224-
return json_encode($this);
225-
}
226183
}

src/ValueObject.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,31 @@
1919
* @deprecated Use Record instead.
2020
*/
2121
#[\AllowDynamicProperties]
22-
class ValueObject implements RecordInterface
22+
class ValueObject implements ValueObjectInterface
2323
{
24-
use RecordTrait;
24+
use ValueObjectTrait;
2525

2626
public function __construct(mixed $data = null)
2727
{
2828
$this->fill($data);
2929
}
3030

31-
public static function wrap(mixed $data): static
31+
public static function wrap(mixed $values): static
3232
{
33-
if ($data instanceof static) {
34-
return $data;
33+
if ($values instanceof static) {
34+
return $values;
3535
}
3636

3737
$ref = new \ReflectionClass(static::class);
3838
$method = $ref->getConstructor();
3939

4040
if ($method?->getDeclaringClass()->getName() !== static::class) {
4141
// The final class declares a constructor, so we will use it.
42-
return new static()->fill($data);
42+
return new static()->fill($values);
4343
}
4444

4545
// Back to legacy constructor.
46-
return new static($data);
46+
return new static($values);
4747
}
4848

4949
public function fill(mixed $data): static

src/ValueObjectInterface.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Windwalker\Data;
6+
7+
use Windwalker\Utilities\Contract\ArrayAccessibleInterface;
8+
9+
interface ValueObjectInterface extends
10+
RecordInterface,
11+
ArrayAccessibleInterface,
12+
\IteratorAggregate,
13+
\Stringable
14+
{
15+
}

0 commit comments

Comments
 (0)