Completed
Pull Request — 2.x (#94)
by Gauthier
02:15
created

Select::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1
Metric Value
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 9.4285
cc 1
eloc 11
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\SqlQuery\Common;
10
11
use Aura\SqlQuery\AbstractQuery;
12
use Aura\SqlQuery\Exception;
13
14
/**
15
 *
16
 * An object for SELECT queries.
17
 *
18
 * @package Aura.SqlQuery
19
 *
20
 */
21
class Select extends AbstractQuery implements SelectInterface, SubselectInterface
22
{
23
    /**
24
     *
25
     * An array of union SELECT statements.
26
     *
27
     * @var array
28
     *
29
     */
30
    protected $union = array();
31
32
    /**
33
     *
34
     * Is this a SELECT FOR UPDATE?
35
     *
36
     * @var
37
     *
38
     */
39
    protected $for_update = false;
40
41
    /**
42
     *
43
     * The columns to be selected.
44
     *
45
     * @var array
46
     *
47
     */
48
    protected $cols = array();
49
50
    /**
51
     *
52
     * Select from these tables; includes JOIN clauses.
53
     *
54
     * @var array
55
     *
56
     */
57
    protected $from = array();
58
59
    /**
60
     *
61
     * The current key in the `$from` array.
62
     *
63
     * @var int
64
     *
65
     */
66
    protected $from_key = -1;
67
68
    /**
69
     *
70
     * GROUP BY these columns.
71
     *
72
     * @var array
73
     *
74
     */
75
    protected $group_by = array();
76
77
    /**
78
     *
79
     * The list of HAVING conditions.
80
     *
81
     * @var array
82
     *
83
     */
84
    protected $having = array();
85
86
    /**
87
     *
88
     * The page number to select.
89
     *
90
     * @var int
91
     *
92
     */
93
    protected $page = 0;
94
95
    /**
96
     *
97
     * The number of rows per page.
98
     *
99
     * @var int
100
     *
101
     */
102
    protected $paging = 10;
103
104
    /**
105
     *
106
     * Tracks table references to avoid duplicate identifiers.
107
     *
108
     * @var array
109
     *
110
     */
111
    protected $table_refs = array();
112
113
    /**
114
     *
115
     * Returns this query object as an SQL statement string.
116
     *
117
     * @return string An SQL statement string.
118
     *
119
     */
120 174
    public function getStatement()
121
    {
122 174
        $union = '';
123 174
        if ($this->union) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->union of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
124 15
            $union = implode(PHP_EOL, $this->union) . PHP_EOL;
125 15
        }
126 174
        return $union . $this->build();
127
    }
128
129
    /**
130
     *
131
     * Sets the number of rows per page.
132
     *
133
     * @param int $paging The number of rows to page at.
134
     *
135
     * @return $this
136
     *
137
     */
138 10
    public function setPaging($paging)
139
    {
140 10
        $this->paging = (int) $paging;
141 10
        if ($this->page) {
142 5
            $this->setPagingLimitOffset();
143 5
        }
144 10
        return $this;
145
    }
146
147
    /**
148
     *
149
     * Gets the number of rows per page.
150
     *
151
     * @return int The number of rows per page.
152
     *
153
     */
154 10
    public function getPaging()
155
    {
156 10
        return $this->paging;
157
    }
158
159
    /**
160
     *
161
     * Makes the select FOR UPDATE (or not).
162
     *
163
     * @param bool $enable Whether or not the SELECT is FOR UPDATE (default
164
     * true).
165
     *
166
     * @return $this
167
     *
168
     */
169 5
    public function forUpdate($enable = true)
170
    {
171 5
        $this->for_update = (bool) $enable;
172 5
        return $this;
173
    }
174
175
    /**
176
     *
177
     * Makes the select DISTINCT (or not).
178
     *
179
     * @param bool $enable Whether or not the SELECT is DISTINCT (default
180
     * true).
181
     *
182
     * @return $this
183
     *
184
     */
185 16
    public function distinct($enable = true)
186
    {
187 16
        $this->setFlag('DISTINCT', $enable);
188 16
        return $this;
189
    }
190
191
    /**
192
     *
193
     * Adds columns to the query.
194
     *
195
     * Multiple calls to cols() will append to the list of columns, not
196
     * overwrite the previous columns.
197
     *
198
     * @param array $cols The column(s) to add to the query. The elements can be
199
     * any mix of these: `array("col", "col AS alias", "col" => "alias")`
200
     *
201
     * @return $this
202
     *
203
     */
204 214
    public function cols(array $cols)
205
    {
206 214
        foreach ($cols as $key => $val) {
207 214
            $this->addCol($key, $val);
208 214
        }
209 214
        return $this;
210
    }
211
212
    /**
213
     *
214
     * Adds a column and alias to the columns to be selected.
215
     *
216
     * @param mixed $key If an integer, ignored. Otherwise, the column to be
217
     * added.
218
     *
219
     * @param mixed $val If $key was an integer, the column to be added;
220
     * otherwise, the column alias.
221
     *
222
     * @return null
223
     *
224
     */
225 214
    protected function addCol($key, $val)
226
    {
227 214
        if (is_string($key)) {
228
            // [col => alias]
229 25
            $this->cols[$val] = $key;
230 25
        } else {
231 204
            $this->addColWithAlias($val);
232
        }
233 214
    }
234
235
    /**
236
     *
237
     * Adds a column with an alias to the columns to be selected.
238
     *
239
     * @param string $spec The column specification: "col alias",
240
     * "col AS alias", or something else entirely.
241
     *
242
     * @return null
243
     *
244
     */
245 204
    protected function addColWithAlias($spec)
246
    {
247 204
        $parts = explode(' ', $spec);
248 204
        $count = count($parts);
249 204
        if ($count == 2) {
250
            // "col alias"
251 5
            $this->cols[$parts[1]] = $parts[0];
252 204
        } elseif ($count == 3 && strtoupper($parts[1]) == 'AS') {
253
            // "col AS alias"
254 5
            $this->cols[$parts[2]] = $parts[0];
255 5
        } else {
256
            // no recognized alias
257 204
            $this->cols[] = $spec;
258
        }
259 204
    }
260
261
    /**
262
     *
263
     * Remove a column via its alias.
264
     *
265
     * @param string $alias The column to remove
266
     *
267
     * @return null
268
     *
269
     */
270 15
    public function removeCol($alias)
271
    {
272 15
        if (isset($this->cols[$alias])) {
273 5
            unset($this->cols[$alias]);
274
275 5
            return true;
276
        }
277
278 10
        $index = array_search($alias, $this->cols);
279 10
        if ($index !== false) {
280 5
            unset($this->cols[$index]);
281 5
            return true;
282
        }
283
284 5
        return false;
285
    }
286
287
    /**
288
     *
289
     * Does the query have any columns in it?
290
     *
291
     * @return bool
292
     *
293
     */
294 5
    public function hasCols()
295
    {
296 5
        return (bool) $this->cols;
297
    }
298
299
    /**
300
     *
301
     * Returns a list of columns.
302
     *
303
     * @return array
304
     *
305
     */
306 15
    public function getCols()
307
    {
308 15
        return $this->cols;
309
    }
310
311
    /**
312
     *
313
     * Tracks table references.
314
     *
315
     * @param string $type FROM, JOIN, etc.
316
     *
317
     * @param string $spec The table and alias name.
318
     *
319
     * @return null
320
     *
321
     * @throws Exception when the reference has already been used.
322
     *
323
     */
324 139
    protected function addTableRef($type, $spec)
325
    {
326 139
        $name = $spec;
327
328 139
        $pos = strripos($name, ' AS ');
329 139
        if ($pos !== false) {
330 30
            $name = trim(substr($name, $pos + 4));
331 30
        }
332
333 139
        if (isset($this->table_refs[$name])) {
334 25
            $used = $this->table_refs[$name];
335 25
            throw new Exception("Cannot reference '$type $spec' after '$used'");
336
        }
337
338 139
        $this->table_refs[$name] = "$type $spec";
339 139
    }
340
341
    /**
342
     *
343
     * Adds a FROM element to the query; quotes the table name automatically.
344
     *
345
     * @param string $spec The table specification; "foo" or "foo AS bar".
346
     *
347
     * @return $this
348
     *
349
     */
350 129
    public function from($spec)
351
    {
352 129
        $this->addTableRef('FROM', $spec);
353 129
        return $this->addFrom($this->quoter->quoteName($spec));
354
    }
355
356
    /**
357
     *
358
     * Adds a raw unquoted FROM element to the query; useful for adding FROM
359
     * elements that are functions.
360
     *
361
     * @param string $spec The table specification, e.g. "function_name()".
362
     *
363
     * @return $this
364
     *
365
     */
366 5
    public function fromRaw($spec)
367
    {
368 5
        $this->addTableRef('FROM', $spec);
369 5
        return $this->addFrom($spec);
370
    }
371
372
    /**
373
     *
374
     * Adds to the $from property and increments the key count.
375
     *
376
     * @param string $spec The table specification.
377
     *
378
     * @return $this
379
     *
380
     */
381 134
    protected function addFrom($spec)
382
    {
383 134
        $this->from[] = array($spec);
384 134
        $this->from_key ++;
385 134
        return $this;
386
    }
387
388
    /**
389
     *
390
     * Adds an aliased sub-select to the query.
391
     *
392
     * @param string|Select $spec If a Select object, use as the sub-select;
393
     * if a string, the sub-select string.
394
     *
395
     * @param string $name The alias name for the sub-select.
396
     *
397
     * @return $this
398
     *
399
     */
400 15
    public function fromSubSelect($spec, $name)
401
    {
402 15
        $this->addTableRef('FROM (SELECT ...) AS', $name);
403 10
        $spec = $this->subSelect($spec, '        ');
404 10
        $name = $this->quoter->quoteName($name);
405 10
        $this->from[] = array("({$spec}    ) AS $name");
406 10
        $this->from_key ++;
407 10
        return $this;
408
    }
409
410
    /**
411
     *
412
     * Formats a sub-SELECT statement, binding values from a Select object as
413
     * needed.
414
     *
415
     * @param string|SelectInterface $spec A sub-SELECT specification.
416
     *
417
     * @param string $indent Indent each line with this string.
418
     *
419
     * @return string The sub-SELECT string.
420
     *
421
     */
422 20
    protected function subSelect($spec, $indent)
423
    {
424 20
        if ($spec instanceof SelectInterface) {
425 10
            $this->bindValues($spec->getBindValues());
426 10
        }
427
428 20
        return PHP_EOL . $indent
429 20
            . ltrim(preg_replace('/^/m', $indent, (string) $spec))
430 20
            . PHP_EOL;
431
    }
432
433
    /**
434
     *
435
     * Adds a JOIN table and columns to the query.
436
     *
437
     * @param string $join The join type: inner, left, natural, etc.
438
     *
439
     * @param string $spec The table specification; "foo" or "foo AS bar".
440
     *
441
     * @param string $cond Join on this condition.
442
     *
443
     * @param array $bind Values to bind to ?-placeholders in the condition.
444
     *
445
     * @return $this
446
     *
447
     * @throws Exception
448
     *
449
     */
450 40
    public function join($join, $spec, $cond = null, array $bind = array())
451
    {
452 40
        if (! $this->from) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->from of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
453 10
            throw new Exception('Cannot join() without from() first.');
454
        }
455
456 40
        $join = strtoupper(ltrim("$join JOIN"));
457 40
        $this->addTableRef($join, $spec);
458
459 35
        $spec = $this->quoter->quoteName($spec);
460 35
        $cond = $this->fixJoinCondition($cond, $bind);
461 35
        $this->from[$this->from_key][] = rtrim("$join $spec $cond");
462 35
        return $this;
463
    }
