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

api-platform / core / 13724363992

07 Mar 2025 03:55PM UTC coverage: 8.174% (-0.3%) from 8.518%
13724363992

Pull #7004

github

web-flow
Merge c88b49ea9 into 1e0bc9dc8
Pull Request #7004: fix(doctrine): correct the use statement for ManagerRegistry

12837 of 157041 relevant lines covered (8.17%)

13.55 hits per line

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

78.48
/src/Doctrine/Orm/Util/QueryBuilderHelper.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\Util;
15

16
use Doctrine\ORM\Query\Expr\Join;
17
use Doctrine\ORM\QueryBuilder;
18
use Doctrine\Persistence\ManagerRegistry;
19

20
/**
21
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
22
 *
23
 * @internal
24
 */
25
final class QueryBuilderHelper
26
{
27
    private function __construct()
28
    {
29
    }
×
30

31
    /**
32
     * Adds a join to the QueryBuilder if none exists.
33
     */
34
    public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, ?string $joinType = null, ?string $conditionType = null, ?string $condition = null, ?string $originAlias = null, ?string $newAlias = null): string
35
    {
36
        $join = self::getExistingJoin($queryBuilder, $alias, $association, $originAlias);
55✔
37

38
        if (null !== $join) {
55✔
39
            return $join->getAlias();
×
40
        }
41

42
        $associationAlias = $newAlias ?? $queryNameGenerator->generateJoinAlias($association);
55✔
43
        $query = "$alias.$association";
55✔
44

45
        if (Join::LEFT_JOIN === $joinType || QueryChecker::hasLeftJoin($queryBuilder)) {
55✔
46
            $queryBuilder->leftJoin($query, $associationAlias, $conditionType, $condition);
4✔
47
        } else {
48
            $queryBuilder->innerJoin($query, $associationAlias, $conditionType, $condition);
53✔
49
        }
50

51
        return $associationAlias;
55✔
52
    }
53

54
    /**
55
     * Gets the entity class name by an alias used in the QueryBuilder.
56
     */
57
    public static function getEntityClassByAlias(string $alias, QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): string
58
    {
59
        if (!\in_array($alias, $queryBuilder->getAllAliases(), true)) {
×
60
            throw new \LogicException(\sprintf('The alias "%s" does not exist in the QueryBuilder.', $alias));
×
61
        }
62

63
        $rootAliasMap = self::mapRootAliases($queryBuilder->getRootAliases(), $queryBuilder->getRootEntities());
×
64

65
        if (isset($rootAliasMap[$alias])) {
×
66
            return $rootAliasMap[$alias];
×
67
        }
68

69
        $metadata = null;
×
70

71
        foreach (self::traverseJoins($alias, $queryBuilder, $managerRegistry) as [$currentMetadata]) {
×
72
            $metadata = $currentMetadata;
×
73
        }
74

75
        if (null === $metadata) {
×
76
            throw new \LogicException(\sprintf('The alias "%s" does not exist in the QueryBuilder.', $alias));
×
77
        }
78

79
        return $metadata->getName();
×
80
    }
81

82
    /**
83
     * Finds the root alias for an alias used in the QueryBuilder.
84
     */
85
    public static function findRootAlias(string $alias, QueryBuilder $queryBuilder): string
86
    {
87
        if (\in_array($alias, $queryBuilder->getRootAliases(), true)) {
79✔
88
            return $alias;
×
89
        }
90

91
        foreach ($queryBuilder->getDQLPart('join') as $rootAlias => $joins) {
79✔
92
            foreach ($joins as $join) {
79✔
93
                if ($alias === $join->getAlias()) {
79✔
94
                    return $rootAlias;
79✔
95
                }
96
            }
97
        }
98

99
        throw new \LogicException(\sprintf('The alias "%s" does not exist in the QueryBuilder.', $alias));
×
100
    }
101

102
    /**
103
     * Traverses through the joins for an alias used in the QueryBuilder.
104
     *
105
     * @return \Generator<string, array>
106
     */
107
    public static function traverseJoins(string $alias, QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): \Generator
