Completed
Push — master ( d056c8...3b3683 )
by Kévin
03:04
created

getPropertyNameCollectionFactoryContext()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 21
Code Lines 12

Duplication

Lines 12
Ratio 57.14 %

Importance

Changes 0
Metric Value
dl 12
loc 21
rs 8.7624
c 0
b 0
f 0
cc 5
eloc 12
nc 6
nop 1
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Hydra\Serializer;
15
16
use ApiPlatform\Core\Api\OperationMethodResolverInterface;
17
use ApiPlatform\Core\Api\OperationType;
18
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
19
use ApiPlatform\Core\Api\UrlGeneratorInterface;
20
use ApiPlatform\Core\Documentation\Documentation;
21
use ApiPlatform\Core\JsonLd\ContextBuilderInterface;
22
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
23
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
24
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
25
use ApiPlatform\Core\Metadata\Property\SubresourceMetadata;
26
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
27
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
28
use Symfony\Component\PropertyInfo\Type;
29
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
30
31
/**
32
 * Creates a machine readable Hydra API documentation.
33
 *
34
 * @author Kévin Dunglas <[email protected]>
35
 */
36
final class DocumentationNormalizer implements NormalizerInterface
37
{
38
    const FORMAT = 'jsonld';
39
40
    private $resourceMetadataFactory;
41
    private $propertyNameCollectionFactory;
42
    private $propertyMetadataFactory;
43
    private $resourceClassResolver;
44
    private $operationMethodResolver;
45
    private $urlGenerator;
46
47 View Code Duplication
    public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, UrlGeneratorInterface $urlGenerator)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