464
465
    /**
466
     *
467
     * Fixes a JOIN condition to quote names in the condition and prefix it
468
     * with a condition type ('ON' is the default and 'USING' is recognized).
469
     *
470
     * @param string $cond Join on this condition.
471
     *
472
     * @param array $bind Values to bind to ?-placeholders in the condition.
473
     *
474
     * @return string
475
     *
476
     */
477 45
    protected function fixJoinCondition($cond, array $bind)
478
    {
479 45
        if (! $cond) {
480 15
            return;
481
        }
482
483 45
        $cond = $this->quoter->quoteNamesIn($cond);
484 45
        $cond = $this->rebuildCondAndBindValues($cond, $bind);
485
486 45
        if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') {
487 5
            return $cond;
488
        }
489
490 45
        if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') {
491 5
            return $cond;
492
        }
493
494 40
        return 'ON ' . $cond;
495
    }
496
497
    /**
498
     *
499
     * Adds a INNER JOIN table and columns to the query.
500
     *
501
     * @param string $spec The table specification; "foo" or "foo AS bar".
502
     *
503
     * @param string $cond Join on this condition.
504
     *
505
     * @param array $bind Values to bind to ?-placeholders in the condition.
506
     *
507
     * @return $this
508
     *
509
     * @throws Exception
510
     *
511
     */
