44
55namespace Windwalker \Data ;
66
7- use Windwalker \Attributes \AttributesAccessor ;
8- use Windwalker \ORM \Attributes \Cast ;
9- use Windwalker \ORM \Attributes \CastNullable ;
107use Windwalker \ORM \Attributes \JsonNoSerialize ;
118use 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 ;
1312use Windwalker \Utilities \TypeCast ;
1413
15- use function Windwalker \get_object_values ;
16-
1714trait 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}
0 commit comments