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

api-platform / core / 3712739783

pending completion
3712739783

Pull #5254

github

GitHub
Merge 9dfa88fa6 into ac711530f
Pull Request #5254: [OpenApi] Add ApiResource::openapi and deprecate openapiContext

199 of 199 new or added lines in 6 files covered. (100.0%)

7494 of 12363 relevant lines covered (60.62%)

67.55 hits per line

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

84.75
/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\Api\ResourceClassResolverInterface;
17
use ApiPlatform\Exception\RuntimeException;
18
use ApiPlatform\Metadata\ApiResource;
19
use ApiPlatform\Metadata\Link;
20
use ApiPlatform\Metadata\Operation;
21
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
22
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
23
use Symfony\Component\PropertyInfo\Type;
24

25
/**
26
 * @internal
27
 */
28
final class LinkFactory implements LinkFactoryInterface, PropertyLinkFactoryInterface
29
{
30
    public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
31
    {
32
    }
649✔
33

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

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

47
        return new Link(fromClass: $relationClass, toProperty: $property, identifiers: $identifiers, parameterName: $property);
×
48
    }
49

50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function createLinksFromIdentifiers(ApiResource|Operation $operation): array
54
    {
55
        $identifiers = $this->getIdentifiersFromResourceClass($resourceClass = $operation->getClass());
20✔
56

57
        if (!$identifiers) {
20✔
58
            return [];
1✔
59
        }
60

61
        $link = (new Link())->withFromClass($resourceClass)->withIdentifiers($identifiers);
20✔
62
        $parameterName = $identifiers[0];
20✔
63

64
        if (1 < \count($identifiers)) {
20✔
65
            $parameterName = 'id';
×
66
            $link = $link->withCompositeIdentifier(true);
×
67
        }
68

69
        return [$link->withParameterName($parameterName)];
20✔
70
    }
71

72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function createLinksFromRelations(ApiResource|Operation $operation): array
76
    {
77
        $links = [];
20✔
78
        foreach ($this->propertyNameCollectionFactory->create($resourceClass = $operation->getClass()) as $property) {
20✔
79
            $metadata = $this->propertyMetadataFactory->create($resourceClass, $property);
20✔
80

81
            if (!($relationClass = $this->getPropertyClassType($metadata->getBuiltinTypes())) || !$this->resourceClassResolver->isResourceClass($relationClass)) {
20✔
82
                continue;
20✔
83
            }
84

85
            $identifiers = $this->getIdentifiersFromResourceClass($resourceClass);
9✔
86

87
            $links[] = (new Link())->withFromProperty($property)->withFromClass($resourceClass)->withToClass($relationClass)->withIdentifiers($identifiers);
9✔
88
        }
89

90
        return $links;
20✔
91
    }
92

93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function createLinksFromAttributes(ApiResource|Operation $operation): array
97
    {
98
        $links = [];
20✔
99
        try {
100
            $reflectionClass = new \ReflectionClass($resourceClass = $operation->getClass());
20✔
101
            foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
20✔
102
                $reflectionProperty = $reflectionClass->getProperty($property);
20✔
103

104
                foreach ($reflectionProperty->getAttributes(Link::class) as $attributeLink) {
20✔
105
                    $metadata = $this->propertyMetadataFactory->create($resourceClass, $property);
1✔
106

107
                    $attributeLink = $attributeLink->newInstance()
1✔
108
                        ->withFromProperty($property);
1✔
109

110
                    if (!$attributeLink->getFromClass()) {
1✔
111
                        $attributeLink = $attributeLink->withFromClass($resourceClass)->withToClass($this->getPropertyClassType($metadata->getBuiltinTypes()) ?? $resourceClass);
1✔
112
                    }
113

114
                    $links[] = $attributeLink;
1✔
115
                }
116
            }
117
        } catch (\ReflectionException) {
3✔
118
        }
119

120
        return $links;
20✔
121
    }
122

123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function completeLink(Link $link): Link
127
    {
128
        if (!$link->getIdentifiers()) {
9✔
129
            $link = $link->withIdentifiers($this->getIdentifiersFromResourceClass($link->getFromClass()));
5✔
130
        }
131

132
        if (1 < \count((array) $link->getIdentifiers())) {
9✔
133
            $link = $link->withCompositeIdentifier(true);
×
134
        }
135

136
        return $link;
9✔
137
    }
138

139
    private function getIdentifiersFromResourceClass(string $resourceClass): array
140
    {
141
        $hasIdProperty = false;
20✔
142
        $identifiers = [];
20✔
143
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
20✔
144
            $isIdentifier = $this->propertyMetadataFactory->create($resourceClass, $property)->isIdentifier();
20✔
145

146
            if (!$hasIdProperty && null === $isIdentifier) {
20✔
147
                $hasIdProperty = 'id' === $property;
3✔
148
            }
149

150
            if ($isIdentifier) {
20✔
151
                $identifiers[] = $property;
18✔
152
            }
153
        }
154

155
        if ($hasIdProperty && !$identifiers) {
20✔
156
            return ['id'];
3✔
157
        }
158

159
        return $identifiers;
18✔
160
    }
161

162
    /**
163
     * @param Type[]|null $types
164
     */
165
    private function getPropertyClassType(?array $types): ?string
166
    {
167
        foreach ($types ?? [] as $type) {
20✔
168
            if ($type->isCollection()) {
20✔
169
                return $this->getPropertyClassType($type->getCollectionValueTypes());
7✔
170
            }
171

172
            if ($class = $type->getClassName()) {
20✔
173
                return $class;
13✔
174
            }
175
        }
176

177
        return null;
20✔
178
    }
179
}
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