• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 6067528200

04 Sep 2023 12:12AM UTC coverage: 36.875% (-21.9%) from 58.794%
6067528200

Pull #5791

github

web-flow
Merge 64157e578 into d09cfc9d2
Pull Request #5791: fix: strip down any sql function name

3096 of 3096 new or added lines in 205 files covered. (100.0%)

9926 of 26918 relevant lines covered (36.87%)

6.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.0
/src/Hydra/Serializer/DocumentationNormalizer.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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\Hydra\Serializer;
15

16
use ApiPlatform\Api\ResourceClassResolverInterface;
17
use ApiPlatform\Api\UrlGeneratorInterface;
18
use ApiPlatform\Documentation\Documentation;
19
use ApiPlatform\JsonLd\ContextBuilderInterface;
20
use ApiPlatform\Metadata\ApiProperty;
21
use ApiPlatform\Metadata\ApiResource;
22
use ApiPlatform\Metadata\CollectionOperationInterface;
23
use ApiPlatform\Metadata\HttpOperation;
24
use ApiPlatform\Metadata\Operation;
25
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
26
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
27
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
28
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
29
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
30
use Symfony\Component\PropertyInfo\Type;
31
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
32
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
33
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
34
use Symfony\Component\Serializer\Serializer;
35

36
/**
37
 * Creates a machine readable Hydra API documentation.
38
 *
39
 * @author Kévin Dunglas <dunglas@gmail.com>
40
 */
41
final class DocumentationNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
42
{
43
    public const FORMAT = 'jsonld';
44

45
    public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly UrlGeneratorInterface $urlGenerator, private readonly ?NameConverterInterface $nameConverter = null)
46
    {
47
    }
87✔
48

49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
53
    {
54
        $classes = [];
6✔
55
        $entrypointProperties = [];
6✔
56

57
        foreach ($object->getResourceNameCollection() as $resourceClass) {
6✔
58
            $resourceMetadataCollection = $this->resourceMetadataFactory->create($resourceClass);
6✔
59

60
            $resourceMetadata = $resourceMetadataCollection[0];
6✔
61
            $shortName = $resourceMetadata->getShortName();
6✔
62
            $prefixedShortName = $resourceMetadata->getTypes()[0] ?? "#$shortName";
6✔
63
            $this->populateEntrypointProperties($resourceMetadata, $shortName, $prefixedShortName, $entrypointProperties, $resourceMetadataCollection);
6✔
64
            $classes[] = $this->getClass($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $context, $resourceMetadataCollection);
6✔
65
        }
66

67
        return $this->computeDoc($object, $this->getClasses($entrypointProperties, $classes));
6✔
68
    }
69

70
    /**
71
     * Populates entrypoint properties.
72
     */
73
    private function populateEntrypointProperties(ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array &$entrypointProperties, ResourceMetadataCollection $resourceMetadataCollection = null): void
74
    {
75
        $hydraCollectionOperations = $this->getHydraOperations(true, $resourceMetadataCollection);
6✔
76
        if (empty($hydraCollectionOperations)) {
6✔
77
            return;
×
78
        }
79

80
        $entrypointProperty = [
6✔
81
            '@type' => 'hydra:SupportedProperty',
6✔
82
            'hydra:property' => [
6✔
83
                '@id' => sprintf('#Entrypoint/%s', lcfirst($shortName)),
6✔
84
                '@type' => 'hydra:Link',
6✔
85
                'domain' => '#Entrypoint',
6✔
86
                'rdfs:label' => "The collection of $shortName resources",
6✔
87
                'rdfs:range' => [
6✔
88
                    ['@id' => 'hydra:Collection'],
6✔
89
                    [
6✔
90
                        'owl:equivalentClass' => [
6✔
91
                            'owl:onProperty' => ['@id' => 'hydra:member'],
6✔
92
                            'owl:allValuesFrom' => ['@id' => $prefixedShortName],
6✔
93
                        ],
6✔
94
                    ],
6✔
95
                ],
6✔
96
                'hydra:supportedOperation' => $hydraCollectionOperations,
6✔
97
            ],
6✔
98
            'hydra:title' => "The collection of $shortName resources",
6✔
99
            'hydra:readable' => true,
6✔
100
            'hydra:writeable' => false,
6✔
101
        ];
6✔
102

103
        if ($resourceMetadata->getDeprecationReason()) {
6✔
104
            $entrypointProperty['owl:deprecated'] = true;
×
105
        }
106

107
        $entrypointProperties[] = $entrypointProperty;
6✔
108
    }
