@@ -37,37 +37,43 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
37
37
use SchemaUriPrefixTrait;
38
38
39
39
private const ITEM_BASE_SCHEMA_NAME = 'HydraItemBaseSchema ' ;
40
+ private const ITEM_BASE_SCHEMA_OUTPUT_NAME = 'HydraOutputBaseSchema ' ;
40
41
private const COLLECTION_BASE_SCHEMA_NAME = 'HydraCollectionBaseSchema ' ;
41
42
private const BASE_PROP = [
42
- 'readOnly ' => true ,
43
43
'type ' => 'string ' ,
44
44
];
45
45
private const BASE_PROPS = [
46
46
'@id ' => self ::BASE_PROP ,
47
47
'@type ' => self ::BASE_PROP ,
48
48
];
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
+ ],
63
65
],
66
+ 'required ' => ['@vocab ' , 'hydra ' ],
67
+ 'additionalProperties ' => true ,
64
68
],
65
- 'required ' => ['@vocab ' , 'hydra ' ],
66
- 'additionalProperties ' => true ,
67
69
],
68
- ],
70
+ ] + self :: BASE_PROPS ,
69
71
],
70
- ] + self ::BASE_PROPS ;
72
+ ];
73
+
74
+ private const ITEM_BASE_SCHEMA_OUTPUT = [
75
+ 'required ' => ['@id ' , '@type ' ],
76
+ ] + self ::ITEM_BASE_SCHEMA ;
71
77
72
78
/**
73
79
* @param array<string, mixed> $defaultContext
@@ -106,47 +112,74 @@ public function buildSchema(string $className, string $format = 'jsonld', string
106
112
$ inputOrOutputClass = $ this ->findOutputClass ($ className , $ type , $ operation , $ serializerContext );
107
113
$ serializerContext ??= $ this ->getSerializerContext ($ operation , $ type );
108
114
}
115
+
109
116
if (null === $ inputOrOutputClass ) {
110
117
// input or output disabled
111
118
return $ this ->schemaFactory ->buildSchema ($ className , $ format , $ type , $ operation , $ schema , $ serializerContext , $ forceCollection );
112
119
}
113
120
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
+
115
132
$ definitionName = $ this ->definitionNameFactory ->create ($ className , $ format , $ className , $ operation , $ serializerContext );
116
133
$ definitions = $ schema ->getDefinitions ();
117
- $ prefix = $ this ->getSchemaUriPrefix ($ schema ->getVersion ());
118
- $ collectionKey = $ schema ->getItemsDefinitionKey ();
119
134
120
- // Already computed
121
- if (!$ collectionKey && isset ($ definitions [$ definitionName ])) {
122
- $ schema ['$ref ' ] = $ prefix .$ definitionName ;
135
+ $ addJsonLdBaseSchema = false ;
123
136
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 );
125
141
}
126
142
127
- $ key = $ schema ->getRootDefinitionKey () ?? $ collectionKey ;
143
+ $ prefix = $ this ->getSchemaUriPrefix ($ schema ->getVersion ());
144
+ $ collectionKey = $ schema ->getItemsDefinitionKey ();
128
145
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 ;
131
150
}
132
151
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 ],
136
161
['$ref ' => $ prefix .$ key ],
137
- ],
138
- ];
162
+ ];
139
163
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
+ ]);
143
172
144
- if (! $ collectionKey ) {
173
+ $ schema -> setDefinitions ( $ definitions );
145
174
$ schema ['$ref ' ] = $ prefix .$ definitionName ;
146
175
147
176
return $ schema ;
148
177
}
149
178
179
+ if (isset ($ definitions [$ key ]['description ' ])) {
180
+ $ definitions [$ definitionName ]['description ' ] = $ definitions [$ key ]['description ' ];
181
+ }
182
+
150
183
// handle hydra:Collection
151
184
if (($ schema ['type ' ] ?? '' ) === 'array ' ) {
152
185
$ hydraPrefix = $ this ->getHydraPrefix ($ serializerContext + $ this ->defaultContext );
@@ -266,4 +299,36 @@ public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
266
299
$ this ->schemaFactory ->setSchemaFactory ($ schemaFactory );
267
300
}
268
301
}
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
+ }
269
334
}
0 commit comments