1414namespace ApiPlatform \Core \JsonSchema ;
1515
1616use ApiPlatform \Core \Api \OperationType ;
17+ use ApiPlatform \Core \Api \ResourceClassResolverInterface ;
1718use ApiPlatform \Core \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
1819use ApiPlatform \Core \Metadata \Property \Factory \PropertyNameCollectionFactoryInterface ;
1920use ApiPlatform \Core \Metadata \Property \PropertyMetadata ;
2021use ApiPlatform \Core \Metadata \Resource \Factory \ResourceMetadataFactoryInterface ;
2122use ApiPlatform \Core \Metadata \Resource \ResourceMetadata ;
2223use ApiPlatform \Core \Swagger \Serializer \DocumentationNormalizer ;
24+ use ApiPlatform \Core \Util \ResourceClassInfoTrait ;
2325use Symfony \Component \PropertyInfo \Type ;
2426use Symfony \Component \Serializer \NameConverter \NameConverterInterface ;
2527use Symfony \Component \Serializer \Normalizer \AbstractNormalizer ;
3335 */
3436final class SchemaFactory implements SchemaFactoryInterface
3537{
36- private $ resourceMetadataFactory ;
38+ use ResourceClassInfoTrait;
39+
40+ private $ typeFactory ;
3741 private $ propertyNameCollectionFactory ;
3842 private $ propertyMetadataFactory ;
39- private $ typeFactory ;
4043 private $ nameConverter ;
4144 private $ distinctFormats = [];
4245
43- public function __construct (TypeFactoryInterface $ typeFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , NameConverterInterface $ nameConverter = null )
46+ public function __construct (TypeFactoryInterface $ typeFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , NameConverterInterface $ nameConverter = null , ResourceClassResolverInterface $ resourceClassResolver = null )
4447 {
48+ $ this ->typeFactory = $ typeFactory ;
4549 $ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
4650 $ this ->propertyNameCollectionFactory = $ propertyNameCollectionFactory ;
4751 $ this ->propertyMetadataFactory = $ propertyMetadataFactory ;
4852 $ this ->nameConverter = $ nameConverter ;
49- $ this ->typeFactory = $ typeFactory ;
53+ $ this ->resourceClassResolver = $ resourceClassResolver ;
5054 }
5155
5256 /**
@@ -62,16 +66,20 @@ public function addDistinctFormat(string $format): void
6266 /**
6367 * {@inheritdoc}
6468 */
65- public function buildSchema (string $ resourceClass , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?Schema $ schema = null , ?array $ serializerContext = null , bool $ forceCollection = false ): Schema
69+ public function buildSchema (string $ className , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?Schema $ schema = null , ?array $ serializerContext = null , bool $ forceCollection = false ): Schema
6670 {
6771 $ schema = $ schema ?? new Schema ();
68- if (null === $ metadata = $ this ->getMetadata ($ resourceClass , $ type , $ operationType , $ operationName , $ serializerContext )) {
72+ if (null === $ metadata = $ this ->getMetadata ($ className , $ type , $ operationType , $ operationName , $ serializerContext )) {
6973 return $ schema ;
7074 }
7175 [$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ metadata ;
7276
77+ if (null === $ resourceMetadata && (null !== $ operationType || null !== $ operationName )) {
78+ throw new \LogicException ('The $operationType and $operationName arguments must be null for non-resource class. ' );
79+ }
80+
7381 $ version = $ schema ->getVersion ();
74- $ definitionName = $ this ->buildDefinitionName ($ resourceClass , $ format , $ type , $ operationType , $ operationName , $ serializerContext );
82+ $ definitionName = $ this ->buildDefinitionName ($ className , $ format , $ type , $ operationType , $ operationName , $ serializerContext );
7583
7684 if (null === $ operationType || null === $ operationName ) {
7785 $ method = Schema::TYPE_INPUT === $ type ? 'POST ' : 'GET ' ;
@@ -103,12 +111,13 @@ public function buildSchema(string $resourceClass, string $format = 'json', stri
103111
104112 $ definition = new \ArrayObject (['type ' => 'object ' ]);
105113 $ definitions [$ definitionName ] = $ definition ;
106- if (null !== $ description = $ resourceMetadata ->getDescription ()) {
114+ if (null !== $ resourceMetadata && null !== $ description = $ resourceMetadata ->getDescription ()) {
107115 $ definition ['description ' ] = $ description ;
108116 }
109117 // see https://github.com/json-schema-org/json-schema-spec/pull/737
110118 if (
111119 Schema::VERSION_SWAGGER !== $ version &&
120+ null !== $ resourceMetadata &&
112121 (
113122 (null !== $ operationType && null !== $ operationName && null !== $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , 'deprecation_reason ' , null , true )) ||
114123 null !== $ resourceMetadata ->getAttribute ('deprecation_reason ' , null )
@@ -118,7 +127,7 @@ public function buildSchema(string $resourceClass, string $format = 'json', stri
118127 }
119128 // externalDocs is an OpenAPI specific extension, but JSON Schema allows additional keys, so we always add it
120129 // See https://json-schema.org/latest/json-schema-core.html#rfc.section.6.4
121- if (null !== $ iri = $ resourceMetadata ->getIri ()) {
130+ if (null !== $ resourceMetadata && null !== $ iri = $ resourceMetadata ->getIri ()) {
122131 $ definition ['externalDocs ' ] = ['url ' => $ iri ];
123132 }
124133
@@ -200,12 +209,12 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
200209 $ schema ->getDefinitions ()[$ definitionName ]['properties ' ][$ normalizedPropertyName ] = $ propertySchema ;
201210 }
202211
203- private function buildDefinitionName (string $ resourceClass , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?array $ serializerContext = null ): string
212+ private function buildDefinitionName (string $ className , string $ format = 'json ' , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType = null , ?string $ operationName = null , ?array $ serializerContext = null ): string
204213 {
205- [$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ this ->getMetadata ($ resourceClass , $ type , $ operationType , $ operationName , $ serializerContext );
214+ [$ resourceMetadata , $ serializerContext , $ inputOrOutputClass ] = $ this ->getMetadata ($ className , $ type , $ operationType , $ operationName , $ serializerContext );
206215
207- $ prefix = $ resourceMetadata ->getShortName ();
208- if (null !== $ inputOrOutputClass && $ resourceClass !== $ inputOrOutputClass ) {
216+ $ prefix = $ resourceMetadata ? $ resourceMetadata -> getShortName () : ( new \ ReflectionClass ( $ className )) ->getShortName ();
217+ if (null !== $ inputOrOutputClass && $ className !== $ inputOrOutputClass ) {
209218 $ prefix .= ': ' .md5 ($ inputOrOutputClass );
210219 }
211220
@@ -224,14 +233,22 @@ private function buildDefinitionName(string $resourceClass, string $format = 'js
224233 return $ name ;
225234 }
226235
227- private function getMetadata (string $ resourceClass , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType , ?string $ operationName , ?array $ serializerContext ): ?array
236+ private function getMetadata (string $ className , string $ type = Schema::TYPE_OUTPUT , ?string $ operationType , ?string $ operationName , ?array $ serializerContext ): ?array
228237 {
229- $ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ resourceClass );
238+ if (!$ this ->isResourceClass ($ className )) {
239+ return [
240+ null ,
241+ $ serializerContext ,
242+ $ className ,
243+ ];
244+ }
245+
246+ $ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ className );
230247 $ attribute = Schema::TYPE_OUTPUT === $ type ? 'output ' : 'input ' ;
231248 if (null === $ operationType || null === $ operationName ) {
232- $ inputOrOutput = $ resourceMetadata ->getAttribute ($ attribute , ['class ' => $ resourceClass ]);
249+ $ inputOrOutput = $ resourceMetadata ->getAttribute ($ attribute , ['class ' => $ className ]);
233250 } else {
234- $ inputOrOutput = $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , $ attribute , ['class ' => $ resourceClass ], true );
251+ $ inputOrOutput = $ resourceMetadata ->getTypedOperationAttribute ($ operationType , $ operationName , $ attribute , ['class ' => $ className ], true );
235252 }
236253
237254 if (null === ($ inputOrOutput ['class ' ] ?? null )) {
0 commit comments