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

api-platform / core / 15133993414

20 May 2025 09:30AM UTC coverage: 26.313% (-1.2%) from 27.493%
15133993414

Pull #7161

github

web-flow
Merge e2c03d45f into 5459ba375
Pull Request #7161: fix(metadata): infer parameter string type from schema

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

11019 existing lines in 363 files now uncovered.

12898 of 49018 relevant lines covered (26.31%)

34.33 hits per line

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

89.23
/src/Metadata/Resource/Factory/LinkFactory.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\Metadata\Resource\Factory;
15

16
use ApiPlatform\Metadata\Exception\RuntimeException;
17
use ApiPlatform\Metadata\Link;
18
use ApiPlatform\Metadata\Metadata;
19
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
20
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
21
use ApiPlatform\Metadata\ResourceClassResolverInterface;
22
use Symfony\Component\PropertyInfo\Type;
23

24
/**
25
 * @internal
26
 */
27
final class LinkFactory implements LinkFactoryInterface, PropertyLinkFactoryInterface
28
{
29
    /**
30
     * @var array<class-string, string[]>
31
     */
32
    private $localIdentifiersPerResourceClassCache = [];
33

34
    public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
35
    {
UNCOV
36
    }
979✔
37

38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function createLinkFromProperty(Metadata $operation, string $property): Link
42
    {
43
        $metadata = $this->propertyMetadataFactory->create($resourceClass = $operation->getClass(), $property);
×
44
        $relationClass = $this->getPropertyClassType($metadata->getBuiltinTypes());
×
45
        if (!$relationClass) {
×
46
            throw new RuntimeException(\sprintf('We could not find a class matching the uriVariable "%s" on "%s".', $property, $resourceClass));
×
47
        }
48

49
        $identifiers = $this->resourceClassResolver->isResourceClass($relationClass) ? $this->getIdentifiersFromResourceClass($relationClass) : ['id'];
×
50

51
        return new Link(fromClass: $relationClass, toProperty: $property, identifiers: $identifiers, parameterName: $property);
×
52
    }
53

54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function createLinksFromIdentifiers(Metadata $operation): array
58
    {
UNCOV
59
        $identifiers = $this->getIdentifiersFromResourceClass($resourceClass = $operation->getClass());
63✔
60

UNCOV
61
        if (!$identifiers) {
63✔
UNCOV
62
            return [];
11✔
63
        }
64

UNCOV
65
        $link = (new Link())->withFromClass($resourceClass)->withIdentifiers($identifiers);
55✔
UNCOV
66
        $parameterName = $identifiers[0];
55✔
UNCOV
67
        if ('value' === $parameterName && enum_exists($resourceClass)) {
55✔
UNCOV
68
            $parameterName = 'id';
2✔
69
        }
70

UNCOV
71
        if (1 < \count($identifiers)) {
55✔
72
            $parameterName = 'id';
1✔
73
            $link = $link->withCompositeIdentifier(true);
1✔
74
        }
75

UNCOV
76
        return [$link->withParameterName($parameterName)];
55✔
77
    }
78

79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function createLinksFromRelations(Metadata $operation): array
83
    {
UNCOV
84
        $links = [];
53✔
UNCOV
85
        foreach ($this->propertyNameCollectionFactory->create($resourceClass = $operation->getClass()) as $property) {
53✔
UNCOV
86
            $metadata = $this->propertyMetadataFactory->create($resourceClass, $property);
52✔
87

UNCOV
88
            if (!($relationClass = $this->getPropertyClassType($metadata->getBuiltinTypes())) || !$this->resourceClassResolver->isResourceClass($relationClass)) {
52✔
UNCOV
89
                continue;
52✔
90
            }
91

UNCOV
92
            $identifiers = $this->getIdentifiersFromResourceClass($resourceClass);
17✔
93

UNCOV
94
            $links[] = (new Link())->withFromProperty($property)->withFromClass($resourceClass)->withToClass($relationClass)->withIdentifiers($identifiers);
17✔
95
        }
96

UNCOV
97
        return $links;
53✔
98
    }
99

100
    /**
101
     * {@inheritdoc}
102
     */
103
    public function createLinksFromAttributes(Metadata $operation): array
104
    {
UNCOV
105
        $links = [];
53✔
106
        try {
UNCOV
107
            $reflectionClass = new \ReflectionClass($resourceClass = $operation->getClass());
53✔
UNCOV
108
            foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
53✔
UNCOV
109
                $reflectionProperty = $reflectionClass->getProperty($property);
52✔
110

UNCOV
111
                foreach ($reflectionProperty->getAttributes(Link::class) as $attributeLink) {
52✔
112
                    $metadata = $this->propertyMetadataFactory->create($resourceClass, $property);
1✔
113

114
                    $attributeLink = $attributeLink->newInstance()
1✔
115
                        ->withFromProperty($property);
1✔
116

117
                    if (!$attributeLink->getFromClass()) {
1✔
118
                        $attributeLink = $attributeLink->withFromClass($resourceClass)->withToClass($this->getPropertyClassType($metadata->getBuiltinTypes()) ?? $resourceClass);
1✔
119
                    }
120

121
                    $links[] = $attributeLink;
1✔
122
                }
123
            }
UNCOV
124
        } catch (\ReflectionException) {
11✔
125
        }
126

UNCOV
127
        return $links;
53✔
128
    }