109

110
    /**
111
     * Gets a Hydra class.
112
     */
113
    private function getClass(string $resourceClass, ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array $context, ResourceMetadataCollection $resourceMetadataCollection = null): array
114
    {
115
        $description = $resourceMetadata->getDescription();
6✔
116
        $isDeprecated = $resourceMetadata->getDeprecationReason();
6✔
117

118
        $class = [
6✔
119
            '@id' => $prefixedShortName,
6✔
120
            '@type' => 'hydra:Class',
6✔
121
            'rdfs:label' => $shortName,
6✔
122
            'hydra:title' => $shortName,
6✔
123
            'hydra:supportedProperty' => $this->getHydraProperties($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $context),
6✔
124
            'hydra:supportedOperation' => $this->getHydraOperations(false, $resourceMetadataCollection),
6✔
125
        ];
6✔
126

127
        if (null !== $description) {
6✔
128
            $class['hydra:description'] = $description;
6✔
129
        }
130

131
        if ($isDeprecated) {
6✔
132
            $class['owl:deprecated'] = true;
×
133
        }
134

135
        return $class;
6✔
136
    }
137

138
    /**
139
     * Creates context for property metatata factories.
140
     */
141
    private function getPropertyMetadataFactoryContext(ApiResource $resourceMetadata): array
142
    {
143
        $normalizationGroups = $resourceMetadata->getNormalizationContext()[AbstractNormalizer::GROUPS] ?? null;
6✔
144
        $denormalizationGroups = $resourceMetadata->getDenormalizationContext()[AbstractNormalizer::GROUPS] ?? null;
6✔
145
        $propertyContext = [
6✔
146
            'normalization_groups' => $normalizationGroups,
6✔
147
            'denormalization_groups' => $denormalizationGroups,
6✔
148
        ];
6✔
149
        $propertyNameContext = [];
6✔
150

151
        if ($normalizationGroups) {
6✔
152
            $propertyNameContext['serializer_groups'] = $normalizationGroups;
×
153
        }
154

155
        if (!$denormalizationGroups) {
6✔
156
            return [$propertyNameContext, $propertyContext];
6✔
157
        }
158

159
        if (!isset($propertyNameContext['serializer_groups'])) {
×
160
            $propertyNameContext['serializer_groups'] = $denormalizationGroups;
×
161

162
            return [$propertyNameContext, $propertyContext];
×
163
        }
164

165
        foreach ($denormalizationGroups as $group) {
×
166
            $propertyNameContext['serializer_groups'][] = $group;
×
167
        }
168

169
        return [$propertyNameContext, $propertyContext];
×
170
    }
171

172
    /**
173
     * Gets Hydra properties.
174
     */
175
    private function getHydraProperties(string $resourceClass, ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array $context): array
176
    {
177
        $classes = [];
6✔
178

179
        $classes[$resourceClass] = true;
6✔
180
        foreach ($resourceMetadata->getOperations() as $operation) {
6✔
181
            /** @var Operation $operation */
182
            if (!$operation instanceof CollectionOperationInterface) {
6✔
183
                continue;
6✔
184
            }
185

186
            $inputMetadata = $operation->getInput();
6✔
187
            if (null !== $inputClass = $inputMetadata['class'] ?? null) {
6✔
188
                $classes[$inputClass] = true;
3✔
189
            }
190

191
            $outputMetadata = $operation->getOutput();
6✔
192
            if (null !== $outputClass = $outputMetadata['class'] ?? null) {
6✔
193
                $classes[$outputClass] = true;
3✔
194
            }
195
        }
196

197
        /** @var string[] $classes */
198
        $classes = array_keys($classes);
6✔
199
        $properties = [];
6✔
200
        [$propertyNameContext, $propertyContext] = $this->getPropertyMetadataFactoryContext($resourceMetadata);
6✔
201

202
        foreach ($classes as $class) {
6✔
203
            foreach ($this->propertyNameCollectionFactory->create($class, $propertyNameContext) as $propertyName) {
6✔
204
                $propertyMetadata = $this->propertyMetadataFactory->create($class, $propertyName, $propertyContext);
6✔
205

206
                if (true === $propertyMetadata->isIdentifier() && false === $propertyMetadata->isWritable()) {
6✔
207
                    continue;
×
208
                }
209

210
                if ($this->nameConverter) {
6✔
211
                    $propertyName = $this->nameConverter->normalize($propertyName, $class, self::FORMAT, $context);
3✔
212
                }
213

214
                $properties[] = $this->getProperty($propertyMetadata, $propertyName, $prefixedShortName, $shortName);
6✔
215
            }
216
        }
217

218
        return $properties;
6✔
219
    }
