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

api-platform / core / 7196499749

13 Dec 2023 02:17PM UTC coverage: 37.359% (+1.4%) from 36.003%
7196499749

push

github

web-flow
ci: conflict sebastian/comparator (#6032)

* ci: conflict sebastian/comparator

* for lowest

10295 of 27557 relevant lines covered (37.36%)

28.14 hits per line

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

52.75
/src/Doctrine/Orm/State/LinksHandlerTrait.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\Doctrine\Orm\State;
15

16
use ApiPlatform\Doctrine\Common\State\LinksHandlerTrait as CommonLinksHandlerTrait;
17
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
18
use ApiPlatform\Metadata\Link;
19
use ApiPlatform\Metadata\Operation;
20
use Doctrine\ORM\Mapping\ClassMetadataInfo;
21
use Doctrine\ORM\QueryBuilder;
22

23
/**
24
 * @internal
25
 */
26
trait LinksHandlerTrait
27
{
28
    use CommonLinksHandlerTrait;
29

30
    private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, QueryNameGenerator $queryNameGenerator, array $context, string $entityClass, Operation $operation): void
31
    {
32
        if (!$identifiers) {
60✔
33
            return;
36✔
34
        }
35

36
        $manager = $this->managerRegistry->getManagerForClass($entityClass);
28✔
37
        $doctrineClassMetadata = $manager->getClassMetadata($entityClass);
28✔
38
        $alias = $queryBuilder->getRootAliases()[0];
28✔
39

40
        $links = $this->getLinks($entityClass, $operation, $context);
28✔
41

42
        if (!$links) {
28✔
43
            return;
×
44
        }
45

46
        $previousAlias = $alias;
28✔
47
        $previousJoinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
28✔
48
        $expressions = [];
28✔
49
        $identifiers = array_reverse($identifiers);
28✔
50

51
        foreach (array_reverse($links) as $link) {
28✔
52
            if (null !== $link->getExpandedValue() || !$link->getFromClass()) {
28✔
53
                continue;
×
54
            }
55

56
            $fromClass = $link->getFromClass();
28✔
57
            if (!$this->managerRegistry->getManagerForClass($fromClass)) {
28✔
58
                $fromClass = $this->getLinkFromClass($link, $operation);
×
59
            }
60

61
            $fromClassMetadata = $manager->getClassMetadata($fromClass);
28✔
62
            $identifierProperties = $link->getIdentifiers();
28✔
63
            $hasCompositeIdentifiers = 1 < \count($identifierProperties);
28✔
64

65
            if (!$link->getFromProperty() && !$link->getToProperty()) {
28✔
66
                $currentAlias = $fromClass === $entityClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
24✔
67

68
                foreach ($identifierProperties as $identifierProperty) {
24✔
69
                    $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
24✔
70
                    $queryBuilder->andWhere("$currentAlias.$identifierProperty = :$placeholder");
24✔
71
                    $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
24✔
72
                }
73

74
                $previousAlias = $currentAlias;
24✔
75
                $previousJoinProperties = $fromClassMetadata->getIdentifierFieldNames();
24✔
76
                continue;
24✔
77
            }
78

79
            $joinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
4✔
80

81
            if ($link->getFromProperty() && !$link->getToProperty()) {
4✔
82
                $joinAlias = $queryNameGenerator->generateJoinAlias('m');
4✔
83
                $associationMapping = $fromClassMetadata->getAssociationMapping($link->getFromProperty()); // @phpstan-ignore-line
4✔
84
                $relationType = $associationMapping['type'];
4✔
85

86
                if ($relationType & ClassMetadataInfo::TO_MANY) {
4✔
87
                    $nextAlias = $queryNameGenerator->generateJoinAlias($alias);
×
88
                    $whereClause = [];
×
89
                    foreach ($identifierProperties as $identifierProperty) {
×
90
                        $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
×
91
                        $whereClause[] = "$nextAlias.{$identifierProperty} = :$placeholder";
×
92
                        $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
×
93
                    }
94

95
                    $property = $associationMapping['mappedBy'] ?? $joinProperties[0];
×
96
                    $select = isset($associationMapping['mappedBy']) ? "IDENTITY($joinAlias.$property)" : "$joinAlias.$property";
×
97
                    $expressions["$previousAlias.{$property}"] = "SELECT $select FROM {$fromClass} $nextAlias INNER JOIN $nextAlias.{$associationMapping['fieldName']} $joinAlias WHERE ".implode(' AND ', $whereClause);
×
98
                    $previousAlias = $nextAlias;
×
99
                    continue;
×
100
                }
101

102
                // A single-valued association path expression to an inverse side is not supported in DQL queries.
103
                if ($relationType & ClassMetadataInfo::TO_ONE && !($associationMapping['isOwningSide'] ?? true)) {
4✔
104
                    $queryBuilder->innerJoin("$previousAlias.".$associationMapping['mappedBy'], $joinAlias);
×
105
                } else {
106
                    $queryBuilder->join(
4✔
107
                        $fromClass,
4✔
108
                        $joinAlias,
4✔
109
                        'WITH',
4✔
110
                        "$previousAlias.{$previousJoinProperties[0]} = $joinAlias.{$associationMapping['fieldName']}"
4✔
111
                    );
4✔
112
                }
113

114
                foreach ($identifierProperties as $identifierProperty) {
4✔
115
                    $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
4✔
116
                    $queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
4✔
117
                    $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
4✔
118
                }
119

120
                $previousAlias = $joinAlias;
4✔
121
                $previousJoinProperties = $joinProperties;
4✔
122
                continue;
4✔
123
            }
124

125
            $joinAlias = $queryNameGenerator->generateJoinAlias($alias);
×
126
            $queryBuilder->join("{$previousAlias}.{$link->getToProperty()}", $joinAlias);
×
127

128
            foreach ($identifierProperties as $identifierProperty) {
×
129
                $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
×
130
                $queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
×
131
                $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
×
132
            }
133

134
            $previousAlias = $joinAlias;
×
135
            $previousJoinProperties = $joinProperties;
×
136
        }
137

138
        if ($expressions) {
28✔
139
            $i = 0;
×
140
            $clause = '';
×
141
            foreach ($expressions as $alias => $expression) {
×
142
                if (0 === $i) {
×
143
                    $clause .= "$alias IN (".$expression;
×
144
                    ++$i;
×
145
                    continue;
×
146
                }
147

148
                $clause .= " AND $alias IN (".$expression;
×
149
                ++$i;
×
150
            }
151

152
            $queryBuilder->andWhere($clause.str_repeat(')', $i));
×
153
        }
154
    }
155

156
    private function getLinkFromClass(Link $link, Operation $operation): string
157
    {
158
        $fromClass = $link->getFromClass();
×
159
        if ($fromClass === $operation->getClass() && $entityClass = $this->getStateOptionsEntityClass($operation)) {
×
160
            return $entityClass;
×
161
        }
162

163
        $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation();
×
164

165
        if ($entityClass = $this->getStateOptionsEntityClass($operation)) {
×
166
            return $entityClass;
×
167
        }
168

169
        throw new \Exception('Can not found a doctrine class for this link.');
×
170
    }
171

172
    private function getStateOptionsEntityClass(Operation $operation): ?string
173
    {
174
        if (($options = $operation->getStateOptions()) && $options instanceof Options && $entityClass = $options->getEntityClass()) {
×
175
            return $entityClass;
×
176
        }
177

178
        return null;
×
179
    }
180
}
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