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

api-platform / core / 14008635868

22 Mar 2025 12:39PM UTC coverage: 8.52% (+0.005%) from 8.515%
14008635868

Pull #7042

github

web-flow
Merge fdd88ef56 into 47a6dffbb
Pull Request #7042: Purge parent collections in inheritance cases

4 of 9 new or added lines in 1 file covered. (44.44%)

540 existing lines in 57 files now uncovered.

13394 of 157210 relevant lines covered (8.52%)

22.93 hits per line

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

91.21
/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\QueryNameGeneratorInterface;
18
use ApiPlatform\Metadata\Link;
19
use ApiPlatform\Metadata\Operation;
20
use Doctrine\ORM\Mapping\ClassMetadata;
21
use Doctrine\ORM\QueryBuilder;
22
use Doctrine\Persistence\ManagerRegistry;
23

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

31
    private ManagerRegistry $managerRegistry;
32

33
    private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, QueryNameGeneratorInterface $queryNameGenerator, array $context, string $entityClass, Operation $operation): void
34
    {
35
        if (!$identifiers) {
760✔
36
            return;
465✔
37
        }
38

39
        $manager = $this->managerRegistry->getManagerForClass($entityClass);
325✔
40
        $doctrineClassMetadata = $manager->getClassMetadata($entityClass);
325✔
41
        $alias = $queryBuilder->getRootAliases()[0];
325✔
42

43
        $links = $this->getLinks($entityClass, $operation, $context);
325✔
44

45
        if (!$links) {
325✔
46
            return;
×
47
        }
48

49
        $previousAlias = $alias;
325✔
50
        $previousJoinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
325✔
51
        $expressions = [];
325✔
52
        $identifiers = array_reverse($identifiers);
325✔
53

54
        foreach (array_reverse($links) as $link) {
325✔
55
            if (null !== $link->getExpandedValue() || !$link->getFromClass()) {
325✔
UNCOV
56
                continue;
2✔
57
            }
58

59
            $fromClass = $link->getFromClass();
325✔
60
            if (!$this->managerRegistry->getManagerForClass($fromClass)) {
325✔
UNCOV
61
                $fromClass = $this->getLinkFromClass($link, $operation);
4✔
62
            }
63

64
            $fromClassMetadata = $manager->getClassMetadata($fromClass);
325✔
65
            $identifierProperties = $link->getIdentifiers();
325✔
66
            $hasCompositeIdentifiers = 1 < \count($identifierProperties);
325✔
67

68
            if (!$link->getFromProperty() && !$link->getToProperty()) {
325✔
69
                $currentAlias = $fromClass === $entityClass ? $alias : $queryNameGenerator->generateJoinAlias($alias);
290✔
70

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

77
                $previousAlias = $currentAlias;
290✔
78
                $previousJoinProperties = $fromClassMetadata->getIdentifierFieldNames();
290✔
79
                continue;
290✔
80
            }
81

UNCOV
82
            $joinProperties = $doctrineClassMetadata->getIdentifierFieldNames();
60✔
83

UNCOV
84
            if ($link->getFromProperty() && !$link->getToProperty()) {
60✔
UNCOV
85
                $joinAlias = $queryNameGenerator->generateJoinAlias('m');
30✔
UNCOV
86
                $associationMapping = $fromClassMetadata->getAssociationMapping($link->getFromProperty()); // @phpstan-ignore-line
30✔
UNCOV
87
                $relationType = $associationMapping['type'];
30✔
88

UNCOV
89
                if ($relationType & ClassMetadata::TO_MANY) {
30✔
UNCOV
90
                    $nextAlias = $queryNameGenerator->generateJoinAlias($alias);
21✔
UNCOV
91
                    $whereClause = [];
21✔
UNCOV
92
                    foreach ($identifierProperties as $identifierProperty) {
21✔
UNCOV
93
                        $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
21✔
UNCOV
94
                        $whereClause[] = "$nextAlias.{$identifierProperty} = :$placeholder";
21✔
UNCOV
95
                        $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
21✔
96
                    }
97

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

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

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

UNCOV
123
                $previousAlias = $joinAlias;
11✔
UNCOV
124
                $previousJoinProperties = $joinProperties;
11✔
UNCOV
125
                continue;
11✔
126
            }
127

UNCOV
128
            $joinAlias = $queryNameGenerator->generateJoinAlias($alias);
33✔
UNCOV
129
            $queryBuilder->join("{$previousAlias}.{$link->getToProperty()}", $joinAlias);
33✔
130

UNCOV
131
            foreach ($identifierProperties as $identifierProperty) {
33✔
UNCOV
132
                $placeholder = $queryNameGenerator->generateParameterName($identifierProperty);
33✔
UNCOV
133
                $queryBuilder->andWhere("$joinAlias.$identifierProperty = :$placeholder");
33✔
UNCOV
134
                $queryBuilder->setParameter($placeholder, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null), $fromClassMetadata->getTypeOfField($identifierProperty));
33✔
135
            }
136

UNCOV
137
            $previousAlias = $joinAlias;
33✔
UNCOV
138
            $previousJoinProperties = $joinProperties;
33✔
139
        }
140

141
        if ($expressions) {
325✔
UNCOV
142
            $i = 0;
21✔
UNCOV
143
            $clause = '';
21✔
UNCOV
144
            foreach ($expressions as $alias => $expression) {
21✔
UNCOV
145
                if (0 === $i) {
21✔
UNCOV
146
                    $clause .= "$alias IN (".$expression;
21✔
UNCOV
147
                    ++$i;
21✔
UNCOV
148
                    continue;
21✔
149
                }
150

151
                $clause .= " AND $alias IN (".$expression;
×
152
                ++$i;
×
153
            }
154

UNCOV
155
            $queryBuilder->andWhere($clause.str_repeat(')', $i));
21✔
156
        }
157
    }
158

159
    private function getLinkFromClass(Link $link, Operation $operation): string
160
    {
UNCOV
161
        $fromClass = $link->getFromClass();
4✔
UNCOV
162
        if ($fromClass === $operation->getClass() && $entityClass = $this->getStateOptionsEntityClass($operation)) {
4✔
UNCOV
163
            return $entityClass;
4✔
164
        }
165

166
        $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation();
×
167

168
        if ($entityClass = $this->getStateOptionsEntityClass($operation)) {
×
169
            return $entityClass;
×
170
        }
171

172
        throw new \Exception('Can not found a doctrine class for this link.');
×
173
    }
174

175
    private function getStateOptionsEntityClass(Operation $operation): ?string
176
    {
UNCOV
177
        if (($options = $operation->getStateOptions()) && $options instanceof Options && $entityClass = $options->getEntityClass()) {
4✔
UNCOV
178
            return $entityClass;
4✔
179
        }
180

181
        return null;
×
182
    }
183
}
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