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

api-platform / core / 20847864477

09 Jan 2026 09:47AM UTC coverage: 29.1% (+0.005%) from 29.095%
20847864477

Pull #7649

github

web-flow
Merge b342dd5db into d640d106b
Pull Request #7649: feat(validator): uuid/ulid parameter validation

0 of 4 new or added lines in 1 file covered. (0.0%)

15050 existing lines in 491 files now uncovered.

16996 of 58406 relevant lines covered (29.1%)

81.8 hits per line

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

96.8
/src/Hal/JsonSchema/SchemaFactory.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\Hal\JsonSchema;
15

16
use ApiPlatform\JsonSchema\DefinitionNameFactory;
17
use ApiPlatform\JsonSchema\DefinitionNameFactoryInterface;
18
use ApiPlatform\JsonSchema\ResourceMetadataTrait;
19
use ApiPlatform\JsonSchema\Schema;
20
use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface;
21
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
22
use ApiPlatform\JsonSchema\SchemaUriPrefixTrait;
23
use ApiPlatform\Metadata\Operation;
24
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
25

26
/**
27
 * Decorator factory which adds HAL properties to the JSON Schema document.
28
 *
29
 * @author Kévin Dunglas <dunglas@gmail.com>
30
 * @author Jachim Coudenys <jachimcoudenys@gmail.com>
31
 */
32
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
33
{
34
    use ResourceMetadataTrait;
35
    use SchemaUriPrefixTrait;
36

37
    private const COLLECTION_BASE_SCHEMA_NAME = 'HalCollectionBaseSchema';
38
    private const COLLECTION_BASE_SCHEMA_NAME_NO_PAGINATION = 'HalCollectionBaseSchemaNoPagination';
39

40
    private const HREF_PROP = [
41
        'href' => [
42
            'type' => 'string',
43
            'format' => 'iri-reference',
44
        ],
45
    ];
46
    private const BASE_PROPS = [
47
        '_links' => [
48
            'type' => 'object',
49
            'properties' => [
50
                'self' => [
51
                    'type' => 'object',
52
                    'properties' => self::HREF_PROP,
53
                ],
54
            ],
55
        ],
56
    ];
57

58
    public function __construct(private readonly SchemaFactoryInterface $schemaFactory, private ?DefinitionNameFactoryInterface $definitionNameFactory = null, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null)
59
    {
UNCOV
60
        if (!$definitionNameFactory) {
2,180✔
61
            $this->definitionNameFactory = new DefinitionNameFactory();
×
62
        }
UNCOV
63
        $this->resourceMetadataFactory = $resourceMetadataFactory;
2,180✔
UNCOV
64
        if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
2,180✔
UNCOV
65
            $this->schemaFactory->setSchemaFactory($this);
2,180✔
66
        }
67
    }
68

69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function buildSchema(string $className, string $format = 'jsonhal', string $type = Schema::TYPE_OUTPUT, ?Operation $operation = null, ?Schema $schema = null, ?array $serializerContext = null, bool $forceCollection = false): Schema
73
    {
UNCOV
74
        if ('jsonhal' !== $format) {
192✔
UNCOV
75
            return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
178✔
76
        }
77

UNCOV
78
        if (!$this->isResourceClass($className)) {
92✔
79
            $operation = null;
4✔
80
            $inputOrOutputClass = null;
4✔
81
            $serializerContext ??= [];
4✔
82
        } else {
UNCOV
83
            $operation = $this->findOperation($className, $type, $operation, $serializerContext, $format);
92✔
UNCOV
84
            $inputOrOutputClass = $this->findOutputClass($className, $type, $operation, $serializerContext);
92✔
UNCOV
85
            $serializerContext ??= $this->getSerializerContext($operation, $type);
92✔
86
        }
87

UNCOV
88
        if (null === $inputOrOutputClass) {
92✔
89
            // input or output disabled
90
            return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
4✔
91
        }
92

UNCOV
93
        $schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection);
92✔
UNCOV
94
        $definitions = $schema->getDefinitions();
92✔
UNCOV
95
        $definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext + ['schema_type' => $type]);
92✔
UNCOV
96
        $prefix = $this->getSchemaUriPrefix($schema->getVersion());
92✔
UNCOV
97
        $collectionKey = $schema->getItemsDefinitionKey();
92✔
98

99
        // Already computed
UNCOV
100
        if (!$collectionKey && isset($definitions[$definitionName])) {
92✔
UNCOV
101
            $schema['$ref'] = $prefix.$definitionName;
66✔
102

UNCOV
103
            return $schema;
66✔
104
        }
105

UNCOV
106
        $key = $schema->getRootDefinitionKey() ?? $collectionKey;
92✔
107

UNCOV
108
        $definitions[$definitionName] = [
92✔
UNCOV
109
            'allOf' => [
92✔
UNCOV
110
                ['type' => 'object', 'properties' => self::BASE_PROPS],
92✔
UNCOV
111
                ['$ref' => $prefix.$key],
92✔
UNCOV
112
            ],
92✔
UNCOV
113
        ];
92✔
114

UNCOV
115
        if (isset($definitions[$key]['description'])) {
92✔
UNCOV
116
            $definitions[$definitionName]['description'] = $definitions[$key]['description'];
56✔
117
        }
118

UNCOV
119
        if (!$collectionKey) {
92✔
UNCOV
120
            $schema['$ref'] = $prefix.$definitionName;
76✔
121

UNCOV
122
            return $schema;
76✔
123
        }
