3131use Symfony \Component \TypeInfo \Type \CompositeTypeInterface ;
3232use Symfony \Component \TypeInfo \Type \GenericType ;
3333use Symfony \Component \TypeInfo \Type \ObjectType ;
34+ use Symfony \Component \TypeInfo \Type \UnionType ;
3435use Symfony \Component \TypeInfo \TypeIdentifier ;
3536
3637/**
37- * {@inheritdoc}
38- *
3938 * @author Kévin Dunglas <[email protected] > 4039 */
4140final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
@@ -57,9 +56,6 @@ public function __construct(ResourceMetadataCollectionFactoryInterface $resource
5756 $ this ->resourceClassResolver = $ resourceClassResolver ;
5857 }
5958
60- /**
61- * {@inheritdoc}
62- */
6359 public function buildSchema (string $ className , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?Operation $ operation = null , ?Schema $ schema = null , ?array $ serializerContext = null , bool $ forceCollection = false ): Schema
6460 {
6561 $ schema = $ schema ? clone $ schema : new Schema ();
@@ -354,6 +350,65 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
354350 $ valueType = TypeHelper::getCollectionValueType ($ t );
355351 }
356352
353+ if ($ valueType instanceof UnionType) {
354+ $ unionRefs = [];
355+
356+ foreach ($ valueType ->getTypes () as $ subtype ) {
357+ if ($ subtype instanceof BuiltinType && TypeIdentifier::NULL === $ subtype ->getTypeIdentifier ()) {
358+ continue ;
359+ }
360+
361+ if ($ subtype instanceof ObjectType) {
362+ $ className = $ subtype ->getClassName ();
363+ } elseif ($ subtype instanceof BuiltinType && $ subtype ->getTypeIdentifier ()->isScalar ()) {
364+ $ unionRefs [] = match ($ subtype ->getTypeIdentifier ()) {
365+ TypeIdentifier::INT => ['type ' => 'integer ' ],
366+ TypeIdentifier::FLOAT => ['type ' => 'number ' ],
367+ TypeIdentifier::BOOL => ['type ' => 'boolean ' ],
368+ TypeIdentifier::TRUE => ['type ' => 'boolean ' , 'const ' => true ],
369+ TypeIdentifier::FALSE => ['type ' => 'boolean ' , 'const ' => false ],
370+ TypeIdentifier::STRING => ['type ' => 'string ' ],
371+ default => ['type ' => 'null ' ],
372+ };
373+
374+ continue ;
375+ } else {
376+ continue ;
377+ }
378+
379+ $ subSchema = new Schema ($ version );
380+ $ subSchema ->setDefinitions ($ schema ->getDefinitions ());
381+
382+ $ result = ($ this ->schemaFactory ?: $ this )->buildSchema (
383+ $ className ,
384+ $ format ,
385+ $ parentType ,
386+ null ,
387+ $ subSchema ,
388+ $ serializerContext + [self ::FORCE_SUBSCHEMA => true ],
389+ );
390+
391+ if (isset ($ result ['$ref ' ])) {
392+ $ unionRefs [] = ['$ref ' => $ result ['$ref ' ]];
393+ }
394+ }
395+
396+ if ($ unionRefs ) {
397+ if ($ isCollection ) {
398+ $ propertySchema ['type ' ] = 'array ' ;
399+ $ propertySchema ['items ' ] = 1 === \count ($ unionRefs )
400+ ? $ unionRefs [0 ]
401+ : ['anyOf ' => $ unionRefs ];
402+ } else {
403+ $ refs = 1 === \count ($ unionRefs )
404+ ? [$ unionRefs [0 ]]
405+ : $ unionRefs ;
406+ }
407+ }
408+
409+ continue ;
410+ }
411+
357412 if (!$ valueType instanceof ObjectType && !$ valueType instanceof GenericType) {
358413 continue ;
359414 }
0 commit comments