220

221
    /**
222
     * Gets Hydra operations.
223
     */
224
    private function getHydraOperations(bool $collection, ResourceMetadataCollection $resourceMetadataCollection = null): array
225
    {
226
        $hydraOperations = [];
6✔
227
        foreach ($resourceMetadataCollection as $resourceMetadata) {
6✔
228
            foreach ($resourceMetadata->getOperations() as $operation) {
6✔
229
                if (('POST' === $operation->getMethod() || $operation instanceof CollectionOperationInterface) !== $collection) {
6✔
230
                    continue;
6✔
231
                }
232

233
                $hydraOperations[] = $this->getHydraOperation($operation, $operation->getTypes()[0] ?? "#{$operation->getShortName()}");
6✔
234
            }
235
        }
236

237
        return $hydraOperations;
6✔
238
    }
239

240
    /**
241
     * Gets and populates if applicable a Hydra operation.
242
     */
243
    private function getHydraOperation(HttpOperation $operation, string $prefixedShortName): array
244
    {
245
        $method = $operation->getMethod() ?: 'GET';
6✔
246

247
        $hydraOperation = $operation->getHydraContext() ?? [];
6✔
248
        if ($operation->getDeprecationReason()) {
6✔
249
            $hydraOperation['owl:deprecated'] = true;
×
250
        }
251

252
        $shortName = $operation->getShortName();
6✔
253
        $inputMetadata = $operation->getInput() ?? [];
6✔
254
        $outputMetadata = $operation->getOutput() ?? [];
6✔
255

256
        $inputClass = \array_key_exists('class', $inputMetadata) ? $inputMetadata['class'] : false;
6✔
257
        $outputClass = \array_key_exists('class', $outputMetadata) ? $outputMetadata['class'] : false;
6✔
258

259
        if ('GET' === $method && $operation instanceof CollectionOperationInterface) {
6✔
260
            $hydraOperation += [
6✔
261
                '@type' => ['hydra:Operation', 'schema:FindAction'],
6✔
262
                'hydra:title' => "Retrieves the collection of $shortName resources.",
6✔
263
                'returns' => null === $outputClass ? 'owl:Nothing' : 'hydra:Collection',
6✔
264
            ];
6✔
265
        } elseif ('GET' === $method) {
6✔
266
            $hydraOperation += [
6✔
267
                '@type' => ['hydra:Operation', 'schema:FindAction'],
6✔
268
                'hydra:title' => "Retrieves a $shortName resource.",
6✔
269
                'returns' => null === $outputClass ? 'owl:Nothing' : $prefixedShortName,
6✔
270
            ];
6✔
271
        } elseif ('PATCH' === $method) {
6✔
272
            $hydraOperation += [
×
273
                '@type' => 'hydra:Operation',
×
274
                'hydra:title' => "Updates the $shortName resource.",
×
275
                'returns' => null === $outputClass ? 'owl:Nothing' : $prefixedShortName,
×
276
                'expects' => null === $inputClass ? 'owl:Nothing' : $prefixedShortName,
×
277
            ];
×
278
        } elseif ('POST' === $method) {
6✔
279
            $hydraOperation += [
6✔
280
                '@type' => ['hydra:Operation', 'schema:CreateAction'],
6✔
281
                'hydra:title' => "Creates a $shortName resource.",
6✔
282
                'returns' => null === $outputClass ? 'owl:Nothing' : $prefixedShortName,
6✔
283
                'expects' => null === $inputClass ? 'owl:Nothing' : $prefixedShortName,
6✔
284
            ];
6✔
285
        } elseif ('PUT' === $method) {
6✔
286
            $hydraOperation += [
6✔
287
                '@type' => ['hydra:Operation', 'schema:ReplaceAction'],
6✔
288
                'hydra:title' => "Replaces the $shortName resource.",
6✔
289
                'returns' => null === $outputClass ? 'owl:Nothing' : $prefixedShortName,
6✔
290
                'expects' => null === $inputClass ? 'owl:Nothing' : $prefixedShortName,
6✔
291
            ];
6✔
292
        } elseif ('DELETE' === $method) {
×
293
            $hydraOperation += [
×
294
                '@type' => ['hydra:Operation', 'schema:DeleteAction'],
×
295
                'hydra:title' => "Deletes the $shortName resource.",
×
296
                'returns' => 'owl:Nothing',
×
297
            ];
×
298
        }
299

300
        $hydraOperation['hydra:method'] ?? $hydraOperation['hydra:method'] = $method;
6✔
301

302
        if (!isset($hydraOperation['rdfs:label']) && isset($hydraOperation['hydra:title'])) {
6✔
303
            $hydraOperation['rdfs:label'] = $hydraOperation['hydra:title'];
6✔
304
        }
305

306
        ksort($hydraOperation);
6✔
307

308
        return $hydraOperation;
6✔
309
    }
