Passed
Pull Request — 2.5 (#3308)
by Karol
03:50
created

QueryChecker::hasLeftJoin()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Bridge\Doctrine\Orm\Util;
15
16
use Doctrine\Common\Persistence\ManagerRegistry;
17
use Doctrine\ORM\Mapping\ClassMetadata;
18
use Doctrine\ORM\Query\Expr\Join;
19
use Doctrine\ORM\QueryBuilder;
20
21
/**
22
 * Utility functions for working with Doctrine ORM query.
23
 *
24
 * @author Teoh Han Hui <[email protected]>
25
 * @author Vincent Chalamon <[email protected]>
26
 *
27
 * @internal
28
 */
29
final class QueryChecker
30
{
31
    private function __construct()
32
    {
33
    }
34
35
    /**
36
     * Determines whether the QueryBuilder uses a HAVING clause.
37
     */
38
    public static function hasHavingClause(QueryBuilder $queryBuilder): bool
39
    {
40
        return null !== $queryBuilder->getDQLPart('having');
41
    }
42
43
    /**
44
     * Determines whether the QueryBuilder has any root entity with foreign key identifier.
45
     */
46
    public static function hasRootEntityWithForeignKeyIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
47
    {
48
        foreach ($queryBuilder->getRootEntities() as $rootEntity) {
49
            /** @var ClassMetadata $rootMetadata */
50
            $rootMetadata = $managerRegistry
51
                ->getManagerForClass($rootEntity)
52
                ->getClassMetadata($rootEntity);
53
54
            if ($rootMetadata->containsForeignIdentifier) {
55
                return true;
56
            }
57
        }
58
59
        return false;
60
    }
61
62
    /**
63
     * Determines whether the QueryBuilder has any composite identifier.
64
     */
65
    public static function hasRootEntityWithCompositeIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
66
    {
67
        foreach ($queryBuilder->getRootEntities() as $rootEntity) {
68
            /** @var ClassMetadata $rootMetadata */
69
            $rootMetadata = $managerRegistry
70
                ->getManagerForClass($rootEntity)
71
                ->getClassMetadata($rootEntity);
72
73
            if ($rootMetadata->isIdentifierComposite) {
74
                return true;
75
            }
76
        }
77
78
        return false;
79
    }
80
81
    /**
82
     * Determines whether the QueryBuilder has a limit on the maximum number of results.
83
     */
84
    public static function hasMaxResults(QueryBuilder $queryBuilder): bool
85
    {
86
        return null !== $queryBuilder->getMaxResults();
87
    }
88
89
    /**
90
     * Determines whether the QueryBuilder has ORDER BY on a column from a fetch joined to-many association.
91
     */
92
    public static function hasOrderByOnFetchJoinedToManyAssociation(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
93
    {
94
        if (
95
            0 === \count($selectParts = $queryBuilder->getDQLPart('select')) ||
96
            0 === \count($queryBuilder->getDQLPart('join')) ||
97
            0 === \count($orderByParts = $queryBuilder->getDQLPart('orderBy'))
98
        ) {
99
            return false;
100
        }
101
102
        $rootAliases = $queryBuilder->getRootAliases();
103
104
        $selectAliases = [];
105
106
        foreach ($selectParts as $select) {
107
            foreach ($select->getParts() as $part) {
108
                [$alias] = explode('.', $part);
109
110
                $selectAliases[] = $alias;
111
            }
112
        }
113
114
        $selectAliases = array_diff($selectAliases, $rootAliases);
115
        if (0 === \count($selectAliases)) {
116
            return false;
117
        }
118
119
        $orderByAliases = [];
120
121
        foreach ($orderByParts as $orderBy) {
122
            foreach ($orderBy->getParts() as $part) {
123
                if (false !== strpos($part, '.')) {
124
                    [$alias] = explode('.', $part);
125
126
                    $orderByAliases[] = $alias;
127
                }
128
            }
129
        }
130
131
        $orderByAliases = array_diff($orderByAliases, $rootAliases);
132
        if (0 === \count($orderByAliases)) {
133
            return false;
134
        }
135
136
        foreach ($orderByAliases as $orderByAlias) {
137
            $inToManyContext = false;
138
139
            foreach (QueryBuilderHelper::traverseJoins($orderByAlias, $queryBuilder, $managerRegistry) as $alias => [$metadata, $association]) {
140
                if ($inToManyContext && \in_array($alias, $selectAliases, true)) {
141
                    return true;
142
                }
143
144
                if (null !== $association && $metadata->isCollectionValuedAssociation($association)) {
145
                    $inToManyContext = true;
146
                }
147
            }
148
        }
149
150
        return false;
151
    }
152
153
    /**
154
     * Determines whether the QueryBuilder has ORDER BY on a column from a fetch joined to-many association.
155
     *
156
     * @deprecated
157
     */
158
    public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
159
    {
160
        @trigger_error(sprintf('The use of "%s::hasOrderByOnToManyJoin()" is deprecated since 2.4 and will be removed in 3.0. Use "%1$s::hasOrderByOnFetchJoinedToManyAssociation()" instead.', __CLASS__), E_USER_DEPRECATED);
161
162
        return self::hasOrderByOnFetchJoinedToManyAssociation($queryBuilder, $managerRegistry);
163
    }
164
165
    /**
166
     * Determines whether the QueryBuilder has a joined to-many association.
167
     */
168
    public static function hasJoinedToManyAssociation(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
169
    {
170
        if (
171
            0 === \count($queryBuilder->getDQLPart('join'))
172
        ) {
173
            return false;
174
        }
175
176
        $joinAliases = array_diff($queryBuilder->getAllAliases(), $queryBuilder->getRootAliases());
177
        if (0 === \count($joinAliases)) {
178
            return false;
179
        }
180
181
        foreach ($joinAliases as $joinAlias) {
182
            foreach (QueryBuilderHelper::traverseJoins($joinAlias, $queryBuilder, $managerRegistry) as $alias => [$metadata, $association]) {
183
                if (null !== $association && $metadata->isCollectionValuedAssociation($association)) {
184
                    return true;
185
                }
186
            }
187
        }
188
189
        return false;
190
    }
191
}
192