Completed
Push — master ( 287087...f3c7b7 )
by Mikael
01:46
created

QueryBuilderTrait::update()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 35
Code Lines 23

Duplication

Lines 13
Ratio 37.14 %

Code Coverage

Tests 24
CRAP Score 5

Importance

Changes 0
Metric Value
dl 13
loc 35
ccs 24
cts 24
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 23
nc 5
nop 3
crap 5
1
<?php
2
3
namespace Anax\Database;
4
5
use \Anax\Database\Exception\BuildException;
6
7
/**
8
 * Build SQL queries by method calling.
9
 */
10
trait QueryBuilderTrait
11
{
12
    /**
13
     * @var $dialect current database dialect used
14
     * @var $sql     the query built
15
     * @var $prefix  prefix to attach to all table names
16
     */
17
    private $dialect;
18
    private $sql;
19
    private $prefix;
20
21
    /**
22
     * @var $start   first line of the sql query
23
     * @var $from    from part
24
     * @var $join    join part
25
     * @var $set     set part for a update
26
     * @var $where   where part
27
     * @var $groupby group part
28
     * @var $orderby order part
29
     * @var $limit   limit part
30
     * @var $offset  offset part
31
     */
32
    private $start;
33
    private $from;
34
    private $join;
35
    private $set;
36
    private $where;
37
    private $groupby;
38
    private $orderby;
39
    private $limit;
40
    private $offset;
41
42
43
44
    /**
45
     * Get SQL.
46
     *
47
     * @return string with the built sql-query
48
     */
49 24
    public function getSQL()
50
    {
51 24
        if ($this->sql) {
52 13
            return $this->sql;
53
        }
54 16
        return $this->build();
55
    }
56
57
58
59
    /**
60
     * Build the SQL query from its parts.
61
     *
62
     * @return string as SQL query
63
     */
64 16
    protected function build()
65
    {
66 16
        $sql = $this->start . "\n"
67 16
            . ($this->from    ? $this->from . "\n"    : null)
68 16
            . ($this->join    ? $this->join           : null)
69 16
            . ($this->set     ? $this->set . "\n"     : null)
70 16
            . ($this->where   ? $this->where . "\n"   : null)
71 16
            . ($this->groupby ? $this->groupby . "\n" : null)
72 16
            . ($this->orderby ? $this->orderby . "\n" : null)
73 16
            . ($this->limit   ? $this->limit . "\n"   : null)
74 16
            . ($this->offset  ? $this->offset . "\n"  : null)
75 16
            . ";";
76
77 16
        return $sql;
78
    }
79
80
81
82
    /**
83
     * Clear all previous sql-code.
84
     *
85
     * @return void
86
     */
87 17
    protected function clear()
88
    {
89 17
        $this->sql      = null;
90 17
        $this->start    = null;
91 17
        $this->from     = null;
92 17
        $this->join     = null;
93 17
        $this->set      = null;
94 17
        $this->where    = null;
95 17
        $this->groupby  = null;
96 17
        $this->orderby  = null;
97 17
        $this->limit    = null;
98 17
        $this->offset   = null;
99 17
    }
100
101
102
103
    /**
104
     * Set database type/dialect to consider when generating SQL.
105
     *
106
     * @param string $dialect representing database type.
107
     *
108
     * @return self
109
     */
110
    public function setSQLDialect($dialect)
111
    {
112
        $this->dialect = $dialect;
113
        return $this;
114
    }
115
116
117
118
    /**
119
     * Set a table prefix.
120
     *
121
     * @param string $prefix to use in front of all tables.
122
     *
123
     * @return self
124
     */
125 19
    public function setTablePrefix($prefix)
126
    {
127 19
        $this->prefix = $prefix;
128 19
        return $this;
129
    }
130
131
132
133
    /**
134
     * Create a table.
135
     *
136
     * @param string $name    the table name.
137
     * @param array  $columns the columns in the table.
138
     *
139
     * @return $this
140
     */
141 2
    public function createTable($name, $columns)
142
    {
143 2
        $cols = null;
144
145 2
        foreach ($columns as $col => $options) {
146 2
            $cols .= "\t" . $col . ' ' . implode(' ', $options) . ",\n";
147 2
        }
148 2
        $cols = substr($cols, 0, -2);
149
150 2
        $this->sql = "CREATE TABLE "
151 2
            . $this->prefix
152 2
            . $name
153 2
            . "\n(\n"
154 2
            . $cols
155 2
            . "\n);\n";
156
157 2
        if ($this->dialect == 'sqlite') {
158 1
            $this->sql = str_replace('auto_increment', '', $this->sql);
159 1
        }
160
161 2
        return $this;
162
    }
163
164
165
166
    /**
167
     * Drop a table.
168
     *
169
     * @param string $name the table name.
170
     *
171
     * @return $this
172
     */
173 1
    public function dropTable($name)
174
    {
175 1
        $this->sql = "DROP TABLE "
176 1
            . $this->prefix
177 1
            . $name
178 1
            . ";\n";
179
180 1
        return $this;
181
    }
182
183
184
185
    /**
186
     * Drop a table if it exists.
187
     *
188
     * @param string $name the table name.
189
     *
190
     * @return $this
191
     */
192 1
    public function dropTableIfExists($name)
193
    {
194 1
        $this->sql = "DROP TABLE IF EXISTS "
195 1
            . $this->prefix
196 1
            . $name
197 1
            . ";\n";
198
199 1
        return $this;
200
    }
201
202
203
204
    /**
205
     * Build a insert-query.
206
     *
207
     * @param string $table   the table name.
208
     * @param array  $columns to insert och key=>value with columns and values.
209
     * @param array  $values  to insert or empty if $columns has both
210
     *                        columns and values.
211
     *
212
     * @throws \Anax\Database\BuildException
213
     *
214
     * @return self for chaining
215
     */
216 10
    public function insert($table, $columns, $values = null)
217
    {
218 10
        list($columns, $values) = $this->mapColumnsWithValues($columns, $values);
219
220 10
        if (count($columns) !== count($values)) {
221 1
            throw new BuildException("Columns does not match values, not equal items.");
222
        }
223
224 9
        $cols = null;
225 9
        $vals = null;
226
227 9
        $max = count($columns);
228 9 View Code Duplication
        for ($i = 0; $i < $max; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229 9
            $cols .= $columns[$i] . ', ';
230
231 9
            $val = $values[$i];
232
233 9
            if ($val == '?') {
234 6
                $vals .= $val . ', ';
235 6
            } else {
236 3
                $vals .= (is_string($val)
237 3
                    ? "'$val'"
238 3
                    : $val)
239 3
                    . ', ';
240
            }
241 9
        }
242
243 9
        $cols = substr($cols, 0, -2);
244 9
        $vals = substr($vals, 0, -2);
245
246 9
        $this->sql = "INSERT INTO "
247 9
            . $this->prefix
248 9
            . $table
249 9
            . "\n\t("
250 9
            . $cols
251 9
            . ")\n"
252 9
            . "\tVALUES\n\t("
253 9
            . $vals
254 9
            . ");\n";
255
256 9
        return $this;
257
    }
258
259
260
261
    /**
262
     * Build an update-query.
263
     *
264
     * @param string $table   the table name.
265
     * @param array  $columns to update or key=>value with columns and values.
266
     * @param array  $values  to update or empty if $columns has bot columns and values.
267
     *
268
     * @throws \Anax\Database\BuildException
269
     *
270
     * @return void
271
     */
272 4
    public function update($table, $columns, $values = null)
273
    {
274 4
        $this->clear();
275 4
        list($columns, $values) = $this->mapColumnsWithValues($columns, $values);
276
277 4
        if (count($columns) != count($values)) {
278 1
            throw new BuildException("Columns does not match values, not equal items.");
279
        }
280
281 3
        $cols = null;
282 3
        $max = count($columns);
283
        
284 3 View Code Duplication
        for ($i = 0; $i < $max; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
285 3
            $cols .= "\t" . $columns[$i] . ' = ';
286
287 3
            $val = $values[$i];
288 3
            if ($val == '?') {
289 2
                $cols .= $val . ",\n";
290 2
            } else {
291 1
                $cols .= (is_string($val)
292 1
                    ? "'$val'"
293 1
                    : $val)
294 1
                    . ",\n";
295
            }
296 3
        }
297
298 3
        $cols = substr($cols, 0, -2);
299
300 3
        $this->start = "UPDATE "
301 3
            . $this->prefix
302 3
            . $table;
303 3
        $this->set = "SET\n$cols";
304
305 3
        return $this;
306
    }
307
308
309
310
    /**
311
     * Build a delete-query.
312
     *
313
     * @param string $table the table name.
314
     * @param array  $where limit which rows are updated.
315
     *
316
     * @return self
317
     */
318 3
    public function deleteFrom($table, $where = null)
319
    {
320 3
        $this->clear();
321
322 3
        if (isset($where)) {
323 1
            $this->where = "WHERE\n\t(" . $where . ")";
324 1
        }
325
326 3
        $this->start = "DELETE";
327 3
        $this->from($table);
328 3
        return $this;
329
    }
330
331
332
333
    /**
334
     * Build a select-query.
335
     *
336
     * @param string $columns which columns to select.
337
     *
338
     * @return $this
339
     */
340 13
    public function select($columns = '*')
341
    {
342 13
        $this->clear();
343 13
        $this->start = "SELECT\n\t$columns";
344 13
        return $this;
345
    }
346
347
348
349
    /**
350
     * Build the from part.
351
     *
352
     * @param string $table name of table.
353
     *
354
     * @return $this
355
     */
356 15
    public function from($table)
357
    {
358 15
        $this->from = "FROM " . $this->prefix . $table;
359 15
        return $this;
360
    }
361
362
363
364
    /**
365
     * Build the inner join part.
366
     *
367
     * @param string $table     name of table.
368
     * @param string $condition to join.
369
     *
370
     * @return $this
371
     */
372 1
    public function join($table, $condition)
373
    {
374
375 1
        return $this->createJoin($table, $condition, 'INNER');
376
    }
377
378
379
380
    /**
381
     * Build the right join part.
382
     *
383
     * @param string $table     name of table.
384
     * @param string $condition to join.
385
     *
386
     * @throws \Anax\Database\BuildException when dialect does not support.
387
     *
388
     * @return $this
389
     */
390 1
    public function rightJoin($table, $condition)
391
    {
392 1
        if ($this->dialect == 'sqlite') {
393
            throw new BuildException("SQLite does not support RIGHT JOIN");
394
        }
395
396 1
        return $this->createJoin($table, $condition, 'RIGHT OUTER');
397
    }
398
399
400
401
    /**
402
     * Build the left join part.
403
     *
404
     * @param string $table     name of table.
405
     * @param string $condition to join.
406
     *
407
     * @return $this
408
     */
409 1
    public function leftJoin($table, $condition)
410
    {
411 1
        return $this->createJoin($table, $condition, 'LEFT OUTER');
412
    }
413
414
415
416
    /**
417
     * Create a inner or outer join.
418
     *
419
     * @param string $table     name of table.
420
     * @param string $condition to join.
421
     * @param string $type      what type of join to create.
422
     *
423
     * @return void
424
     */
425 3
    private function createJoin($table, $condition, $type)
426
    {
427 3
        $this->join .= $type
428 3
            . " JOIN " . $this->prefix . $table
429 3
            . "\n\tON " . $condition . "\n";
430
431 3
        return $this;
432
    }
433
434
435
436
    /**
437
     * Build the where part.
438
     *
439
     * @param string $condition for building the where part of the query.
440
     *
441
     * @return $this
442
     */
443 7
    public function where($condition)
444
    {
445 7
        $this->where = "WHERE\n\t(" . $condition . ")";
446
447 7
        return $this;
448
    }
449
450
451
452
    /**
453
     * Build the where part with conditions.
454
     *
455
     * @param string $condition for building the where part of the query.
456
     *
457
     * @return $this
458
     */
459 1
    public function andWhere($condition)
460
    {
461 1
        $this->where .= "\n\tAND (" . $condition . ")";
462
463 1
        return $this;
464
    }
465
466
467
468
    /**
469
    * Build the group by part.
470
    *
471
    * @param string $condition for building the group by part of the query.
472
    *
473
    * @return $this
474
    */
475 1
    public function groupBy($condition)
476
    {
477 1
        $this->groupby = "GROUP BY " . $condition;
478
479 1
        return $this;
480
    }
481
482
483
484
    /**
485
    * Build the order by part.
486
    *
487
    * @param string $condition for building the where part of the query.
488
    *
489
    * @return $this
490
    */
491 1
    public function orderBy($condition)
492
    {
493 1
        $this->orderby = "ORDER BY " . $condition;
494
495 1
        return $this;
496
    }
497
498
499
500
    /**
501
     * Build the LIMIT by part.
502
     *
503
     * @param string $condition for building the LIMIT part of the query.
504
     *
505
     * @return $this
506
     */
507 1
    public function limit($condition)
508
    {
509 1
        $this->limit = "LIMIT \n\t" . intval($condition);
510
511 1
        return $this;
512
    }
513
514
515
516
    /**
517
     * Build the OFFSET by part.
518
     *
519
     * @param string $condition for building the OFFSET part of the query.
520
     *
521
     * @return $this
522
     */
523 1
    public function offset($condition)
524
    {
525 1
        $this->offset = "OFFSET \n\t" . intval($condition);
526
527 1
        return $this;
528
    }
529
530
531
532
    /**
533
     * Create a proper column value arrays from incoming $columns and $values.
534
     *
535
     * @param array       $columns
536
     * @param array|null  $values
537
     *
538
     * @return array that can be parsed with list($columns, $values)
539
     */
540 12
    public function mapColumnsWithValues($columns, $values)
541
    {
542
        // If $values is null, then use $columns to build it up
543 12
        if (is_null($values)) {
544
545 8
            if ($this->isAssoc($columns)) {
546
547
                // Incoming is associative array, split it up in two
548 2
                $values = array_values($columns);
549 2
                $columns = array_keys($columns);
550
551 2
            } else {
552
553
                // Create an array of '?' to match number of columns
554 6
                $max = count($columns);
555 6
                for ($i = 0; $i < $max; $i++) {
556 6
                    $values[] = '?';
557 6
                }
558
            }
559 8
        }
560
561 12
        return [$columns, $values];
562
    }
563
564
565
566
    /**
567
     * Utility to check if array is associative array.
568
     *
569
     * http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential/4254008#4254008
570
     *
571
     * @param array $array input array to check.
572
     *
573
     * @return boolean true if array is associative array with at least
574
     *                      one key, else false.
575
     *
576
     */
577 8
    private function isAssoc($array)
578
    {
579 8
        return (bool) count(array_filter(array_keys($array), 'is_string'));
580
    }
581
}
582