310

311
    /**
312
     * Gets the range of the property.
313
     */
314
    private function getRange(ApiProperty $propertyMetadata): array|string|null
315
    {
316
        $jsonldContext = $propertyMetadata->getJsonldContext();
6✔
317

318
        if (isset($jsonldContext['@type'])) {
6✔
319
            return $jsonldContext['@type'];
3✔
320
        }
321

322
        $builtInTypes = $propertyMetadata->getBuiltinTypes() ?? [];
6✔
323
        $types = [];
6✔
324

325
        foreach ($builtInTypes as $type) {
6✔
326
            if ($type->isCollection() && null !== $collectionType = $type->getCollectionValueTypes()[0] ?? null) {
6✔
327
                $type = $collectionType;
3✔
328
            }
329

330
            switch ($type->getBuiltinType()) {
6✔
331
                case Type::BUILTIN_TYPE_STRING:
332
                    if (!\in_array('xmls:string', $types, true)) {
6✔
333
                        $types[] = 'xmls:string';
6✔
334
                    }
335
                    break;
6✔
336
                case Type::BUILTIN_TYPE_INT:
337
                    if (!\in_array('xmls:integer', $types, true)) {
×
338
                        $types[] = 'xmls:integer';
×
339
                    }
340
                    break;
×
341
                case Type::BUILTIN_TYPE_FLOAT:
342
                    if (!\in_array('xmls:decimal', $types, true)) {
×
343
                        $types[] = 'xmls:decimal';
×
344
                    }
345
                    break;
×
346
                case Type::BUILTIN_TYPE_BOOL:
347
                    if (!\in_array('xmls:boolean', $types, true)) {
×
348
                        $types[] = 'xmls:boolean';
×
349
                    }
350
                    break;
×
351
                case Type::BUILTIN_TYPE_OBJECT:
352
                    if (null === $className = $type->getClassName()) {
3✔
353
                        continue 2;
×
354
                    }
355

356
                    if (is_a($className, \DateTimeInterface::class, true)) {
3✔
357
                        if (!\in_array('xmls:dateTime', $types, true)) {
×
358
                            $types[] = 'xmls:dateTime';
×
359
                        }
360
                        break;
×
361
                    }
362

363
                    if ($this->resourceClassResolver->isResourceClass($className)) {
3✔
364
                        $resourceMetadata = $this->resourceMetadataFactory->create($className);
3✔
365
                        $operation = $resourceMetadata->getOperation();
3✔
366

367
                        if (!$operation instanceof HttpOperation || !$operation->getTypes()) {
3✔
368
                            if (!\in_array("#{$operation->getShortName()}", $types, true)) {
3✔
369
                                $types[] = "#{$operation->getShortName()}";
3✔
370
                            }
371
                            break;
3✔
372
                        }
373

374
                        $types = array_unique(array_merge($types, $operation->getTypes()));
×
375
                        break;
×
376
                    }
377
            }
378
        }
379

380
        if ([] === $types) {
6✔
381
            return null;
3✔
382
        }
383

384
        return 1 === \count($types) ? $types[0] : $types;
6✔
385
    }
386

387
    /**
388
     * Builds the classes array.
389
     */
390
    private function getClasses(array $entrypointProperties, array $classes): array
