QueryBuilderTrait::isAssoc()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Anax\DatabaseQueryBuilder;
4
5
use Anax\DatabaseQueryBuilder\Exception\BuildException;
6
7
/**
8
 * Trait to implement building SQL queries by method calling.
9
 */
10
trait QueryBuilderTrait
11
{
12
    /**
13
     * @var $dialect current database dialect used
0 ignored issues
show
Documentation Bug introduced by
The doc comment $dialect at position 0 could not be parsed: Unknown type name '$dialect' at position 0 in $dialect.
Loading history...
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
0 ignored issues
show
Documentation Bug introduced by
The doc comment $start at position 0 could not be parsed: Unknown type name '$start' at position 0 in $start.
Loading history...
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 18
    public function getSQL()
50
    {
51 18
        if ($this->sql) {
52 8
            return $this->sql;
53
        }
54 12
        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 12
    protected function build()
65
    {
66 12
        $sql = $this->start . "\n"
67 12
            . ($this->from    ? $this->from . "\n"    : null)
68 12
            . ($this->join    ? $this->join           : null)
69 12
            . ($this->set     ? $this->set . "\n"     : null)
70 12
            . ($this->where   ? $this->where . "\n"   : null)
71 12
            . ($this->groupby ? $this->groupby . "\n" : null)
72 12
            . ($this->orderby ? $this->orderby . "\n" : null)
73 12
            . ($this->limit   ? $this->limit . "\n"   : null)
74 12
            . ($this->offset  ? $this->offset . "\n"  : null)
75 12
            . ";";
76
77 12
        return $sql;
78
    }
79
80
81
82
    /**
83
     * Clear all previous sql-code.
84
     *
85
     * @return void
86
     */
87 13
    protected function clear()
88
    {
89 13
        $this->sql      = null;
90 13
        $this->start    = null;
91 13
        $this->from     = null;
92 13
        $this->join     = null;
93 13
        $this->set      = null;
94 13
        $this->where    = null;
95 13
        $this->groupby  = null;
96 13
        $this->orderby  = null;
97 13
        $this->limit    = null;
98 13
        $this->offset   = null;
99 13
    }
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 2
    public function setSQLDialect($dialect)
111
    {
112 2
        $this->dialect = $dialect;
113 2
        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 20
    public function setTablePrefix($prefix)
126
    {
127 20
        $this->prefix = $prefix;
128 20
        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 3
    public function createTable($name, $columns)
142
    {
143 3
        $cols = null;
144
145 3
        foreach ($columns as $col => $options) {
146 3
            $cols .= "\t" . $col . ' ' . implode(' ', $options) . ",\n";
147
        }
148 3
        $cols = substr($cols, 0, -2);
149
150 3
        $this->sql = "CREATE TABLE "
151 3
            . $this->prefix
152 3
            . $name
153 3
            . "\n(\n"
154 3
            . $cols
155 3
            . "\n);\n";
156
157 3
        if ($this->dialect == 'sqlite') {
158 2
            $this->sql = str_replace('auto_increment', '', $this->sql);
159
        }
160
161 3
        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 6
    public function insert($table, $columns, $values = null)
217
    {
218 6
        list($columns, $values) = $this->mapColumnsWithValues($columns, $values);
219
220 6
        if (count($columns) !== count($values)) {
221 1
            throw new BuildException("Columns does not match values, not equal items.");
222
        }
223
224 5
        $cols = null;
225 5
        $vals = null;
226
227 5
        $max = count($columns);
228 5
        for ($i = 0; $i < $max; $i++) {
229 5
            $cols .= $columns[$i] . ', ';
230
231 5
            $val = $values[$i];
232
233 5
            if ($val == '?') {
234 1
                $vals .= $val . ', ';
235
            } else {
236 4
                $vals .= (is_string($val)
237 4
                    ? "'$val'"
238 4
                    : $val)
239 4
                    . ', ';
240
            }
241
        }
242
243 5
        $cols = substr($cols, 0, -2);
244 5
        $vals = substr($vals, 0, -2);
245
246 5
        $this->sql = "INSERT INTO "
247 5
            . $this->prefix
248 5
            . $table
249 5
            . "\n\t("
250 5
            . $cols
251 5
            . ")\n"
252 5
            . "\tVALUES\n\t("
253 5
            . $vals
254 5
            . ");\n";
255
256 5
        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 2
    public function update($table, $columns, $values = null)
273
    {
274 2
        $this->clear();
275 2
        list($columns, $values) = $this->mapColumnsWithValues($columns, $values);
276
277 2
        if (count($columns) != count($values)) {
278 1
            throw new BuildException("Columns does not match values, not equal items.");
279
        }
280
281 1
        $cols = null;
282 1
        $max = count($columns);
283
        
284 1
        for ($i = 0; $i < $max; $i++) {
285 1
            $cols .= "\t" . $columns[$i] . ' = ';
286
287 1
            $val = $values[$i];
288 1
            if ($val == '?') {
289
                $cols .= $val . ",\n";
290
            } else {
291 1
                $cols .= (is_string($val)
292 1
                    ? "'$val'"
293 1
                    : $val)
294 1
                    . ",\n";
295
            }
296
        }
297
298 1
        $cols = substr($cols, 0, -2);
299
300 1
        $this->start = "UPDATE "
301 1
            . $this->prefix
302 1
            . $table;
303 1
        $this->set = "SET\n$cols";
304
305 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Anax\DatabaseQueryBuilder\QueryBuilderTrait which is incompatible with the documented return type void.
Loading history...
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 2
    public function deleteFrom($table, $where = null)
319
    {
320 2
        $this->clear();
321
322 2
        if (isset($where)) {
323 1
            $this->where = "WHERE\n\t(" . $where . ")";
0 ignored issues
show
Bug introduced by
Are you sure $where of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

323
            $this->where = "WHERE\n\t(" . /** @scrutinizer ignore-type */ $where . ")";
Loading history...
324
        }
325
326 2
        $this->start = "DELETE";
327 2
        $this->from($table);
328 2
        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 9
    public function select($columns = '*')
341
    {
342 9
        $this->clear();
343 9
        $this->start = "SELECT\n\t$columns";
344 9
        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 11
    public function from($table)
357
    {
358 11
        $this->from = "FROM " . $this->prefix . $table;
359 11
        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');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createJoin...e, $condition, 'INNER') returns the type void which is incompatible with the documented return type Anax\DatabaseQueryBuilder\QueryBuilderTrait.
Loading history...
Bug introduced by
Are you sure the usage of $this->createJoin($table, $condition, 'INNER') targeting Anax\DatabaseQueryBuilde...lderTrait::createJoin() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->createJoin($table...ndition, 'RIGHT OUTER') targeting Anax\DatabaseQueryBuilde...lderTrait::createJoin() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug Best Practice introduced by
The expression return $this->createJoin...ndition, 'RIGHT OUTER') returns the type void which is incompatible with the documented return type Anax\DatabaseQueryBuilder\QueryBuilderTrait.
Loading history...
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');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createJoin...ondition, 'LEFT OUTER') returns the type void which is incompatible with the documented return type Anax\DatabaseQueryBuilder\QueryBuilderTrait.
Loading history...
Bug introduced by
Are you sure the usage of $this->createJoin($table...ondition, 'LEFT OUTER') targeting Anax\DatabaseQueryBuilde...lderTrait::createJoin() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Anax\DatabaseQueryBuilder\QueryBuilderTrait which is incompatible with the documented return type void.
Loading history...
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 4
    public function where($condition)
444
    {
445 4
        $this->where = "WHERE\n\t(" . $condition . ")";
446
447 4
        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 8
    public function mapColumnsWithValues($columns, $values)
541
    {
542
        // If $values is null, then use $columns to build it up
543 8
        if (is_null($values)) {
544 4
            if ($this->isAssoc($columns)) {
545
                // Incoming is associative array, split it up in two
546 3
                $values = array_values($columns);
547 3
                $columns = array_keys($columns);
548
            } else {
549
                // Create an array of '?' to match number of columns
550 1
                $max = count($columns);
551 1
                for ($i = 0; $i < $max; $i++) {
552 1
                    $values[] = '?';
553
                }
554
            }
555
        }
556
557 8
        return [$columns, $values];
558
    }
559
560
561
562
    /**
563
     * Utility to check if array is associative array.
564
     *
565
     * http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential/4254008#4254008
566
     *
567
     * @param array $array input array to check.
568
     *
569
     * @return boolean true if array is associative array with at least
570
     *                      one key, else false.
571
     *
572
     */
573 4
    private function isAssoc($array)
574
    {
575 4
        return (bool) count(array_filter(array_keys($array), 'is_string'));
576
    }
577
}
578