Completed
Pull Request — master (#28)
by
unknown
02:31
created

AutoJoin::getEmbeddableAlias()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.8571
cc 3
eloc 13
nc 3
nop 1
1
<?php
2
3
namespace RulerZ\Executor\DoctrineQueryBuilder;
4
5
use Doctrine\ORM\QueryBuilder;
6
use Doctrine\ORM\EntityManager;
7
8
class AutoJoin
9
{
10
    const ALIAS_PREFIX = 'rulerz_';
11
12
    /**
13
     * List of root and association entity embeddables
14
     *
15
     * @var array
16
     */
17
    private $embeddables = null;
18
19
    /**
20
     * Associative list of known aliases (selected or joined tables).
21
     *
22
     * @var array
23
     */
24
    private $knownAliases = [];
25
26
    /**
27
     * Associative list of joined tables and their alias.
28
     *
29
     * @var array
30
     */
31
    private $joinMap = null;
32
33
    /**
34
     * @var QueryBuilder
35
     */
36
    private $queryBuilder;
37
38
    /**
39
     * @var array
40
     */
41
    private $expectedJoinChains = [];
42
43
    public function __construct(QueryBuilder $queryBuilder, array $expectedJoinChains)
44
    {
45
        $this->queryBuilder       = $queryBuilder;
46
        $this->expectedJoinChains = $expectedJoinChains;
47
    }
48
49
    public function getJoinAlias($table)
50
    {
51
        if ($this->embeddables === null) {
52
            $this->embeddables = $this->analizeEmbeddables($this->queryBuilder);
53
        }
54
55
        if ($this->joinMap === null) {
56
            $this->joinMap      = $this->analizeJoinedTables($this->queryBuilder);
57
            $this->knownAliases = array_flip($this->queryBuilder->getRootAliases()) + array_flip($this->joinMap);
58
59
            $this->autoJoin($this->queryBuilder);
60
        }
61
62
        // the table is identified as an embeddable
63
        if (array_search($table, $this->embeddables) !== false) {
64
            return $this->getEmbeddableAlias($table);
65
        }
66
67
        // the table name is a known alias (already join for instance) so we
68
        // don't need to do anything.
69
        if (isset($this->knownAliases[$table])) {
70
            return $table;
71
        }
72
73
        // otherwise the table should have automatically been joined, so we use our table prefix
74
        if (isset($this->knownAliases[self::ALIAS_PREFIX.$table])) {
75
            return self::ALIAS_PREFIX . $table;
76
        }
77
78
        throw new \RuntimeException(sprintf('Could not automatically join table "%s"', $table));
79
    }
80
81
    private function getEmbeddableAlias($table)
82
    {
83
        $embeddable_alias = '';
0 ignored issues
show
Unused Code introduced by
$embeddable_alias is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
84
        $embeddable_dimensions = explode('.', $table);
85
86
        if (count($embeddable_dimensions) === 1) {
87
            // the embeddable is not inside an association, so we use the root alias prefix
88
            $embeddable_alias = $this->queryBuilder->getRootAliases()[0] . '.' . $table;
89
        }
90
        else {
91
            // remove the embeddable's property
92
            array_pop($embeddable_dimensions);
93
            $table_alias = implode('.', $embeddable_dimensions);
94
95
            if (isset($this->knownAliases[$table_alias])) {
96
                // the table name is a known alias (already join for instance) so we
97
                // don't need to do anything.
98
                $embeddable_alias = $table;
99
            } else {
100
                // otherwise the table should have automatically been joined, so we use our table prefix
101
                $embeddable_alias = self::ALIAS_PREFIX . $table;
102
            }
103
        }
104
105
        return $embeddable_alias;
106
    }
107
108
    private function traverseAssociationsForEmbeddables(EntityManager $entityManager, array $associations, $fieldNamePrefix = false)
109
    {
110
        $associationsEmbeddables = array();
111
112
        foreach ($associations as $association) {
113
            $classMetaData = $entityManager->getClassMetadata($association['targetEntity']);
114
115
            foreach ($classMetaData->embeddedClasses as $embeddedClassKey => $embeddedClass) {
116
                $associationsEmbeddables[] = implode('.', array_filter(array($fieldNamePrefix, $association['fieldName'], $embeddedClassKey)));
117
            }
118
119
            $associationMappings = $classMetaData->getAssociationMappings();
120
            $associationMappings = array_filter($associationMappings, function($associationMapping) {
121
                return $associationMapping['isOwningSide'] === false;
122
            });
123
124
            if (count($associationMappings) !== 0) {
125
                $traversedAssociationsEmbeddables = $this->traverseAssociationsForEmbeddables($entityManager, $associationMappings, $association['fieldName']);
126
                $associationsEmbeddables = array_merge($associationsEmbeddables, $traversedAssociationsEmbeddables);
127
            }
128
        }
129
130
        return $associationsEmbeddables;
131
    }
132
133
    private function analizeEmbeddables(QueryBuilder $queryBuilder)
134
    {
135
        $embeddables = array();
136
        $entityManager = $queryBuilder->getEntityManager();
137
        $rootEntities = $queryBuilder->getRootEntities();
138
139
        foreach ($rootEntities as $rootEntity) {
140
            $classMetaData = $entityManager->getClassMetadata($rootEntity);
141
142
            foreach ($classMetaData->embeddedClasses as $embeddedClassKey => $embeddedClass) {
143
                $embeddables[] = $embeddedClassKey;
144
            }
145
146
            $traversedAssociationsEmbeddables = $this->traverseAssociationsForEmbeddables($entityManager, $classMetaData->getAssociationMappings());
147
            $embeddables = array_merge($embeddables, $traversedAssociationsEmbeddables);
148
        }
149
150
        return $embeddables;
151
    }
152
153
    /**
154
     * Builds an associative array of already joined tables and their alias.
155
     *
156
     * @param QueryBuilder $queryBuilder
157
     *
158
     * @return array
159
     */
160
    private function analizeJoinedTables(QueryBuilder $queryBuilder)
161
    {
162
        $joinMap = [];
163
        $joins   = $queryBuilder->getDQLPart('join');
164
        foreach (array_keys($joins) as $fromTable) {
165
            foreach ($joins[$fromTable] as $join) {
166
                $joinMap[$join->getJoin()] = $join->getAlias();
167
            }
168
        }
169
        return $joinMap;
170
    }
171
172
    private function autoJoin(QueryBuilder $queryBuilder)
173
    {
174
        foreach ($this->expectedJoinChains as $tablesToJoin) {
175
            // if the table is an embeddable, the property needs to be removed
176
            if (array_search(implode('.', $tablesToJoin), $this->embeddables) !== false) {
177
                array_pop($tablesToJoin);
178
            }
179
180
            // check if the first dimension is a known alias
181
            if (isset($this->knownAliases[$tablesToJoin[0]])) {
182
                $joinTo = $tablesToJoin[0];
183
                array_pop($tablesToJoin);
184
            } else { // if not, it's the root table
185
                $joinTo = $queryBuilder->getRootAliases()[0];
186
            }
187
188
            foreach ($tablesToJoin as $table) {
189
                $joinAlias = self::ALIAS_PREFIX . $table;
190
                $join      = sprintf('%s.%s', $joinTo, $table);
191
192
                if (!isset($this->joinMap[$join])) {
193
                    $this->joinMap[$join]           = $joinAlias;
194
                    $this->knownAliases[$joinAlias] = true;
195
196
                    $queryBuilder->join(sprintf('%s.%s', $joinTo, $table), $joinAlias);
197
                } else {
198
                    $joinAlias = $this->joinMap[$join];
199
                }
200
201
                $joinTo = $joinAlias;
202
            }
203
        }
204
    }
205
}
206