512 10
    public function innerJoin($spec, $cond = null, array $bind = array())
513
    {
514 10
        return $this->join('INNER', $spec, $cond, $bind);
515
    }
516
517
    /**
518
     *
519
     * Adds a LEFT JOIN table and columns to the query.
520
     *
521
     * @param string $spec The table specification; "foo" or "foo AS bar".
522
     *
523
     * @param string $cond Join on this condition.
524
     *
525
     * @param array $bind Values to bind to ?-placeholders in the condition.
526
     *
527
     * @return $this
528
     *
529
     * @throws Exception
530
     *
531
     */
532 10
    public function leftJoin($spec, $cond = null, array $bind = array())
533
    {
534 10
        return $this->join('LEFT', $spec, $cond, $bind);
535
    }
536
537
    /**
538
     *
539
     * Adds a JOIN to an aliased subselect and columns to the query.
540
     *
541
     * @param string $join The join type: inner, left, natural, etc.
542
     *
543
     * @param string|Select $spec If a Select
544
     * object, use as the sub-select; if a string, the sub-select
545
     * command string.
546
     *
547
     * @param string $name The alias name for the sub-select.
548
     *
549
     * @param string $cond Join on this condition.
550
     *
551
     * @param array $bind Values to bind to ?-placeholders in the condition.
552
     *
553
     * @return $this
554
     *
555
     * @throws Exception
556
     *
557
     */
