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

api-platform / core / 15255731762

26 May 2025 01:55PM UTC coverage: 0.0% (-26.5%) from 26.526%
15255731762

Pull #7176

github

web-flow
Merge 66f6cf4d2 into 79edced67
Pull Request #7176: Merge 4.1

0 of 387 new or added lines in 28 files covered. (0.0%)

11394 existing lines in 372 files now uncovered.

0 of 51334 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/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

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

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

68
    /**
69
     * {@inheritdoc}
70
     */
71
    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
72
    {
UNCOV
73
        if ('jsonhal' !== $format) {
×
UNCOV
74
            return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
×
75
        }
76

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

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

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

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

UNCOV
102
            return $schema;
×
103
        }
104

UNCOV
105
        $key = $schema->getRootDefinitionKey() ?? $collectionKey;
×
106

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

UNCOV
114
        if (isset($definitions[$key]['description'])) {
×
UNCOV
115
            $definitions[$definitionName]['description'] = $definitions[$key]['description'];
×
116
        }
117

UNCOV
118
        if (!$collectionKey) {
×
UNCOV
119
            $schema['$ref'] = $prefix.$definitionName;
×
120

UNCOV
121
            return $schema;
×
122
        }
123

UNCOV
124
        if (($schema['type'] ?? '') === 'array') {
×
UNCOV
125
            if (!isset($definitions[self::COLLECTION_BASE_SCHEMA_NAME])) {
×
UNCOV
126
                $definitions[self::COLLECTION_BASE_SCHEMA_NAME] = [
×
UNCOV
127
                    'type' => 'object',
×
UNCOV
128
                    'properties' => [
×
UNCOV
129
                        '_embedded' => [
×
UNCOV
130
                            'anyOf' => [
×
UNCOV
131
                                [
×
UNCOV
132
                                    'type' => 'object',
×
UNCOV
133
                                    'properties' => [
×
UNCOV
134
                                        'item' => [
×
UNCOV
135
                                            'type' => 'array',
×
UNCOV
136
                                        ],
×
UNCOV
137
                                    ],
×
UNCOV
138
                                ],
×
UNCOV
139
                                ['type' => 'object'],
×
UNCOV
140
                            ],
×
UNCOV
141
                        ],
×
UNCOV
142
                        'totalItems' => [
×
UNCOV
143
                            'type' => 'integer',
×
UNCOV
144
                            'minimum' => 0,
×
UNCOV
145
                        ],
×
UNCOV
146
                        'itemsPerPage' => [
×
UNCOV
147
                            'type' => 'integer',
×
UNCOV
148
                            'minimum' => 0,
×
UNCOV
149
                        ],
×
UNCOV
150
                        '_links' => [
×
UNCOV
151
                            'type' => 'object',
×
UNCOV
152
                            'properties' => [
×
UNCOV
153
                                'self' => [
×
UNCOV
154
                                    'type' => 'object',
×
UNCOV
155
                                    'properties' => self::HREF_PROP,
×
UNCOV
156
                                ],
×
UNCOV
157
                                'first' => [
×
UNCOV
158
                                    'type' => 'object',
×
UNCOV
159
                                    'properties' => self::HREF_PROP,
×
UNCOV
160
                                ],
×
UNCOV
161
                                'last' => [
×
UNCOV
162
                                    'type' => 'object',
×
UNCOV
163
                                    'properties' => self::HREF_PROP,
×
UNCOV
164
                                ],
×
UNCOV
165
                                'next' => [
×
UNCOV
166
                                    'type' => 'object',
×
UNCOV
167
                                    'properties' => self::HREF_PROP,
×
UNCOV
168
                                ],
×
UNCOV
169
                                'previous' => [
×
UNCOV
170
                                    'type' => 'object',
×
UNCOV
171
                                    'properties' => self::HREF_PROP,
×
UNCOV
172
                                ],
×
UNCOV
173
                            ],
×
UNCOV
174
                        ],
×
UNCOV
175
                    ],
×
UNCOV
176
                    'required' => ['_links', '_embedded'],
×
UNCOV
177
                ];
×
178
            }
179

UNCOV
180
            unset($schema['items']);
×
UNCOV
181
            unset($schema['type']);
×
182

UNCOV
183
            $schema['description'] = "$definitionName collection.";
×
UNCOV
184
            $schema['allOf'] = [
×
UNCOV
185
                ['$ref' => $prefix.self::COLLECTION_BASE_SCHEMA_NAME],
×
UNCOV
186
                [
×
UNCOV
187
                    'type' => 'object',
×
UNCOV
188
                    'properties' => [
×
UNCOV
189
                        '_embedded' => [
×
UNCOV
190
                            'additionalProperties' => [
×
UNCOV
191
                                'type' => 'array',
×
UNCOV
192
                                'items' => ['$ref' => $prefix.$definitionName],
×
UNCOV
193
                            ],
×
UNCOV
194
                        ],
×
UNCOV
195
                    ],
×
UNCOV
196
                ],
×
UNCOV
197
            ];
×
198

UNCOV
199
            return $schema;
×
200
        }
201

202
        return $schema;
×
203
    }
204

205
    public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
206
    {
207
        if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
×
208
            $this->schemaFactory->setSchemaFactory($schemaFactory);
×
209
        }
210
    }
211
}
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

© 2025 Coveralls, Inc