Completed
Push — master ( 29c52c...d78909 )
by Kévin
03:18
created

QueryChecker::hasLeftJoin()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
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
final class QueryChecker
28
{
29
    private function __construct()
30
    {
31
    }
32
33
    /**
34
     * Determines whether the query builder uses a HAVING clause.
35
     *
36
     * @param QueryBuilder $queryBuilder
37
     *
38
     * @return bool
39
     */
40
    public static function hasHavingClause(QueryBuilder $queryBuilder): bool
41
    {
42
        return !empty($queryBuilder->getDQLPart('having'));
43
    }
44
45
    /**
46
     * Determines whether the query builder has any root entity with foreign key identifier.
47
     *
48
     * @param QueryBuilder    $queryBuilder
49
     * @param ManagerRegistry $managerRegistry
50
     *
51
     * @return bool
52
     */
53
    public static function hasRootEntityWithForeignKeyIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
54
    {
55
        return self::hasRootEntityWithIdentifier($queryBuilder, $managerRegistry, true);
56
    }
57
58
    /**
59
     * Determines whether the query builder has any composite identifier.
60
     *
61
     * @param QueryBuilder    $queryBuilder
62
     * @param ManagerRegistry $managerRegistry
63
     *
64
     * @return bool
65
     */
66
    public static function hasRootEntityWithCompositeIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
67
    {
68
        return self::hasRootEntityWithIdentifier($queryBuilder, $managerRegistry, false);
69
    }
70
71
    /**
72
     * Detects if the root entity has the given identifier.
73
     *
74
     * @param QueryBuilder    $queryBuilder
75
     * @param ManagerRegistry $managerRegistry
76
     * @param bool            $isForeign
77
     *
78
     * @return bool
79
     */
80
    private static function hasRootEntityWithIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry, bool $isForeign): bool
81
    {
82
        foreach ($queryBuilder->getRootEntities() as $rootEntity) {
83
            $rootMetadata = $managerRegistry
84
                ->getManagerForClass($rootEntity)
85
                ->getClassMetadata($rootEntity);
86
87
            if ($rootMetadata instanceof ClassMetadata && ($isForeign ? $rootMetadata->isIdentifierComposite : $rootMetadata->containsForeignIdentifier)) {
88
                return true;
89
            }
90
        }
91
92
        return false;
93
    }
94
95
    /**
96
     * Determines whether the query builder has the maximum number of results specified.
97
     *
98
     * @param QueryBuilder $queryBuilder
99
     *
100
     * @return bool
101
     */
102
    public static function hasMaxResults(QueryBuilder $queryBuilder): bool
103
    {
104
        return null !== $queryBuilder->getMaxResults();
105
    }
106
107
    /**
108
     * Determines whether the query builder has ORDER BY on entity joined through
109
     * to-many association.
110
     *
111
     * @param QueryBuilder    $queryBuilder
112
     * @param ManagerRegistry $managerRegistry
113
     *
114
     * @return bool
115
     */
116
    public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
117
    {
118
        if (
119
            empty($orderByParts = $queryBuilder->getDQLPart('orderBy')) ||
120
            empty($joinParts = $queryBuilder->getDQLPart('join'))
121
        ) {
122
            return false;
123
        }
124
125
        $orderByAliases = [];
126
        foreach ($orderByParts as $orderBy) {
127
            $parts = QueryJoinParser::getOrderByParts($orderBy);
128
129
            foreach ($parts as $part) {
130
                if (false !== ($pos = strpos($part, '.'))) {
131
                    $alias = substr($part, 0, $pos);
132
133
                    $orderByAliases[$alias] = true;
134
                }
135
            }
136
        }
137
138
        if (!empty($orderByAliases)) {
139
            foreach ($joinParts as $joins) {
140
                foreach ($joins as $join) {
141
                    $alias = QueryJoinParser::getJoinAlias($join);
142
143
                    if (isset($orderByAliases[$alias])) {
144
                        $relationship = QueryJoinParser::getJoinRelationship($join);
145
                        list($parentAlias, $association) = explode('.', $relationship);
146
147
                        $parentMetadata = QueryJoinParser::getClassMetadataFromJoinAlias($parentAlias, $queryBuilder, $managerRegistry);
148
149
                        if ($parentMetadata->isCollectionValuedAssociation($association)) {
150
                            return true;
151
                        }
152
                    }
153
                }
154
            }
155
        }
156
157
        return false;
158
    }
159
160
    /**
161
     * Determines whether the query builder already has a left join.
162
     *
163
     * @param QueryBuilder $queryBuilder
164
     *
165
     * @return bool
166
     */
167
    public static function hasLeftJoin(QueryBuilder $queryBuilder): bool
168
    {
169
        foreach ($queryBuilder->getDQLPart('join') as $dqlParts) {
170
            foreach ($dqlParts as $dqlPart) {
171
                if (Join::LEFT_JOIN === $dqlPart->getJoinType()) {
172
                    return true;
173
                }
174
            }
175
        }
176
177
        return false;
178
    }
179
}
180