124

UNCOV
125
        if (($schema['type'] ?? '') === 'array') {
84✔
UNCOV
126
            if (!isset($definitions[self::COLLECTION_BASE_SCHEMA_NAME_NO_PAGINATION])) {
84✔
UNCOV
127
                $definitions[self::COLLECTION_BASE_SCHEMA_NAME_NO_PAGINATION] = [
84✔
UNCOV
128
                    'type' => 'object',
84✔
UNCOV
129
                    'properties' => [
84✔
UNCOV
130
                        'totalItems' => [
84✔
UNCOV
131
                            'type' => 'integer',
84✔
UNCOV
132
                            'minimum' => 0,
84✔
UNCOV
133
                        ],
84✔
UNCOV
134
                        '_embedded' => [
84✔
UNCOV
135
                            'anyOf' => [
84✔
UNCOV
136
                                [
84✔
UNCOV
137
                                    'type' => 'object',
84✔
UNCOV
138
                                    'properties' => [
84✔
UNCOV
139
                                        'item' => [
84✔
UNCOV
140
                                            'type' => 'array',
84✔
UNCOV
141
                                        ],
84✔
UNCOV
142
                                    ],
84✔
UNCOV
143
                                ],
84✔
UNCOV
144
                                ['type' => 'object'],
84✔
UNCOV
145
                            ],
84✔
UNCOV
146
                        ],
84✔
UNCOV
147
                        '_links' => [
84✔
UNCOV
148
                            'type' => 'object',
84✔
UNCOV
149
                            'properties' => [
84✔
UNCOV
150
                                'self' => [
84✔
UNCOV
151
                                    'type' => 'object',
84✔
UNCOV
152
                                    'properties' => self::HREF_PROP,
84✔
UNCOV
153
                                ],
84✔
UNCOV
154
                            ],
84✔
UNCOV
155
                        ],
84✔
UNCOV
156
                    ],
84✔
UNCOV
157
                    'required' => ['_links', '_embedded'],
84✔
UNCOV
158
                ];
84✔
159

UNCOV
160
                $definitions[self::COLLECTION_BASE_SCHEMA_NAME] = [
84✔
UNCOV
161
                    'allOf' => [
84✔
UNCOV
162
                        ['$ref' => $prefix.self::COLLECTION_BASE_SCHEMA_NAME_NO_PAGINATION],
84✔
UNCOV
163
                        [
84✔
UNCOV
164
                            'type' => 'object',
84✔
UNCOV
165
                            'properties' => [
84✔
UNCOV
166
                                'itemsPerPage' => [
84✔
UNCOV
167
                                    'type' => 'integer',
84✔
UNCOV
168
                                    'minimum' => 0,
84✔
UNCOV
169
                                ],
84✔
UNCOV
170
                                '_links' => [
84✔
UNCOV
171
                                    'properties' => [
84✔
UNCOV
172
                                        'first' => [
84✔
UNCOV
173
                                            'type' => 'object',
84✔
UNCOV
174
                                            'properties' => self::HREF_PROP,
84✔
UNCOV
175
                                        ],
84✔
UNCOV
176
                                        'last' => [
84✔
UNCOV
177
                                            'type' => 'object',
84✔
UNCOV
178
                                            'properties' => self::HREF_PROP,
84✔
UNCOV
179
                                        ],
84✔
UNCOV
180
                                        'next' => [
84✔
UNCOV
181
                                            'type' => 'object',
84✔
UNCOV
182
                                            'properties' => self::HREF_PROP,
84✔
UNCOV
183
                                        ],
84✔
UNCOV
184
                                        'previous' => [
84✔
UNCOV
185
                                            'type' => 'object',
84✔
UNCOV
186
                                            'properties' => self::HREF_PROP,
84✔
UNCOV
187
                                        ],
84✔
UNCOV
188
                                    ],
84✔
UNCOV
189
                                ],
84✔
UNCOV
190
                            ],
84✔
UNCOV
191
                        ],
84✔
UNCOV
192
                    ],
84✔
UNCOV
193
                ];
84✔
194
            }
195

UNCOV
196
            unset($schema['items']);
84✔
UNCOV
197
            unset($schema['type']);
84✔
198

UNCOV
199
            $schema['description'] = "$definitionName collection.";
84✔
UNCOV
200
            $schema['allOf'] = [
84✔
UNCOV
201
                ['$ref' => $prefix.(false === $operation->getPaginationEnabled() ? self::COLLECTION_BASE_SCHEMA_NAME_NO_PAGINATION : self::COLLECTION_BASE_SCHEMA_NAME)],
84✔
UNCOV
202
                [
84✔
UNCOV
203
                    'type' => 'object',
84✔
UNCOV
204
                    'properties' => [
84✔
UNCOV
205
                        '_embedded' => [
84✔
UNCOV
206
                            'additionalProperties' => [
84✔
UNCOV
207
                                'type' => 'array',
84✔
UNCOV
208
                                'items' => ['$ref' => $prefix.$definitionName],
84✔
UNCOV
209
                            ],
84✔
UNCOV
210
                        ],
84✔
UNCOV
211
                    ],
84✔
UNCOV
212
                ],
84✔
UNCOV
213
            ];
84✔
214

UNCOV
215
            return $schema;
84✔
216
        }
217

218
        return $schema;
×
219
    }
220

221
    public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
222
    {
223
        if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
×
224
            $this->schemaFactory->setSchemaFactory($schemaFactory);
×
225
        }
226
    }
227
}
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