@@ -37,37 +37,43 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
3737 use SchemaUriPrefixTrait;
3838
3939 private const ITEM_BASE_SCHEMA_NAME = 'HydraItemBaseSchema ' ;
40+ private const ITEM_BASE_SCHEMA_OUTPUT_NAME = 'HydraOutputBaseSchema ' ;
4041 private const COLLECTION_BASE_SCHEMA_NAME = 'HydraCollectionBaseSchema ' ;
4142 private const BASE_PROP = [
42- 'readOnly ' => true ,
4343 'type ' => 'string ' ,
4444 ];
4545 private const BASE_PROPS = [
4646 '@id ' => self ::BASE_PROP ,
4747 '@type ' => self ::BASE_PROP ,
4848 ];
49- private const BASE_ROOT_PROPS = [
50- '@context ' => [
51- 'readOnly ' => true ,
52- 'oneOf ' => [
53- ['type ' => 'string ' ],
54- [
55- 'type ' => 'object ' ,
56- 'properties ' => [
57- '@vocab ' => [
58- 'type ' => 'string ' ,
59- ],
60- 'hydra ' => [
61- 'type ' => 'string ' ,
62- 'enum ' => [ContextBuilder::HYDRA_NS ],
49+ private const ITEM_BASE_SCHEMA = [
50+ 'type ' => 'object ' ,
51+ 'properties ' => [
52+ '@context ' => [
53+ 'oneOf ' => [
54+ ['type ' => 'string ' ],
55+ [
56+ 'type ' => 'object ' ,
57+ 'properties ' => [
58+ '@vocab ' => [
59+ 'type ' => 'string ' ,
60+ ],
61+ 'hydra ' => [
62+ 'type ' => 'string ' ,
63+ 'enum ' => [ContextBuilder::HYDRA_NS ],
64+ ],
6365 ],
66+ 'required ' => ['@vocab ' , 'hydra ' ],
67+ 'additionalProperties ' => true ,
6468 ],
65- 'required ' => ['@vocab ' , 'hydra ' ],
66- 'additionalProperties ' => true ,
6769 ],
68- ],
70+ ] + self :: BASE_PROPS ,
6971 ],
70- ] + self ::BASE_PROPS ;
72+ ];
73+
74+ private const ITEM_BASE_SCHEMA_OUTPUT = [
75+ 'required ' => ['@id ' , '@type ' ],
76+ ] + self ::ITEM_BASE_SCHEMA ;
7177
7278 /**
7379 * @param array<string, mixed> $defaultContext
@@ -106,47 +112,74 @@ public function buildSchema(string $className, string $format = 'jsonld', string
106112 $ inputOrOutputClass = $ this ->findOutputClass ($ className , $ type , $ operation , $ serializerContext );
107113 $ serializerContext ??= $ this ->getSerializerContext ($ operation , $ type );
108114 }
115+
109116 if (null === $ inputOrOutputClass ) {
110117 // input or output disabled
111118 return $ this ->schemaFactory ->buildSchema ($ className , $ format , $ type , $ operation , $ schema , $ serializerContext , $ forceCollection );
112119 }
113120
114- $ schema = $ this ->schemaFactory ->buildSchema ($ className , 'json ' , $ type , $ operation , $ schema , $ serializerContext , $ forceCollection );
121+ if ($ schema ) {
122+ $ definitions = $ schema ->getDefinitions ();
123+ $ jsonDefinitionName = $ this ->definitionNameFactory ->create ($ className , 'json ' , $ className , $ operation , $ serializerContext );
124+
125+ if (!isset ($ definitions [$ jsonDefinitionName ])) {
126+ $ schema = $ this ->schemaFactory ->buildSchema ($ className , 'json ' , $ type , $ operation , $ schema , $ serializerContext , $ forceCollection );
127+ }
128+ } else {
129+ $ schema = $ this ->schemaFactory ->buildSchema ($ className , 'json ' , $ type , $ operation , $ schema , $ serializerContext , $ forceCollection );
130+ }
131+
115132 $ definitionName = $ this ->definitionNameFactory ->create ($ className , $ format , $ className , $ operation , $ serializerContext );
116133 $ definitions = $ schema ->getDefinitions ();
117- $ prefix = $ this ->getSchemaUriPrefix ($ schema ->getVersion ());
118- $ collectionKey = $ schema ->getItemsDefinitionKey ();
119134
120- // Already computed
121- if (!$ collectionKey && isset ($ definitions [$ definitionName ])) {
122- $ schema ['$ref ' ] = $ prefix .$ definitionName ;
135+ $ addJsonLdBaseSchema = false ;
123136
124- return $ schema ;
137+ if (!isset ($ definitions [$ definitionName ])) {
138+ $ addJsonLdBaseSchema = true ;
139+ // only compute json-ld references, skip the scalar properties as they're inherited from the json format
140+ $ schema = $ this ->schemaFactory ->buildSchema ($ className , 'jsonld ' , $ type , $ operation , $ schema , [self ::COMPUTE_REFERENCES => true ] + $ serializerContext , $ forceCollection );
125141 }
126142
127- $ key = $ schema ->getRootDefinitionKey () ?? $ collectionKey ;
143+ $ prefix = $ this ->getSchemaUriPrefix ($ schema ->getVersion ());
144+ $ collectionKey = $ schema ->getItemsDefinitionKey ();
128145
129- if (!isset ($ definitions [self ::ITEM_BASE_SCHEMA_NAME ])) {
130- $ definitions [self ::ITEM_BASE_SCHEMA_NAME ] = ['type ' => 'object ' , 'properties ' => self ::BASE_ROOT_PROPS ];
146+ $ key = $ schema ->getRootDefinitionKey () ?? $ collectionKey ;
147+ $ name = Schema::TYPE_OUTPUT === $ type ? self ::ITEM_BASE_SCHEMA_NAME : self ::ITEM_BASE_SCHEMA_OUTPUT_NAME ;
148+ if (!isset ($ definitions [$ name ])) {
149+ $ definitions [$ name ] = Schema::TYPE_OUTPUT === $ type ? self ::ITEM_BASE_SCHEMA_OUTPUT : self ::ITEM_BASE_SCHEMA ;
131150 }
132151
133- $ definitions [$ definitionName ] = [
134- 'allOf ' => [
135- ['$ref ' => $ prefix .self ::ITEM_BASE_SCHEMA_NAME ],
152+ if (!$ collectionKey && isset ($ definitions [$ definitionName ])) {
153+ if (!$ addJsonLdBaseSchema ) {
154+ $ schema ['$ref ' ] = $ prefix .$ definitionName ;
155+
156+ return $ schema ;
157+ }
158+
159+ $ allOf = [
160+ ['$ref ' => $ prefix .$ name ],
136161 ['$ref ' => $ prefix .$ key ],
137- ],
138- ];
162+ ];
139163
140- if (isset ($ definitions [$ key ]['description ' ])) {
141- $ definitions [$ definitionName ]['description ' ] = $ definitions [$ key ]['description ' ];
142- }
164+ // if there're no properties, we did not compute any json-ld specific reference
165+ if (isset ($ definitions [$ definitionName ]['properties ' ])) {
166+ $ allOf [] = $ definitions [$ definitionName ];
167+ }
168+
169+ $ definitions [$ definitionName ] = new \ArrayObject ([
170+ 'allOf ' => $ allOf ,
171+ ]);
143172
144- if (! $ collectionKey ) {
173+ $ schema -> setDefinitions ( $ definitions );
145174 $ schema ['$ref ' ] = $ prefix .$ definitionName ;
146175
147176 return $ schema ;
148177 }
149178
179+ if (isset ($ definitions [$ key ]['description ' ])) {
180+ $ definitions [$ definitionName ]['description ' ] = $ definitions [$ key ]['description ' ];
181+ }
182+
150183 // handle hydra:Collection
151184 if (($ schema ['type ' ] ?? '' ) === 'array ' ) {
152185 $ hydraPrefix = $ this ->getHydraPrefix ($ serializerContext + $ this ->defaultContext );
@@ -266,4 +299,36 @@ public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
266299 $ this ->schemaFactory ->setSchemaFactory ($ schemaFactory );
267300 }
268301 }
302+
303+ private function collectRefs (array |\ArrayObject $ baseFormatSchema , $ prefix )
304+ {
305+ if (!$ key = $ this ->getSubSchemaKey ($ baseFormatSchema )) {
306+ return null ;
307+ }
308+
309+ foreach ($ baseFormatSchema [$ key ] as $ k => $ s ) {
310+ if (isset ($ s ['$ref ' ])) {
311+ dd ($ s ['$ref ' ], $ prefix );
312+ }
313+
314+ if (!$ s instanceof \ArrayObject) {
315+ continue ;
316+ }
317+
318+ $ this ->collectRefs ($ s , $ prefix );
319+ }
320+
321+ return [];
322+ }
323+
324+ private function getSubSchemaKey (array |\ArrayObject $ subSchema ): ?string
325+ {
326+ foreach (['properties ' , 'items ' , 'allOf ' , 'anyOf ' , 'oneOf ' ] as $ key ) {
327+ if (isset ($ subSchema [$ key ])) {
328+ return $ key ;
329+ }
330+ }
331+
332+ return null ;
333+ }
269334}
0 commit comments