391
    {
392
        $classes[] = [
6✔
393
            '@id' => '#Entrypoint',
6✔
394
            '@type' => 'hydra:Class',
6✔
395
            'hydra:title' => 'The API entrypoint',
6✔
396
            'hydra:supportedProperty' => $entrypointProperties,
6✔
397
            'hydra:supportedOperation' => [
6✔
398
                '@type' => 'hydra:Operation',
6✔
399
                'hydra:method' => 'GET',
6✔
400
                'rdfs:label' => 'The API entrypoint.',
6✔
401
                'returns' => '#EntryPoint',
6✔
402
            ],
6✔
403
        ];
6✔
404

405
        // Constraint violation
406
        $classes[] = [
6✔
407
            '@id' => '#ConstraintViolation',
6✔
408
            '@type' => 'hydra:Class',
6✔
409
            'hydra:title' => 'A constraint violation',
6✔
410
            'hydra:supportedProperty' => [
6✔
411
                [
6✔
412
                    '@type' => 'hydra:SupportedProperty',
6✔
413
                    'hydra:property' => [
6✔
414
                        '@id' => '#ConstraintViolation/propertyPath',
6✔
415
                        '@type' => 'rdf:Property',
6✔
416
                        'rdfs:label' => 'propertyPath',
6✔
417
                        'domain' => '#ConstraintViolation',
6✔
418
                        'range' => 'xmls:string',
6✔
419
                    ],
6✔
420
                    'hydra:title' => 'propertyPath',
6✔
421
                    'hydra:description' => 'The property path of the violation',
6✔
422
                    'hydra:readable' => true,
6✔
423
                    'hydra:writeable' => false,
6✔
424
                ],
6✔
425
                [
6✔
426
                    '@type' => 'hydra:SupportedProperty',
6✔
427
                    'hydra:property' => [
6✔
428
                        '@id' => '#ConstraintViolation/message',
6✔
429
                        '@type' => 'rdf:Property',
6✔
430
                        'rdfs:label' => 'message',
6✔
431
                        'domain' => '#ConstraintViolation',
6✔
432
                        'range' => 'xmls:string',
6✔
433
                    ],
6✔
434
                    'hydra:title' => 'message',
6✔
435
                    'hydra:description' => 'The message associated with the violation',
6✔
436
                    'hydra:readable' => true,
6✔
437
                    'hydra:writeable' => false,
6✔
438
                ],
6✔
439
            ],
6✔
440
        ];
6✔
441

442
        // Constraint violation list
443
        $classes[] = [
6✔
444
            '@id' => '#ConstraintViolationList',
6✔
445
            '@type' => 'hydra:Class',
6✔
446
            'subClassOf' => 'hydra:Error',
6✔
447
            'hydra:title' => 'A constraint violation list',
6✔
448
            'hydra:supportedProperty' => [
6✔
449
                [
6✔
450
                    '@type' => 'hydra:SupportedProperty',
6✔
451
                    'hydra:property' => [
6✔
452
                        '@id' => '#ConstraintViolationList/violations',
6✔
453
                        '@type' => 'rdf:Property',
6✔
454
                        'rdfs:label' => 'violations',
6✔
455
                        'domain' => '#ConstraintViolationList',
6✔
456
                        'range' => '#ConstraintViolation',
6✔
457
                    ],
6✔
458
                    'hydra:title' => 'violations',
6✔
459
                    'hydra:description' => 'The violations',
6✔
460
                    'hydra:readable' => true,
6✔
461
                    'hydra:writeable' => false,
6✔
462
                ],
6✔
463
            ],
6✔
464
        ];
6✔
465

466
        return $classes;
6✔
467
    }
468

469
    /**
470
     * Gets a property definition.
471
     */
472
    private function getProperty(ApiProperty $propertyMetadata, string $propertyName, string $prefixedShortName, string $shortName): array