558 15
    public function joinSubSelect($join, $spec, $name, $cond = null, array $bind = array())
559
    {
560 15
        if (! $this->from) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->from of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
561 5
            throw new Exception('Cannot join() without from() first.');
562
        }
563
564 15
        $join = strtoupper(ltrim("$join JOIN"));
565 15
        $this->addTableRef("$join (SELECT ...) AS", $name);
566
567 10
        $spec = $this->subSelect($spec, '            ');
568 10
        $name = $this->quoter->quoteName($name);
569 10
        $cond = $this->fixJoinCondition($cond, $bind);
570
571 10
        $text = rtrim("$join ($spec        ) AS $name $cond");
572 10
        $this->from[$this->from_key][] = '        ' . $text ;
573 10
        return $this;
574
    }
575
576
    /**
577
     *
578
     * Adds grouping to the query.
579
     *
580
     * @param array $spec The column(s) to group by.
581
     *
582
     * @return $this
583
     *
584
     */
585 5
    public function groupBy(array $spec)
586
    {
587 5
        foreach ($spec as $col) {
588 5
            $this->group_by[] = $this->quoter->quoteNamesIn($col);
589 5
        }
590 5
        return $this;
591
    }
592
593
    /**
594
     *
595
     * Adds a HAVING condition to the query by AND. If the condition has
596
     * ?-placeholders, additional arguments to the method will be bound to
597
     * those placeholders sequentially.
598
     *
599
     * @param string $cond The HAVING condition.
600
     *
601
     * @return $this
602
     *
603
     */
604 10
    public function having($cond)
605
    {
606 10
        $this->addClauseCondWithBind('having', 'AND', func_get_args());
607 10
        return $this;
608
    }
609
610
    /**
611
     *
612
     * Adds a HAVING condition to the query by AND. If the condition has
613
     * ?-placeholders, additional arguments to the method will be bound to
614
     * those placeholders sequentially.
615
     *
616
     * @param string $cond The HAVING condition.
617
     *
618
     * @return $this
619
     *
620
     * @see having()
621
     *
622
     */
623 5
    public function orHaving($cond)
624
    {
625 5
        $this->addClauseCondWithBind('having', 'OR', func_get_args());
626 5
        return $this;
627
    }
628
629
    /**
630
     *
631
     * Sets the limit and count by page number.
632
     *
633
     * @param int $page Limit results to this page number.
634
     *
635
     * @return $this
636
     *
637
     */
638 10
    public function page($page)
639
    {
640 10
        $this->page = (int) $page;
641 10
        $this->setPagingLimitOffset();
642 10
        return $this;
643
    }
644
645
    /**
646
     *
647
     * Updates the limit and offset values when changing pagination.
648
     *
649
     * @return null
650
     *
651
     */
652 10
    protected function setPagingLimitOffset()
653
    {
654 10
        $this->limit  = 0;
655 10
        $this->offset = 0;
656 10
        if ($this->page) {
657 10
            $this->limit  = $this->paging;
658 10
            $this->offset = $this->paging * ($this->page - 1);
659 10
        }
660 10
    }
661
662
    /**
663
     *
664
     * Returns the page number being selected.
665
     *
666
     * @return int
667
     *
668
     */
669 5
    public function getPage()
670
    {
671 5
        return $this->page;
672
    }
673
674
    /**
675
     *
676
     * Takes the current select properties and retains them, then sets
677
     * UNION for the next set of properties.
678
     *
679
     * @return $this
680
     *
681
     */
682 10
    public function union()
683
    {
684 10
        $this->union[] = $this->build() . PHP_EOL . 'UNION';
685 10
        $this->reset();
686 10
        return $this;
687
    }
688
689
    /**
690
     *
691
     * Takes the current select properties and retains them, then sets
692
     * UNION ALL for the next set of properties.
693
     *
694
     * @return $this
695
     *
696
     */
697 5
    public function unionAll()
698
    {
699 5
        $this->union[] = $this->build() . PHP_EOL . 'UNION ALL';
700 5
        $this->reset();
701 5
        return $this;
702
    }
703
704
    /**
705
     *
706
     * Returns the LIMIT value.
707
     *
708
     * @return int
709
     *
710
     */
711 10
    public function getLimit()
712
    {
713 10
        return $this->limit;
714
    }
715
716
    /**
717
     *
718
     * Returns the OFFSET value.
719
     *
720
     * @return int
721
     *
722
     */
723 10
    public function getOffset()
724
    {
725 10
        return $this->offset;
726
    }
727
728
    /**
729
     *
730
     * Clears the current select properties; generally used after adding a
731
     * union.
732
     *
733
     * @return null
734
     *
735
     */
736 15
    protected function reset()
737
    {
738 15
        $this->resetFlags();
739 15
        $this->cols       = array();
740 15
        $this->from       = array();
741 15
        $this->from_key   = -1;
742 15
        $this->where      = array();
743 15
        $this->group_by   = array();
744 15
        $this->having     = array();
745 15
        $this->order_by   = array();
746 15
        $this->limit      = 0;
747 15
        $this->offset     = 0;
748 15
        $this->page       = 0;
749 15
        $this->for_update = false;
750 15
        $this->table_refs = array();
751 15
    }
752
753
    /**
754
     *
755
     * Builds this query object into a string.
756
     *
757
     * @return string
758
     *
759
     */
760 174
    protected function build()
761
    {
762
        return 'SELECT'
763 174
            . $this->buildFlags()
764 174
            . $this->buildCols()
765 169
            . $this->buildFrom() // includes JOIN
766 169
            . $this->buildWhere()
767 169
            . $this->buildGroupBy()
768 169
            . $this->buildHaving()
769 169
            . $this->buildOrderBy()
770 169
            . $this->buildLimit()
771 169
            . $this->buildForUpdate();
772
    }
773
774
    /**
775
     *
776
     * Builds the columns clause.
777
     *
778
     * @return string
779
     *
780
     * @throws Exception when there are no columns in the SELECT.
781
     *
782
     */
783 174
    protected function buildCols()
784
    {
785 174
        if (! $this->cols) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cols of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
786 5
            throw new Exception('No columns in the SELECT.');
787
        }
788
789 169
        $cols = array();
790 169
        foreach ($this->cols as $key => $val) {
791 169
            if (is_int($key)) {
792 169
                $cols[] = $this->quoter->quoteNamesIn($val);
793 169
            } else {
794 10
                $cols[] = $this->quoter->quoteNamesIn("$val AS $key");
795
            }
796 169
        }
797
798 169
        return $this->indentCsv($cols);
799
    }
800
801
    /**
802
     *
803
     * Builds the FROM clause.
804
     *
805
     * @return string
806
     *
807
     */
808 169
    protected function buildFrom()
809
    {
810 169
        if (! $this->from) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->from of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
811 60
            return ''; // not applicable
812
        }
