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

api-platform / core / 9710836697

28 Jun 2024 09:35AM UTC coverage: 63.285% (+1.2%) from 62.122%
9710836697

push

github

soyuka
docs: changelog v3.3.7

11104 of 17546 relevant lines covered (63.29%)

52.26 hits per line

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

94.92
/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
    public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceClassResolverInterface $resourceClassResolver)
30
    {
31
    }
327✔
32

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

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

46
        return new Link(fromClass: $relationClass, toProperty: $property, identifiers: $identifiers, parameterName: $property);
4✔
47
    }
48

49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function createLinksFromIdentifiers(Metadata $operation): array
53
    {
54
        $identifiers = $this->getIdentifiersFromResourceClass($resourceClass = $operation->getClass());
13✔
55

56
        if (!$identifiers) {
13✔
57
            return [];
8✔
58
        }
59

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

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

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

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

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

84
            $identifiers = $this->getIdentifiersFromResourceClass($resourceClass);
4✔
85

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

89
        return $links;
9✔
90
    }
91

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

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

106
                    $attributeLink = $attributeLink->newInstance()
4✔
107
                        ->withFromProperty($property);
4✔
108

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

113
                    $links[] = $attributeLink;
4✔
114
                }
115
            }
116
        } catch (\ReflectionException) {
5✔
117
        }
118

119
        return $links;
9✔
120
    }
121

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

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

135
        return $link;
10✔
136
    }
137

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

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

149
            if ($isIdentifier) {
9✔
150
                $identifiers[] = $property;
9✔
151
            }
152
        }
153

154
        if ($hasIdProperty && !$identifiers) {
13✔
155
            return ['id'];
×
156
        }
157

158
        return $identifiers;
13✔
159
    }
160

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

171
            if ($class = $type->getClassName()) {
9✔
172
                return $class;
9✔
173
            }
174
        }
175

176
        return null;
9✔
177
    }
178
}
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