473
    {
474
        if ($iri = $propertyMetadata->getIris()) {
6✔
475
            $iri = 1 === (is_countable($iri) ? \count($iri) : 0) ? $iri[0] : $iri;
3✔
476
        }
477

478
        if (!isset($iri)) {
6✔
479
            $iri = "#$shortName/$propertyName";
6✔
480
        }
481

482
        $propertyData = [
6✔
483
            '@id' => $iri,
6✔
484
            '@type' => false === $propertyMetadata->isReadableLink() ? 'hydra:Link' : 'rdf:Property',
6✔
485
            'rdfs:label' => $propertyName,
6✔
486
            'domain' => $prefixedShortName,
6✔
487
        ];
6✔
488

489
        $property = [
6✔
490
            '@type' => 'hydra:SupportedProperty',
6✔
491
            'hydra:property' => $propertyData,
6✔
492
            'hydra:title' => $propertyName,
6✔
493
            'hydra:required' => $propertyMetadata->isRequired(),
6✔
494
            'hydra:readable' => $propertyMetadata->isReadable(),
6✔
495
            'hydra:writeable' => $propertyMetadata->isWritable() || $propertyMetadata->isInitializable(),
6✔
496
        ];
6✔
497

498
        if (null !== $range = $this->getRange($propertyMetadata)) {
6✔
499
            $property['hydra:property']['range'] = $range;
6✔
500
        }
501

502
        if (null !== $description = $propertyMetadata->getDescription()) {
6✔
503
            $property['hydra:description'] = $description;
6✔
504
        }
505

506
        if ($propertyMetadata->getDeprecationReason()) {
6✔
507
            $property['owl:deprecated'] = true;
×
508
        }
509

510
        return $property;
6✔
511
    }
512

513
    /**
514
     * Computes the documentation.
515
     */
516
    private function computeDoc(Documentation $object, array $classes): array
517
    {
518
        $doc = ['@context' => $this->getContext(), '@id' => $this->urlGenerator->generate('api_doc', ['_format' => self::FORMAT]), '@type' => 'hydra:ApiDocumentation'];
6✔
519

520
        if ('' !== $object->getTitle()) {
6✔
521
            $doc['hydra:title'] = $object->getTitle();
6✔
522
        }
523

524
        if ('' !== $object->getDescription()) {
6✔
525
            $doc['hydra:description'] = $object->getDescription();
6✔
526
        }
527

528
        $doc['hydra:entrypoint'] = $this->urlGenerator->generate('api_entrypoint');
6✔
529
        $doc['hydra:supportedClass'] = $classes;
6✔
530

531
        return $doc;
6✔
532
    }
533

534
    /**
535
     * Builds the JSON-LD context for the API documentation.
536
     */
537
    private function getContext(): array
538
    {
539
        return [
6✔
540
            '@vocab' => $this->urlGenerator->generate('api_doc', ['_format' => self::FORMAT], UrlGeneratorInterface::ABS_URL).'#',
6✔
541
            'hydra' => ContextBuilderInterface::HYDRA_NS,
6✔
542
            'rdf' => ContextBuilderInterface::RDF_NS,
6✔
543
            'rdfs' => ContextBuilderInterface::RDFS_NS,
6✔
544
            'xmls' => ContextBuilderInterface::XML_NS,
6✔
545
            'owl' => ContextBuilderInterface::OWL_NS,
6✔
546
            'schema' => ContextBuilderInterface::SCHEMA_ORG_NS,
6✔
547
            'domain' => ['@id' => 'rdfs:domain', '@type' => '@id'],
6✔
548
            'range' => ['@id' => 'rdfs:range', '@type' => '@id'],
6✔
549
            'subClassOf' => ['@id' => 'rdfs:subClassOf', '@type' => '@id'],
6✔
550
            'expects' => ['@id' => 'hydra:expects', '@type' => '@id'],
6✔
551
            'returns' => ['@id' => 'hydra:returns', '@type' => '@id'],
6✔
552
        ];
6✔
553
    }
554

555
    /**
556
     * {@inheritdoc}
557
     */
558
    public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
559
    {
560
        return self::FORMAT === $format && $data instanceof Documentation;
3✔
561
    }
562

563
    public function getSupportedTypes($format): array
564
    {
565
        return self::FORMAT === $format ? [Documentation::class => true] : [];
75✔
566
    }
567

568
    public function hasCacheableSupportsMethod(): bool
569
    {
570
        if (method_exists(Serializer::class, 'getSupportedTypes')) {
×
571
            trigger_deprecation(
×
572
                'api-platform/core',
×
573
                '3.1',
×
574
                'The "%s()" method is deprecated, use "getSupportedTypes()" instead.',
×
575
                __METHOD__
×
576
            );
×
577
        }
578

579
        return true;
×
580
    }
581
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc