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

api-platform / core / 14635100171

24 Apr 2025 06:39AM UTC coverage: 8.271% (+0.02%) from 8.252%
14635100171

Pull #6904

github

web-flow
Merge c9cefd82e into a3e5e53ea
Pull Request #6904: feat(graphql): added support for graphql subscriptions to work for actions

0 of 73 new or added lines in 3 files covered. (0.0%)

1999 existing lines in 144 files now uncovered.

13129 of 158728 relevant lines covered (8.27%)

13.6 hits per line

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

89.06
/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 ApiPlatform\Metadata\Util\TypeHelper;
23
use Symfony\Component\TypeInfo\Type;
24

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

35
    public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
36
    {
37
    }
1,216✔
38

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

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

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

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

62
        if (!$identifiers) {
94✔
63
            return [];
18✔
64
        }
65

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

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

77
        return [$link->withParameterName($parameterName)];
81✔
78
    }
79

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

89
            if (!($relationClass = $this->getPropertyClassType($metadata->getNativeType())) || !$this->resourceClassResolver->isResourceClass($relationClass)) {
78✔
90
                continue;
78✔
91
            }
92

93
            $identifiers = $this->getIdentifiersFromResourceClass($resourceClass);
22✔
94

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

98
        return $links;
80✔
99
    }
100

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

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

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

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

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

128
        return $links;
80✔
129
    }
130

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

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

144
        return $link;
40✔
145
    }
146

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

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

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

167
            if ($isIdentifier) {
84✔
168
                $identifiers[] = $property;
80✔
169
            }
170
        }
171

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

176
        if (!$hasIdProperty && !$identifiers && enum_exists($resourceClass)) {
95✔
177
            return $this->localIdentifiersPerResourceClassCache[$resourceClass] = ['value'];
5✔
178
        }
179

180
        return $this->localIdentifiersPerResourceClassCache[$resourceClass] = $identifiers;
93✔
181
    }
182

183
    private function getPropertyClassType(?Type $type): ?string
184
    {
185
        if (!$type) {
78✔
186
            return null;
11✔
187
        }
188

189
        if ($collectionValueType = TypeHelper::getCollectionValueType($type)) {
78✔
190
            return $this->getPropertyClassType($collectionValueType);
31✔
191
        }
192

193
        return TypeHelper::getClassName($type);
78✔
194
    }
195
}
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