Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
102 / 102 |
|
100.00% |
26 / 26 |
CRAP | |
100.00% |
1 / 1 |
Base | |
100.00% |
102 / 102 |
|
100.00% |
26 / 26 |
48 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
29 / 29 |
|
100.00% |
1 / 1 |
9 | |||
__toString | n/a |
0 / 0 |
n/a |
0 / 0 |
4 | |||||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
addSelect | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
setDistinctSelect | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setResolveLevel | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getResolveLevel | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getAlias | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getTablename | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
addTable | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
addTableAlias | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
addRequiredJoins | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addResolvedReferences | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
addJoin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
leftJoin | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getSql | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getParameters | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getReferenceMapping | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
expression | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addEntityMapping | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getPrefixed | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPrefixedList | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
addReferenceMapping | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
addLimit | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
addValues | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
postProcess | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
postProcessJoins | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace BO\Zmsdb\Query; |
4 | |
5 | use Solution10\SQL\Select; |
6 | use Solution10\SQL\Insert; |
7 | use Solution10\SQL\Update; |
8 | use Solution10\SQL\Delete; |
9 | use Solution10\SQL\Dialect\MySQL; |
10 | use Solution10\SQL\Expression; |
11 | |
12 | /** |
13 | * Base class to construct entity specific queries |
14 | * Usually used with the interface MappingInterface |
15 | * Further, it allows to react to resolveReferences as parameter to calling methods |
16 | */ |
17 | |
18 | /** |
19 | * @SuppressWarnings(NumberOfChildren) |
20 | * @SuppressWarnings(Complexity) |
21 | * |
22 | */ |
23 | abstract class Base |
24 | { |
25 | /** |
26 | * Identifier for the type of query |
27 | */ |
28 | const SELECT = 'SELECT'; |
29 | const INSERT = 'INSERT'; |
30 | const UPDATE = 'UPDATE'; |
31 | const REPLACE = 'REPLACE'; |
32 | const DELETE = 'DELETE'; |
33 | |
34 | /** |
35 | * Name of table in DB |
36 | */ |
37 | const TABLE = null; |
38 | /** |
39 | * Alias used to access TABLE |
40 | */ |
41 | const ALIAS = null; |
42 | |
43 | /** |
44 | * @var \Solution10\SQL\Query $query |
45 | */ |
46 | protected $query = null; |
47 | |
48 | /** |
49 | * @var String $query |
50 | */ |
51 | protected $prefix = ''; |
52 | |
53 | /** |
54 | * Name of the query used for caching |
55 | * |
56 | */ |
57 | protected $name = false; |
58 | |
59 | /** |
60 | * Level given ususally by parameter resolveReferences |
61 | * |
62 | */ |
63 | protected $resolveLevel = null; |
64 | |
65 | protected static $sqlCache = []; |
66 | |
67 | protected $currentSqlString = null; |
68 | |
69 | /** |
70 | * List of joined aliasnames to avoid double joins |
71 | * |
72 | */ |
73 | protected $joinedAliasList = []; |
74 | |
75 | /** |
76 | * List of joined queries to avoid double joins |
77 | * |
78 | */ |
79 | protected $joinedQueryList = []; |
80 | |
81 | /** |
82 | * Create query builder if necessary |
83 | * |
84 | * @param Mixed $queryType one of the constants for a query type or of instance \Solution10\SQL\Query |
85 | * @param String $prefix If used in a subquery, prefix results with this string |
86 | * @param String $name A named query has a cached SQL as soon as called first |
87 | */ |
88 | public function __construct($queryType, $prefix = '', $name = false, $resolveLevel = null) |
89 | { |
90 | $this->prefix = $prefix; |
91 | $this->name = $name; |
92 | $this->setResolveLevel($resolveLevel); |
93 | $dialect = new MySQL(); |
94 | if (self::SELECT === $queryType) { |
95 | $this->query = new Select($dialect); |
96 | $this->addSelect(); |
97 | } elseif (self::INSERT === $queryType) { |
98 | $this->query = new Insert($dialect); |
99 | $this->addTable(); |
100 | } elseif (self::UPDATE === $queryType) { |
101 | $this->query = new Update($dialect); |
102 | $this->addTableAlias(); |
103 | } elseif (self::REPLACE === $queryType) { |
104 | $this->query = new INSERT($dialect); |
105 | $this->query->queryBaseStatement('REPLACE INTO'); |
106 | $this->addTable(); |
107 | } elseif (self::DELETE === $queryType) { |
108 | $this->query = new Delete($dialect); |
109 | $this->query->queryBaseStatement('DELETE ' . $this::getAlias() . ' FROM'); |
110 | $this->addTableAlias(); |
111 | } elseif ($queryType instanceof self) { |
112 | $this->query = $queryType->query; |
113 | $this->joinedAliasList =& $queryType->joinedAliasList; |
114 | $this->resolveLevel = $queryType->resolveLevel - 1; |
115 | } elseif ($queryType instanceof \Solution10\SQL\Query) { |
116 | $this->query = $queryType; |
117 | } |
118 | if ($this->query instanceof Select) { |
119 | $this->addRequiredJoins(); |
120 | } |
121 | } |
122 | |
123 | /** |
124 | * @codeCoverageIgnore |
125 | */ |
126 | public function __toString() |
127 | { |
128 | if ($this->name) { |
129 | $name = $this->name . '_' . $this->prefix . (string) $this->resolveLevel; |
130 | if (!isset(static::$sqlCache[$name])) { |
131 | static::$sqlCache[$name] = $this->getSql(); |
132 | } |
133 | return static::$sqlCache[$name]; |
134 | } |
135 | if ($this->currentSqlString) { |
136 | $sql = $this->currentSqlString; |
137 | } else { |
138 | $sql = $this->getSql(); |
139 | } |
140 | return $sql; |
141 | } |
142 | |
143 | public function getName() |
144 | { |
145 | return $this->name ? $this->name : get_class($this); |
146 | } |
147 | |
148 | /** |
149 | * Add the from part to the queryBaseStatement |
150 | * This implementation tries to guess the syntax using the constant TABLE in the class |
151 | * Override the method for a special implementation or required joins |
152 | * |
153 | * @return self |
154 | */ |
155 | protected function addSelect() |
156 | { |
157 | $table = $this::getTablename(); |
158 | $alias = $this::getAlias(); |
159 | $this->query->from($table, $alias); |
160 | return $this; |
161 | } |
162 | |
163 | public function setDistinctSelect() |
164 | { |
165 | $this->query->queryBaseStatement('SELECT DISTINCT'); |
166 | } |
167 | |
168 | public function setResolveLevel($resolveLevel) |
169 | { |
170 | if ($resolveLevel !== null) { |
171 | $this->resolveLevel = $resolveLevel; |
172 | } |
173 | return $this; |
174 | } |
175 | |
176 | public function getResolveLevel() |
177 | { |
178 | if (null === $this->resolveLevel) { |
179 | throw new \Exception("Required setting for resolveReferenceLevel missing in " . get_class($this)); |
180 | } |
181 | return $this->resolveLevel; |
182 | } |
183 | |
184 | /** |
185 | * Add the alias part to the queryBaseStatement |
186 | * This implementation tries to guess the syntax using the constant TABLE in the class |
187 | * Override the method for a special implementation or required joins |
188 | * |
189 | * @return self |
190 | */ |
191 | public static function getAlias() |
192 | { |
193 | $class = get_called_class(); |
194 | $alias = constant($class . '::ALIAS'); |
195 | if (null === $alias) { |
196 | $alias = lcfirst(preg_replace('#^.*\\\#', '', $class)); |
197 | } |
198 | return $alias; |
199 | } |
200 | |
201 | /** |
202 | * Get the table name for the query |
203 | * |
204 | * @return string |
205 | */ |
206 | public static function getTablename() |
207 | { |
208 | $class = get_called_class(); |
209 | $table = constant($class . '::TABLE'); |
210 | return $table; |
211 | } |
212 | |
213 | /** |
214 | * Add the from part to the queryBaseStatement |
215 | * This implementation tries to guess the syntax using the constant TABLE in the class |
216 | * Override the method for a special implementation or required joins |
217 | * |
218 | * @return self |
219 | */ |
220 | protected function addTable() |
221 | { |
222 | $table = $this::getTablename(); |
223 | $alias = $this::getAlias(); |
224 | $this->query->table($table, $alias); |
225 | return $this; |
226 | } |
227 | |
228 | /** |
229 | * Add the from part to the queryBaseStatement |
230 | * This implementation tries to guess the syntax using the constant TABLE in the class |
231 | * Override the method for a special implementation or required joins |
232 | * |
233 | * @return self |
234 | */ |
235 | protected function addTableAlias() |
236 | { |
237 | $table = $this::getTablename(); |
238 | $alias = $this::getAlias(); |
239 | $this->query->table(self::expression($table . ' ' . $alias)); |
240 | return $this; |
241 | } |
242 | |
243 | /** |
244 | * Add joins to table if required |
245 | * Override this method if join are required for a select |
246 | */ |
247 | protected function addRequiredJoins() |
248 | { |
249 | } |
250 | |
251 | /** |
252 | * resolves references by joining tables defined in the method addJoin() |
253 | * |
254 | * @param Int $depth Number of levels of sub references to resolve |
255 | * @return self |
256 | */ |
257 | public function addResolvedReferences($depth) |
258 | { |
259 | $this->setResolveLevel($depth); |
260 | if ($depth > 0) { |
261 | $queryList = $this->addJoin(); |
262 | foreach ($queryList as $query) { |
263 | $query->setResolveLevel($depth); |
264 | $query->addResolvedReferences($depth - 1); |
265 | $query->addEntityMapping(); |
266 | } |
267 | $this->joinedQueryList = $queryList; |
268 | } else { |
269 | $this->addReferenceMapping(); |
270 | } |
271 | return $this; |
272 | } |
273 | |
274 | /** |
275 | * If resolveReferences is required, override this method |
276 | * |
277 | * @return Array of self |
278 | */ |
279 | protected function addJoin() |
280 | { |
281 | return []; |
282 | } |
283 | |
284 | protected function leftJoin($alias, $left = null, $operator = null, $right = null) |
285 | { |
286 | $aliasId = $alias->getAliasIdentifier(); |
287 | //error_log(get_class($this) . " JOIN $aliasId CHECK " . implode(',', $this->joinedAliasList)); |
288 | if (!in_array($aliasId, $this->joinedAliasList)) { |
289 | $this->joinedAliasList[] = $aliasId; |
290 | $this->query->leftJoin($alias, $left, $operator, $right); |
291 | } else { |
292 | //throw new \Exception("Tried to add Alias ".$aliasId); |
293 | } |
294 | return $this->query; |
295 | } |
296 | |
297 | /** |
298 | * get SQL-String |
299 | * Implement a simple caching routine to prevent multiple rebuilds |
300 | * |
301 | * @return String |
302 | */ |
303 | public function getSql() |
304 | { |
305 | $this->currentSqlString = (string)$this->query; |
306 | return $this->currentSqlString; |
307 | } |
308 | |
309 | /** |
310 | * List of parameters to use for a prepared statement |
311 | * |
312 | * @return Array |
313 | */ |
314 | public function getParameters() |
315 | { |
316 | return $this->query->params(); |
317 | } |
318 | |
319 | public function getReferenceMapping() |
320 | { |
321 | return [ |
322 | ]; |
323 | } |
324 | |
325 | /** |
326 | * Shortcut to create an SQL-Expression without quoting |
327 | * |
328 | * @return \Solution10\SQL\Expression |
329 | */ |
330 | protected static function expression($string) |
331 | { |
332 | return new Expression($string); |
333 | } |
334 | |
335 | /** |
336 | * Add a select part to the query containing a mapping from the db schema to the entity schema |
337 | * |
338 | * @return self |
339 | */ |
340 | public function addEntityMapping($type = null) |
341 | { |
342 | $entityMapping = $this->getPrefixedList($this->getEntityMapping($type)); |
343 | $this->query->select($entityMapping); |
344 | return $this; |
345 | } |
346 | |
347 | protected function getPrefixed($prefix) |
348 | { |
349 | return $this->prefix . $prefix; |
350 | } |
351 | |
352 | protected function getPrefixedList($unprefixedList) |
353 | { |
354 | $prefixed = []; |
355 | foreach ($unprefixedList as $key => $value) { |
356 | $prefixed[$this->getPrefixed($key)] = $value; |
357 | } |
358 | return $prefixed; |
359 | } |
360 | |
361 | /** |
362 | * Add a select part to the query containing references if no resolveReferences is given |
363 | * |
364 | * @return self |
365 | */ |
366 | protected function addReferenceMapping() |
367 | { |
368 | $referenceMapping = $this->getPrefixedList($this->getReferenceMapping()); |
369 | $this->query->select($referenceMapping); |
370 | return $this; |
371 | } |
372 | |
373 | public function addLimit($count, $offset = null) |
374 | { |
375 | $this->query->limit($count); |
376 | if ($offset) { |
377 | $this->query->offset($offset); |
378 | } |
379 | return $this; |
380 | } |
381 | |
382 | /** |
383 | * Add values to a insert or update query |
384 | * |
385 | * @return self |
386 | */ |
387 | public function addValues($values) |
388 | { |
389 | $this->query->values($values); |
390 | return $this; |
391 | } |
392 | |
393 | /** |
394 | * postProcess data if necessary |
395 | * |
396 | */ |
397 | public function postProcess($data) |
398 | { |
399 | return $data; |
400 | } |
401 | |
402 | /** |
403 | * postProcess data including joined queries if necessary |
404 | * |
405 | */ |
406 | public function postProcessJoins($data) |
407 | { |
408 | $data = $this->postProcess($data); |
409 | foreach ($this->joinedQueryList as $query) { |
410 | $data = $query->postProcess($data); |
411 | } |
412 | return $data; |
413 | } |
414 | } |