88use Liip \MetadataParser \Metadata \ClassMetadata ;
99use Liip \MetadataParser \Metadata \PropertyMetadata ;
1010use Liip \MetadataParser \Metadata \PropertyType ;
11- use Liip \MetadataParser \Metadata \PropertyTypeArray ;
1211use Liip \MetadataParser \Metadata \PropertyTypeClass ;
1312use Liip \MetadataParser \Metadata \PropertyTypeDateTime ;
13+ use Liip \MetadataParser \Metadata \PropertyTypeIterable ;
1414use Liip \MetadataParser \Metadata \PropertyTypePrimitive ;
15+ use Liip \MetadataParser \Metadata \PropertyTypeUnion ;
1516use Liip \MetadataParser \Metadata \PropertyTypeUnknown ;
1617use Liip \MetadataParser \Reducer \GroupReducer ;
1718use Liip \MetadataParser \Reducer \PreferredReducer ;
@@ -120,16 +121,51 @@ private function generateCodeForClass(
120121 string $ modelPath ,
121122 array $ stack = [],
122123 ): string {
124+ $ discriminatorMetadata = $ classMetadata ->getDiscriminatorMetadata ();
125+ if (null !== $ discriminatorMetadata && $ discriminatorMetadata ->baseClass == $ classMetadata ->getClassName ()) {
126+ return $ this ->generateCodeForDiscriminatorClass ($ classMetadata , $ apiVersion , $ serializerGroups , $ arrayPath , $ modelPath , $ stack );
127+ }
128+
123129 $ stack [$ classMetadata ->getClassName ()] = ($ stack [$ classMetadata ->getClassName ()] ?? 0 ) + 1 ;
124130
125131 $ code = '' ;
126132 foreach ($ classMetadata ->getProperties () as $ propertyMetadata ) {
127133 $ code .= $ this ->generateCodeForField ($ propertyMetadata , $ apiVersion , $ serializerGroups , $ arrayPath , $ modelPath , $ stack );
128134 }
129135
136+ if (null !== $ discriminatorMetadata ) {
137+ $ discriminatorFieldPath = $ arrayPath .'[" ' .$ discriminatorMetadata ->propertyName .'"] ' ;
138+ $ code .= $ this ->templating ->renderAssign ($ discriminatorFieldPath , \sprintf ("'%s' " , $ discriminatorMetadata ->value ));
139+ }
140+
130141 return $ this ->templating ->renderClass ($ arrayPath , $ code );
131142 }
132143
144+ /**
145+ * @param list<string> $serializerGroups
146+ * @param array<string, positive-int> $stack
147+ */
148+ private function generateCodeForDiscriminatorClass (
149+ ClassMetadata $ classMetadata ,
150+ ?string $ apiVersion ,
151+ array $ serializerGroups ,
152+ string $ arrayPath ,
153+ string $ modelPath ,
154+ array $ stack = [],
155+ ): string {
156+ $ code = '' ;
157+ $ discriminatorMetadata = $ classMetadata ->getDiscriminatorMetadata ();
158+ foreach ($ discriminatorMetadata ->classMap as $ class ) {
159+ $ code .= $ this ->templating ->renderInstanceOfConditional (
160+ $ modelPath ,
161+ $ class ,
162+ $ this ->generateCodeForClass ($ discriminatorMetadata ->getMetadataForClass ($ class ), $ apiVersion , $ serializerGroups , $ arrayPath , $ modelPath , $ stack )
163+ );
164+ }
165+
166+ return $ code ;
167+ }
168+
133169 /**
134170 * @param list<string> $serializerGroups
135171 * @param array<string, positive-int> $stack
@@ -196,9 +232,12 @@ private function generateCodeForFieldType(
196232 case $ type instanceof PropertyTypeClass:
197233 return $ this ->generateCodeForClass ($ type ->getClassMetadata (), $ apiVersion , $ serializerGroups , $ fieldPath , $ modelPropertyPath , $ stack );
198234
199- case $ type instanceof PropertyTypeArray :
235+ case $ type instanceof PropertyTypeIterable :
200236 return $ this ->generateCodeForArray ($ type , $ apiVersion , $ serializerGroups , $ fieldPath , $ modelPropertyPath , $ stack );
201237
238+ case $ type instanceof PropertyTypeUnion:
239+ return $ this ->generateCodeForUnion ($ type , $ apiVersion , $ serializerGroups , $ fieldPath , $ modelPropertyPath , $ stack );
240+
202241 default :
203242 throw new \Exception ('Unexpected type ' .$ type ::class.' at ' .$ modelPropertyPath );
204243 }
@@ -209,7 +248,7 @@ private function generateCodeForFieldType(
209248 * @param array<string, positive-int> $stack
210249 */
211250 private function generateCodeForArray (
212- PropertyTypeArray $ type ,
251+ PropertyTypeIterable $ type ,
213252 ?string $ apiVersion ,
214253 array $ serializerGroups ,
215254 string $ arrayPath ,
@@ -221,11 +260,11 @@ private function generateCodeForArray(
221260
222261 switch ($ subType ) {
223262 case $ subType instanceof PropertyTypePrimitive:
224- case $ subType instanceof PropertyTypeArray && self ::isArrayForPrimitive ($ subType ):
263+ case $ subType instanceof PropertyTypeIterable && self ::isArrayForPrimitive ($ subType ):
225264 case $ subType instanceof PropertyTypeUnknown && $ this ->configuration ->shouldAllowGenericArrays ():
226265 return $ this ->templating ->renderArrayAssign ($ arrayPath , $ modelPath );
227266
228- case $ subType instanceof PropertyTypeArray :
267+ case $ subType instanceof PropertyTypeIterable :
229268 $ innerCode = $ this ->generateCodeForArray ($ subType , $ apiVersion , $ serializerGroups , $ arrayPath .'[ ' .$ index .'] ' , $ modelPath .'[ ' .$ index .'] ' , $ stack );
230269 break ;
231270
@@ -252,14 +291,62 @@ private function generateCodeForArray(
252291 return $ this ->templating ->renderLoopArray ($ arrayPath , $ modelPath , $ index , $ innerCode );
253292 }
254293
255- private static function isArrayForPrimitive (PropertyTypeArray $ type ): bool
294+ /**
295+ * @param list<string> $serializerGroups
296+ * @param array<string, positive-int> $stack
297+ */
298+ private function generateCodeForUnion (
299+ PropertyTypeUnion $ subType ,
300+ ?string $ apiVersion ,
301+ array $ serializerGroups ,
302+ string $ arrayPath ,
303+ string $ modelPath ,
304+ array $ stack ,
305+ ): string {
306+ $ code = '' ;
307+
308+ $ types = $ subType ->getTypes ();
309+ $ typesWithoutPrimitives = array_filter ($ types , static function (PropertyType $ subType ): bool {
310+ return !($ subType instanceof PropertyTypePrimitive || $ subType instanceof PropertyTypeUnknown);
311+ });
312+
313+ $ hasPrimitives = \count ($ types ) !== \count ($ typesWithoutPrimitives );
314+ if ($ hasPrimitives ) {
315+ $ code .= $ this ->templating ->renderPrimitiveConditional (
316+ $ modelPath ,
317+ $ this ->templating ->renderAssign ($ arrayPath , $ modelPath )
318+ );
319+ }
320+
321+ foreach ($ typesWithoutPrimitives as $ subType ) {
322+ switch ($ subType ::class) {
323+ case PropertyTypeClass::class:
324+ $ code .= $ this ->templating ->renderInstanceOfConditional (
325+ $ modelPath ,
326+ $ subType ->getClassName (),
327+ $ this ->generateCodeForFieldType ($ subType , $ apiVersion , $ serializerGroups , $ arrayPath , $ modelPath , $ stack )
328+ );
329+ break ;
330+ case PropertyTypeIterable::class:
331+ $ code .= $ this ->templating ->renderArrayConditional (
332+ $ modelPath ,
333+ $ this ->generateCodeForArray ($ subType , $ apiVersion , $ serializerGroups , $ arrayPath , $ modelPath , $ stack )
334+ );
335+ break ;
336+ }
337+ }
338+
339+ return $ code ;
340+ }
341+
342+ private static function isArrayForPrimitive (PropertyTypeIterable $ type ): bool
256343 {
257344 do {
258345 $ type = $ type ->getSubType ();
259346 if ($ type instanceof PropertyTypePrimitive) {
260347 return true ;
261348 }
262- } while ($ type instanceof PropertyTypeArray );
349+ } while ($ type instanceof PropertyTypeIterable );
263350
264351 return false ;
265352 }
0 commit comments