129

130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function completeLink(Link $link): Link
134
    {
UNCOV
135
        if (!$link->getIdentifiers()) {
28✔
UNCOV
136
            $link = $link->withIdentifiers($this->getIdentifiersFromResourceClass($link->getFromClass()));
13✔
137
        }
138

UNCOV
139
        if (1 < \count((array) $link->getIdentifiers())) {
28✔
140
            $link = $link->withCompositeIdentifier(true);
×
141
        }
142

UNCOV
143
        return $link;
28✔
144
    }
145

146
    /**
147
     * @param class-string $resourceClass
148
     *
149
     * @return string[]
150
     */
151
    private function getIdentifiersFromResourceClass(string $resourceClass): array
152
    {
UNCOV
153
        if (isset($this->localIdentifiersPerResourceClassCache[$resourceClass])) {
64✔
UNCOV
154
            return $this->localIdentifiersPerResourceClassCache[$resourceClass];
63✔
155
        }
156

UNCOV
157
        $hasIdProperty = false;
64✔
UNCOV
158
        $identifiers = [];
64✔
UNCOV
159
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
64✔
UNCOV
160
            $isIdentifier = $this->propertyMetadataFactory->create($resourceClass, $property)->isIdentifier();
57✔
161

UNCOV
162
            if (!$hasIdProperty && null === $isIdentifier) {
57✔
UNCOV
163
                $hasIdProperty = 'id' === $property;
34✔
164
            }
165

UNCOV
166
            if ($isIdentifier) {
57✔
UNCOV
167
                $identifiers[] = $property;
54✔
168
            }
169
        }
170

UNCOV
171
        if ($hasIdProperty && !$identifiers) {
64✔
172
            return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['id'];
2✔
173
        }
174

UNCOV
175
        if (!$hasIdProperty && !$identifiers && enum_exists($resourceClass)) {
63✔
UNCOV
176
            return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['value'];
3✔
177
        }
178

UNCOV
179
        return $this->localIdentifiersPerResourceClassCache[$resourceClass] = $identifiers;
62✔
180
    }
181

182
    /**
183
     * @param Type[]|null $types
184
     */
185
    private function getPropertyClassType(?array $types): ?string
186
    {
UNCOV
187
        foreach ($types ?? [] as $type) {
52✔
UNCOV
188
            if ($type->isCollection()) {
52✔
UNCOV
189
                return $this->getPropertyClassType($type->getCollectionValueTypes());
19✔
190
            }
191

UNCOV
192
            if ($class = $type->getClassName()) {
52✔
UNCOV
193
                return $class;
35✔
194
            }
195
        }
196

UNCOV
197
        return null;
52✔
198
    }
199
}
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