813
814 109
        $refs = array();
815 109
        foreach ($this->from as $from) {
816 109
            $refs[] = implode(PHP_EOL, $from);
817 109
        }
818 109
        return PHP_EOL . 'FROM' . $this->indentCsv($refs);
819
    }
820
821
    /**
822
     *
823
     * Builds the GROUP BY clause.
824
     *
825
     * @return string
826
     *
827
     */
828 169
    protected function buildGroupBy()
829
    {
830 169
        if (! $this->group_by) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->group_by of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
831 164
            return ''; // not applicable
832
        }
833
834 5
        return PHP_EOL . 'GROUP BY' . $this->indentCsv($this->group_by);
835
    }
836
837
    /**
838
     *
839
     * Builds the HAVING clause.
840
     *
841
     * @return string
842
     *
843
     */
844 169
    protected function buildHaving()
845
    {
846 169
        if (! $this->having) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->having of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
847 154
            return ''; // not applicable
848
        }
849
850 15
        return PHP_EOL . 'HAVING' . $this->indent($this->having);
851
    }
852
853
    /**
854
     *
855
     * Builds the FOR UPDATE clause.
856
     *
857
     * @return string
858
     *
859
     */
860 169
    protected function buildForUpdate()
861
    {
862 169
        if (! $this->for_update) {
863 164
            return ''; // not applicable
864
        }
865
866 5
        return PHP_EOL . 'FOR UPDATE';
867
    }
868
869
    /**
870
     *
871
     * Adds a WHERE condition to the query by AND. If the condition has
872
     * ?-placeholders, additional arguments to the method will be bound to
873
     * those placeholders sequentially.
874
     *
875
     * @param string $cond The WHERE condition.
876
     * @param mixed ...$bind arguments to bind to placeholders
877
     *
878
     * @return $this
879
     *
880
     */
881 30
    public function where($cond)
882
    {
883 30
        $this->addWhere('AND', func_get_args());
884 30
        return $this;
885
    }
886
887
    /**
888
     *
889
     * Adds a WHERE condition to the query by OR. If the condition has
890
     * ?-placeholders, additional arguments to the method will be bound to
891
     * those placeholders sequentially.
892
     *
893
     * @param string $cond The WHERE condition.
894
     * @param mixed ...$bind arguments to bind to placeholders
895
     *
896
     * @return $this
897
     *
898
     * @see where()
899
     *
900
     */
901 5
    public function orWhere($cond)
902
    {
903 5
        $this->addWhere('OR', func_get_args());
904 5
        return $this;
905
    }
906
907
    /**
908
     *
909
     * Sets a limit count on the query.
910
     *
911
     * @param int $limit The number of rows to select.
912
     *
913
     * @return $this
914
     *
915
     */
916 15
    public function limit($limit)
917
    {
918 15
        $this->limit = (int) $limit;
919 15
        if ($this->page) {
920 5
            $this->page = 0;
921 5
            $this->offset = 0;
922 5
        }
923 15
        return $this;
924
    }
925
926
    /**
927
     *
928
     * Sets a limit offset on the query.
929
     *
930
     * @param int $offset Start returning after this many rows.
931
     *
932
     * @return $this
933
     *
934
     */
935 15
    public function offset($offset)
936
    {
937 15
        $this->offset = (int) $offset;
938 15
        if ($this->page) {
939 5
            $this->page = 0;
940 5
            $this->limit = 0;
941 5
        }
942 15
        return $this;
943
    }
944
945
    /**
946
     *
947
     * Adds a column order to the query.
948
     *
949
     * @param array $spec The columns and direction to order by.
950
     *
951
     * @return $this
952
     *
953
     */
954 5
    public function orderBy(array $spec)
955
    {
956 5
        return $this->addOrderBy($spec);
957
    }
958
959
    /**
960
     * Clear some part of the query
961
     *
962
     * @param $part
963
     * @return $this
964
     */
965
    public function clear($part)
966
    {
967
968
        // arrays
969
        if(in_array($part, array('where', 'cols', 'from', 'group_by', 'having', 'union')))
970
        {
971
            $this->$part = array();
972
        }
973
974
        // 0
975
        if(in_array($part, array('limit', 'offset')))
976
        {
977
            $this->$part = 0;
978
        }
979
980
        return $this;
981
    }
982
}
983