Completed
Push — 2.0 ( 0357a9...3fe951 )
by Peter
08:22 queued 10s
created

DQLContextResolver::resolveAlias()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 43
rs 8.2986
c 0
b 0
f 0
cc 7
nc 10
nop 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of the Happyr Doctrine Specification package.
6
 *
7
 * (c) Tobias Nyholm <[email protected]>
8
 *     Kacper Gunia <[email protected]>
9
 *     Peter Gribanov <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Happyr\DoctrineSpecification;
16
17
use Doctrine\ORM\Query\Expr\Join;
18
use Doctrine\ORM\QueryBuilder;
19
20
final class DQLContextResolver
21
{
22
    /**
23
     * @var bool
24
     */
25
    private static $deadJoinsProtection = true;
26
27
    /**
28
     * @var bool
29
     */
30
    private static $conflictProtection = true;
31
32
    /**
33
     * @var bool
34
     */
35
    private static $autoJoining = true;
36
37
    /**
38
     * @param QueryBuilder $qb
39
     * @param string       $context
40
     *
41
     * @return string
42
     */
43
    public static function resolveAlias(QueryBuilder $qb, string $context): string
44
    {
45
        $dqlAliasParts = explode('.', $context);
46
47
        // any given alias can be used
48
        if (!self::$deadJoinsProtection) {
49
            return array_pop($dqlAliasParts);
50
        }
51
52
        // use alias without conflict protection and dead joins.
53
        // we do not check parent joins for the found join.
54
        if (!self::$conflictProtection) {
55
            $dqlAlias = end($dqlAliasParts);
56
57
            if (in_array($dqlAlias, $qb->getAllAliases(), true)) {
58
                return $dqlAlias;
59
            }
60
61
            reset($dqlAliasParts);
62
        }
63
64
        $dqlAlias = $rootAlias = array_shift($dqlAliasParts);
65
66
        // check all parts of context path
67
        while ([] !== $dqlAliasParts) {
68
            $field = array_shift($dqlAliasParts);
69
            $join = sprintf('%s.%s', $dqlAlias, $field);
70
            $joinPart = self::findJoin($qb, $rootAlias, $join);
71
72
            // use exists or create new join
73
            if ($joinPart instanceof Join) {
74
                $dqlAlias = $joinPart->getAlias();
75
            } else {
76
                $dqlAlias = self::getUniqueAlias($qb, $field);
77
78
                if (self::$autoJoining) {
79
                    $qb->join($join, $dqlAlias);
80
                }
81
            }
82
        }
83
84
        return $dqlAlias;
85
    }
86
87
    /**
88
     * @return bool
89
     */
90
    public static function isDeadJoinsProtectionEnabled(): bool
91
    {
92
        return self::$deadJoinsProtection;
93
    }
94
95
    public static function enableDeadJoinsProtection(): void
96
    {
97
        self::$deadJoinsProtection = true;
98
    }
99
100
    public static function disableDeadJoinsProtection(): void
101
    {
102
        self::$deadJoinsProtection = false;
103
    }
104
105
    /**
106
     * @return bool
107
     */
108
    public static function isConflictProtectionEnabled(): bool
109
    {
110
        return self::$conflictProtection;
111
    }
112
113
    public static function enableConflictProtection(): void
114
    {
115
        self::$conflictProtection = true;
116
    }
117
118
    public static function disableConflictProtection(): void
119
    {
120
        self::$conflictProtection = false;
121
    }
122
123
    /**
124
     * @return bool
125
     */
126
    public static function isAutoJoiningEnabled(): bool
127
    {
128
        return self::$autoJoining;
129
    }
130
131
    public static function enableAutoJoining(): void
132
    {
133
        self::$autoJoining = true;
134
    }
135
136
    public static function disableAutoJoining(): void
137
    {
138
        self::$autoJoining = false;
139
    }
140
141
    /**
142
     * Find configured relationship.
143
     *
144
     * @param QueryBuilder $qb
145
     * @param string       $rootAlias
146
     * @param string       $join
147
     *
148
     * @return Join|null
149
     */
150
    private static function findJoin(QueryBuilder $qb, string $rootAlias, string $join): ?Join
151
    {
152
        $joinParts = $qb->getDQLPart('join');
153
154
        if (!array_key_exists($rootAlias, $joinParts)) {
155
            return null;
156
        }
157
158
        foreach ($joinParts[$rootAlias] as $joinPart) {
159
            if ($joinPart instanceof Join && $joinPart->getJoin() === $join) {
160
                return $joinPart;
161
            }
162
        }
163
164
        return null;
165
    }
166
167
    /**
168
     * @param QueryBuilder $qb
169
     * @param string       $dqlAlias
170
     *
171
     * @return string
172
     */
173
    private static function getUniqueAlias(QueryBuilder $qb, string $dqlAlias): string
174
    {
175
        $newAlias = $dqlAlias;
176
177
        while (self::$conflictProtection && in_array($newAlias, $qb->getAllAliases(), true)) {
178
            $newAlias = uniqid($dqlAlias);
179
        }
180
181
        return $newAlias;
182
    }
183
}
184