108
    {
109
        $rootAliasMap = self::mapRootAliases($queryBuilder->getRootAliases(), $queryBuilder->getRootEntities());
79✔
110

111
        $joinParts = $queryBuilder->getDQLPart('join');
79✔
112
        $rootAlias = self::findRootAlias($alias, $queryBuilder);
79✔
113

114
        $joinAliasMap = self::mapJoinAliases($joinParts[$rootAlias]);
79✔
115

116
        $aliasMap = [...$rootAliasMap, ...$joinAliasMap];
79✔
117

118
        $apexEntityClass = null;
79✔
119
        $associationStack = [];
79✔
120
        $aliasStack = [];
79✔
121
        $currentAlias = $alias;
79✔
122

123
        while (null === $apexEntityClass) {
79✔
124
            if (!isset($aliasMap[$currentAlias])) {
79✔
125
                throw new \LogicException(\sprintf('Unknown alias "%s".', $currentAlias));
×
126
            }
127

128
            if (\is_string($aliasMap[$currentAlias])) {
79✔
129
                $aliasStack[] = $currentAlias;
79✔
130
                $apexEntityClass = $aliasMap[$currentAlias];
79✔
131
            } else {
132
                [$parentAlias, $association] = $aliasMap[$currentAlias];
76✔
133

134
                $associationStack[] = $association;
76✔
135
                $aliasStack[] = $currentAlias;
76✔
136
                $currentAlias = $parentAlias;
76✔
137
            }
138
        }
139

140
        $entityClass = $apexEntityClass;
79✔
141

142
        while (null !== ($alias = array_pop($aliasStack))) {
79✔
143
            $metadata = $managerRegistry
79✔
144
                ->getManagerForClass($entityClass)
79✔
145
                ->getClassMetadata($entityClass);
79✔
146

147
            $association = array_pop($associationStack);
79✔
148

149
            yield $alias => [
79✔
150
                $metadata,
79✔
151
                $association,
79✔
152
            ];
79✔
153

154
            if (null !== $association) {
61✔
155
                $entityClass = $metadata->getAssociationTargetClass($association);
58✔
156
            }
157
        }
158
    }
159

160
    /**
161
     * Gets the existing join from QueryBuilder DQL parts.
162
     */
163
    public static function getExistingJoin(QueryBuilder $queryBuilder, string $alias, string $association, ?string $originAlias = null): ?Join
164
    {
165
        $parts = $queryBuilder->getDQLPart('join');
132✔
166
        $rootAlias = $originAlias ?? $queryBuilder->getRootAliases()[0];
132✔
167

168
        if (!isset($parts[$rootAlias])) {
132✔
169
            return null;
132✔
170
        }
171

172
        foreach ($parts[$rootAlias] as $join) {
56✔
173
            /** @var Join $join */
174
            if (\sprintf('%s.%s', $alias, $association) === $join->getJoin()) {
56✔
175
                return $join;
7✔
176
            }
177
        }
178

179
        return null;
52✔
180
    }
181

182
    /**
183
     * Maps the root aliases to root entity classes.
184
     *
185
     * @return array<string, string>
186
     */
187
    private static function mapRootAliases(array $rootAliases, array $rootEntities): array
188
    {
189
        /** @var false|array $aliasMap */
190
        $aliasMap = array_combine($rootAliases, $rootEntities);
79✔
191
        if (false === $aliasMap) {
79✔
192
            throw new \LogicException('Number of root aliases and root entities do not match.');
×
193
        }
194

195
        return $aliasMap;
79✔
196
    }
197

198
    /**
199
     * Maps the join aliases to the parent alias and association, or the entity class.
200
     *
201
     * @return array<string, string[]|string>
202
     */
203
    private static function mapJoinAliases(iterable $joins): array
204
    {
205
        $aliasMap = [];
79✔
206

207
        foreach ($joins as $join) {
79✔
208
            $alias = $join->getAlias();
79✔
209
            $relationship = $join->getJoin();
79✔
210

211
            if (str_contains((string) $relationship, '.')) {
79✔
212
                $aliasMap[$alias] = explode('.', (string) $relationship);
76✔
213
            } else {
214
                $aliasMap[$alias] = $relationship;
3✔
215
            }
216
        }
217

218
        return $aliasMap;
79✔
219
    }
220
}
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