48
    {
49
        $this->resourceMetadataFactory = $resourceMetadataFactory;
50
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
51
        $this->propertyMetadataFactory = $propertyMetadataFactory;
52
        $this->resourceClassResolver = $resourceClassResolver;
53
        $this->operationMethodResolver = $operationMethodResolver;
54
        $this->urlGenerator = $urlGenerator;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function normalize($object, $format = null, array $context = [])
61
    {
62
        $classes = [];
63
        $entrypointProperties = [];
64
65
        foreach ($object->getResourceNameCollection() as $resourceClass) {
66
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
67
            $shortName = $resourceMetadata->getShortName();
68
            $prefixedShortName = $resourceMetadata->getIri() ?? "#$shortName";
69
70
            $this->populateEntrypointProperties($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $entrypointProperties);
71
            $classes[] = $this->getClass($resourceClass, $resourceMetadata, $shortName, $prefixedShortName);
72
        }
73
74
        return $this->computeDoc($object, $this->getClasses($entrypointProperties, $classes));
75
    }
76
77
    /**
78
     * Populates entrypoint properties.
79
     *
80
     * @param string           $resourceClass
81
     * @param ResourceMetadata $resourceMetadata
82
     * @param string           $shortName
83
     * @param string           $prefixedShortName
84
     * @param array            $entrypointProperties
85
     */
86
    private function populateEntrypointProperties(string $resourceClass, ResourceMetadata $resourceMetadata, string $shortName, string $prefixedShortName, array &$entrypointProperties)
87
    {
88
        $hydraCollectionOperations = $this->getHydraOperations($resourceClass, $resourceMetadata, $prefixedShortName, true);
89
        if (empty($hydraCollectionOperations)) {
90
            return;
91
        }
92
93
        $entrypointProperties[] = [
94
            '@type' => 'hydra:SupportedProperty',
95
            'hydra:property' => [
96
                '@id' => sprintf('#Entrypoint/%s', lcfirst($shortName)),
97
                '@type' => 'hydra:Link',
98
                'domain' => '#Entrypoint',
99
                'rdfs:label' => "The collection of $shortName resources",
100
                'rdfs:range' => [
101
                    'hydra:Collection',
102
                    [
103
                        'owl:equivalentClass' => [
104
                            'owl:onProperty' => 'hydra:member',
105
                            'owl:allValuesFrom' => "#$shortName",
106
                        ],
107
                    ],
108
                ],
109
                'hydra:supportedOperation' => $hydraCollectionOperations,
110
            ],
111
            'hydra:title' => "The collection of $shortName resources",
112
            'hydra:readable' => true,
113
            'hydra:writable' => false,
114
        ];
115
    }
116
117
    /**
118
     * Gets a Hydra class.
119
     *
120
     * @param string           $resourceClass
121
     * @param ResourceMetadata $resourceMetadata
122
     * @param string           $shortName
123
     * @param string           $prefixedShortName
124
     *
125
     * @return array
126
     */
127
    private function getClass(string $resourceClass, ResourceMetadata $resourceMetadata, string $shortName, string $prefixedShortName): array
128
    {
129
        $class = [
130
            '@id' => $prefixedShortName,
131
            '@type' => 'hydra:Class',
132
            'rdfs:label' => $shortName,
133
            'hydra:title' => $shortName,
134
            'hydra:supportedProperty' => $this->getHydraProperties($resourceClass, $resourceMetadata, $shortName, $prefixedShortName),
135
            'hydra:supportedOperation' => $this->getHydraOperations($resourceClass, $resourceMetadata, $prefixedShortName, false),
136
        ];
137
138
        if (null !== $description = $resourceMetadata->getDescription()) {
139
            $class['hydra:description'] = $description;
140
        }
141
142
        return $class;
143
    }
144
145
    /**
146
     * Gets the context for the property name factory.
147
     *
148
     * @param ResourceMetadata $resourceMetadata
149
     *
150
     * @return array
151
     */
152
    private function getPropertyNameCollectionFactoryContext(ResourceMetadata $resourceMetadata): array
153
    {
154
        $attributes = $resourceMetadata->getAttributes();
155
        $context = [];
156
157 View Code Duplication
        if (isset($attributes['normalization_context']['groups'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
            $context['serializer_groups'] = $attributes['normalization_context']['groups'];
159
        }
160
161 View Code Duplication
        if (isset($attributes['denormalization_context']['groups'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
            if (isset($context['serializer_groups'])) {
163
                foreach ($attributes['denormalization_context']['groups'] as $groupName) {
164
                    $context['serializer_groups'][] = $groupName;
165
                }
166
            } else {
167
                $context['serializer_groups'] = $attributes['denormalization_context']['groups'];
168
            }
169
        }
170
171
        return $context;
172
    }
173
174
    /**
175
     * Gets Hydra properties.
176
     *
177
     * @param string           $resourceClass
178
     * @param ResourceMetadata $resourceMetadata
179
     * @param string           $shortName
180
     * @param string           $prefixedShortName
181
     *
182
     * @return array
183
     */
184
    private function getHydraProperties(string $resourceClass, ResourceMetadata $resourceMetadata, string $shortName, string $prefixedShortName): array
185
    {
186
        $properties = [];
187
        foreach ($this->propertyNameCollectionFactory->create($resourceClass, $this->getPropertyNameCollectionFactoryContext($resourceMetadata)) as $propertyName) {
188
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
189
            if (true === $propertyMetadata->isIdentifier() && false === $propertyMetadata->isWritable()) {
190
                continue;
191
            }
192
193
            $properties[] = $this->getProperty($propertyMetadata, $propertyName, $prefixedShortName, $shortName);
194
        }
195
196
        return $properties;
197
    }
198
199
    /**
200
     * Gets Hydra operations.
201
     *
202
     * @param string           $resourceClass
203
     * @param ResourceMetadata $resourceMetadata
204
     * @param string           $prefixedShortName
205
     * @param bool             $collection
206
     *
207
     * @return array
208
     */
209
    private function getHydraOperations(string $resourceClass, ResourceMetadata $resourceMetadata, string $prefixedShortName, bool $collection): array
210
    {
211
        if (null === $operations = $collection ? $resourceMetadata->getCollectionOperations() : $resourceMetadata->getItemOperations()) {
212
            return [];
213
        }
214
215
        $hydraOperations = [];
216
        foreach ($operations as $operationName => $operation) {
217
            $hydraOperations[] = $this->getHydraOperation($resourceClass, $resourceMetadata, $operationName, $operation, $prefixedShortName, $collection ? OperationType::COLLECTION : OperationType::ITEM);
218
        }
219
220
        foreach ($this->propertyNameCollectionFactory->create($resourceClass, $this->getPropertyNameCollectionFactoryContext($resourceMetadata)) as $propertyName) {
221
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
222
223
            if (!$propertyMetadata->hasSubresource()) {
224
                continue;
225
            }
226
227
            $subresourceMetadata = $this->resourceMetadataFactory->create($propertyMetadata->getSubresource()->getResourceClass());
228
            $prefixedShortName = "#{$subresourceMetadata->getShortName()}";
229
230
            $hydraOperations[] = $this->getHydraOperation($resourceClass, $subresourceMetadata, $operationName, $operation, $prefixedShortName, OperationType::SUBRESOURCE, $propertyMetadata->getSubresource());
0 ignored issues
show
Bug introduced by
The variable $operationName seems to be defined by a foreach iteration on line 216. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
Bug introduced by
The variable $operation seems to be defined by a foreach iteration on line 216. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
231
        }
232
233
        return $hydraOperations;
234
    }
235
236
    /**
237
     * Gets and populates if applicable a Hydra operation.
238
     *
239
     * @param string              $resourceClass
240
     * @param ResourceMetadata    $resourceMetadata
241
     * @param string              $operationName
242
     * @param array               $operation
243
     * @param string              $prefixedShortName
244
     * @param string              $operationType
245
     * @param SubresourceMetadata $operationType
246
     *
247
     * @return array
248
     */
249
    private function getHydraOperation(string $resourceClass, ResourceMetadata $resourceMetadata, string $operationName, array $operation, string $prefixedShortName, string $operationType, SubresourceMetadata $subresourceMetadata = null): array
250
    {
251
        if (OperationType::COLLECTION === $operationType) {
252
            $method = $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $operationName);
253
        } elseif (OperationType::ITEM === $operationType) {
254
            $method = $this->operationMethodResolver->getItemOperationMethod($resourceClass, $operationName);
255
        } else {
256
            $method = 'GET';
257
        }
258
259
        $hydraOperation = $operation['hydra_context'] ?? [];
260
        $shortName = $resourceMetadata->getShortName();
261
262
        if ('GET' === $method && OperationType::COLLECTION === $operationType) {
263
            $hydraOperation += [
264
                '@type' => ['hydra:Operation', 'schema:FindAction'],
265
                'hydra:title' => "Retrieves the collection of $shortName resources.",
266
                'returns' => 'hydra:Collection',
267
            ];
268
        } elseif ('GET' === $method && OperationType::SUBRESOURCE === $operationType) {
269
            $hydraOperation += [
270
                '@type' => ['hydra:Operation', 'schema:FindAction'],
271
                'hydra:title' => $subresourceMetadata->isCollection() ? "Retrieves the collection of $shortName resources." : "Retrieves a $shortName resource.",
0 ignored issues
show
Bug introduced by
It seems like $subresourceMetadata is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
272
                'returns' => "#$shortName",
273
            ];
274 View Code Duplication
        } elseif ('GET' === $method) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
            $hydraOperation += [
276
                '@type' => ['hydra:Operation', 'schema:FindAction'],
277
                'hydra:title' => "Retrieves $shortName resource.",
278
                'returns' => $prefixedShortName,
279
            ];
280
        } elseif ('POST' === $method) {
281
            $hydraOperation += [
282
                '@type' => ['hydra:Operation', 'schema:CreateAction'],
283
                'hydra:title' => "Creates a $shortName resource.",
284
                'returns' => $prefixedShortName,
285
                'expects' => $prefixedShortName,
286
            ];
287 View Code Duplication
        } elseif ('PUT' === $method) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
            $hydraOperation += [
289
                '@type' => ['hydra:Operation', 'schema:ReplaceAction'],
290
                'hydra:title' => "Replaces the $shortName resource.",
291
                'returns' => $prefixedShortName,
292
                'expects' => $prefixedShortName,
293
            ];
294
        } elseif ('DELETE' === $method) {
295
            $hydraOperation += [
296
                '@type' => ['hydra:Operation', 'schema:DeleteAction'],
297
                'hydra:title' => "Deletes the $shortName resource.",
298
                'returns' => 'owl:Nothing',
299
            ];
300
        }
301
302
        $hydraOperation['hydra:method'] ?? $hydraOperation['hydra:method'] = $method;
303
304
        if (!isset($hydraOperation['rdfs:label']) && isset($hydraOperation['hydra:title'])) {
305
            $hydraOperation['rdfs:label'] = $hydraOperation['hydra:title'];
306
        }
307
308
        ksort($hydraOperation);
309
310
        return $hydraOperation;
311
    }
312
313
    /**
314
     * Gets the range of the property.
315
     *
316
     * @param PropertyMetadata $propertyMetadata
317
     *
318
     * @return string|null
319
     */
320
    private function getRange(PropertyMetadata $propertyMetadata)
321
    {
322
        $jsonldContext = $propertyMetadata->getAttributes()['jsonld_context'] ?? [];
323
324
        if (isset($jsonldContext['@type'])) {
325
            return $jsonldContext['@type'];
326
        }
327
328
        if (null === $type = $propertyMetadata->getType()) {
329
            return null;
330
        }
331
332
        if ($type->isCollection() && null !== $collectionType = $type->getCollectionValueType()) {
333
            $type = $collectionType;
334
        }
335
336
        switch ($type->getBuiltinType()) {
337
            case Type::BUILTIN_TYPE_STRING:
338
                return 'xmls:string';
339
            case Type::BUILTIN_TYPE_INT:
340
                return 'xmls:integer';
341
            case Type::BUILTIN_TYPE_FLOAT:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
342
                return 'xmls:decimal';
343
            case Type::BUILTIN_TYPE_BOOL:
344
                return 'xmls:boolean';
345
            case Type::BUILTIN_TYPE_OBJECT:
346
                if (null === $className = $type->getClassName()) {
347
                    return null;
348
                }
349
350
                if (is_a($className, \DateTimeInterface::class, true)) {
351
                    return 'xmls:dateTime';
352
                }
353
354
                if ($this->resourceClassResolver->isResourceClass($className)) {
355
                    $resourceMetadata = $this->resourceMetadataFactory->create($className);
356
357
                    return $resourceMetadata->getIri() ?? "#{$resourceMetadata->getShortName()}";
358
                }
359
                break;
360
        }
361
    }
362
363
    /**
364
     * Builds the classes array.
365
     *
366
     * @param array $entrypointProperties
367
     * @param array $classes
368
     *
369
     * @return array
370
     */
371
    private function getClasses(array $entrypointProperties, array $classes): array
372
    {
373
        $classes[] = [
374
            '@id' => '#Entrypoint',
375
            '@type' => 'hydra:Class',
376
            'hydra:title' => 'The API entrypoint',
377
            'hydra:supportedProperty' => $entrypointProperties,
378
            'hydra:supportedOperation' => [
379
                '@type' => 'hydra:Operation',
380
                'hydra:method' => 'GET',
381
                'rdfs:label' => 'The API entrypoint.',
382
                'returns' => '#EntryPoint',
383
            ],
384
        ];
385
386
        // Constraint violation
387
        $classes[] = [
388
            '@id' => '#ConstraintViolation',
389
            '@type' => 'hydra:Class',
390
            'hydra:title' => 'A constraint violation',
391
            'hydra:supportedProperty' => [
392
                [
393
                    '@type' => 'hydra:SupportedProperty',
394
                    'hydra:property' => [
395
                        '@id' => '#ConstraintViolation/propertyPath',
396
                        '@type' => 'rdf:Property',
397
                        'rdfs:label' => 'propertyPath',
398
                        'domain' => '#ConstraintViolation',
399
                        'range' => 'xmls:string',
400
                    ],
401
                    'hydra:title' => 'propertyPath',
402
                    'hydra:description' => 'The property path of the violation',
403
                    'hydra:readable' => true,
404
                    'hydra:writable' => false,
405
                ],
406
                [
407
                    '@type' => 'hydra:SupportedProperty',
408
                    'hydra:property' => [
409
                        '@id' => '#ConstraintViolation/message',
410
                        '@type' => 'rdf:Property',
411
                        'rdfs:label' => 'message',
412
                        'domain' => '#ConstraintViolation',
413
                        'range' => 'xmls:string',
414
                    ],
415
                    'hydra:title' => 'message',
416
                    'hydra:description' => 'The message associated with the violation',
417
                    'hydra:readable' => true,
418
                    'hydra:writable' => false,
419
                ],
420
            ],
421
        ];
422
423
        // Constraint violation list
424
        $classes[] = [
425
            '@id' => '#ConstraintViolationList',
426
            '@type' => 'hydra:Class',
427
            'subClassOf' => 'hydra:Error',
428
            'hydra:title' => 'A constraint violation list',
429
            'hydra:supportedProperty' => [
430
                [
431
                    '@type' => 'hydra:SupportedProperty',
432
                    'hydra:property' => [
433
                        '@id' => '#ConstraintViolationList/violations',
434
                        '@type' => 'rdf:Property',
435
                        'rdfs:label' => 'violations',
436
                        'domain' => '#ConstraintViolationList',
437
                        'range' => '#ConstraintViolation',
438
                    ],
439
                    'hydra:title' => 'violations',
440
                    'hydra:description' => 'The violations',
441
                    'hydra:readable' => true,
442
                    'hydra:writable' => false,
443
                ],
444
            ],
445
        ];
446
447
        return $classes;
448
    }
449
450
    /**
451
     * Gets a property definition.
452
     *
453
     * @param PropertyMetadata $propertyMetadata
454
     * @param string           $propertyName
455
     * @param string           $prefixedShortName
456
     * @param string           $shortName
457
     *
458
     * @return array
459
     */
460
    private function getProperty(PropertyMetadata $propertyMetadata, string $propertyName, string $prefixedShortName, string $shortName): array
461
    {
462
        $propertyData = [
463
            '@id' => $propertyMetadata->getIri() ?? "#$shortName/$propertyName",
464
            '@type' => $propertyMetadata->isReadableLink() ? 'rdf:Property' : 'hydra:Link',
465
            'rdfs:label' => $propertyName,
466
            'domain' => $prefixedShortName,
467
        ];
468
469
        $type = $propertyMetadata->getType();
470
471
        if (null !== $type && !$type->isCollection() && (null !== $className = $type->getClassName()) && $this->resourceClassResolver->isResourceClass($className)) {
472
            $propertyData['owl:maxCardinality'] = 1;
473
        }
474
475
        $property = [
476
            '@type' => 'hydra:SupportedProperty',
477
            'hydra:property' => $propertyData,
478
            'hydra:title' => $propertyName,
479
            'hydra:required' => $propertyMetadata->isRequired(),
480
            'hydra:readable' => $propertyMetadata->isReadable(),
481
            'hydra:writable' => $propertyMetadata->isWritable(),
482
        ];
483
484
        if (null !== $range = $this->getRange($propertyMetadata)) {
485
            $property['hydra:property']['range'] = $range;
486
        }
487
488
        if (null !== $description = $propertyMetadata->getDescription()) {
489
            $property['hydra:description'] = $description;
490
        }
491
492
        return $property;
493
    }
494
495
    /**
496
     * Computes the documentation.
497
     *
498
     * @param Documentation $object
499
     * @param array         $classes
500
     *
501
     * @return array
502
     */
503
    private function computeDoc(Documentation $object, array $classes): array
504
    {
505
        $doc = ['@context' => $this->getContext(), '@id' => $this->urlGenerator->generate('api_doc', ['_format' => self::FORMAT]), '@type' => 'hydra:ApiDocumentation'];
506
507
        if ('' !== $object->getTitle()) {
508
            $doc['hydra:title'] = $object->getTitle();
509
        }
510
511
        if ('' !== $object->getDescription()) {
512
            $doc['hydra:description'] = $object->getDescription();
513
        }
514
515
        $doc['hydra:entrypoint'] = $this->urlGenerator->generate('api_entrypoint');
516
        $doc['hydra:supportedClass'] = $classes;
517
518
        return $doc;
519
    }
520
521
    /**
522
     * Builds the JSON-LD context for the API documentation.
523
     *
524
     * @return array
525
     */
526
    private function getContext(): array
527
    {
528
        return [
529
            '@vocab' => $this->urlGenerator->generate('api_doc', ['_format' => self::FORMAT], UrlGeneratorInterface::ABS_URL).'#',
530
            'hydra' => ContextBuilderInterface::HYDRA_NS,
531
            'rdf' => ContextBuilderInterface::RDF_NS,
532
            'rdfs' => ContextBuilderInterface::RDFS_NS,
533
            'xmls' => ContextBuilderInterface::XML_NS,
534
            'owl' => ContextBuilderInterface::OWL_NS,
535
            'schema' => ContextBuilderInterface::SCHEMA_ORG_NS,
536
            'domain' => ['@id' => 'rdfs:domain', '@type' => '@id'],
537
            'range' => ['@id' => 'rdfs:range', '@type' => '@id'],
538
            'subClassOf' => ['@id' => 'rdfs:subClassOf', '@type' => '@id'],
539
            'expects' => ['@id' => 'hydra:expects', '@type' => '@id'],
540
            'returns' => ['@id' => 'hydra:returns', '@type' => '@id'],
541
        ];
542
    }
543
544
    /**
545
     * {@inheritdoc}
546
     */
547
    public function supportsNormalization($data, $format = null, array $context = [])
548
    {
549
        return self::FORMAT === $format && $data instanceof Documentation;
550
    }
551
}
552