Completed
Push — master ( aa7333...2621dc )
by Konstantinos
06:40
created

QueryBuilder::notEquals()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * This file contains a class to quickly generate database queries for models
4
 *
5
 * @package    BZiON\Models\QueryBuilder
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * This class can be used to search for models with specific characteristics in
11
 * the database.
12
 *
13
 * Note that most methods of this class return itself, so that you can easily
14
 * add a number of different filters.
15
 *
16
 * <code>
17
 *     return Team::getQueryBuilder()
18
 *     ->active()
19
 *     ->where('name')->startsWith('a')
20
 *     ->sortBy('name')->reverse()
21
 *     ->getModels();
22
 * </code>
23
 *
24
 * @package    BZiON\Models\QueryBuilder
25
 */
26
class QueryBuilder implements Countable
27
{
28
    /**
29
     * The type of the model we're building a query for
30
     * @var string
31
     */
32
    protected $type;
33
34
    /**
35
     * The columns that the model provided us
36
     * @var array
37
     */
38
    protected $columns = array('id' => 'id');
39
40
    /**
41
     * The conditions to include in WHERE
42
     * @var string[]
43
     */
44
    protected $conditions = array();
45
46
    /**
47
     * The MySQL value parameters
48
     * @var array
49
     */
50
    protected $parameters = array();
51
52
    /**
53
     * The MySQL value parameters for pagination
54
     * @var array
55
     */
56
    protected $paginationParameters = array();
57
58
    /**
59
     * Extra MySQL query string to pass
60
     * @var string
61
     */
62
    protected $extras = '';
63
64
    /**
65
     * Extra MySQL query groupby string to pass
66
     * @var string
67
     */
68
    protected $groupQuery = '';
69
70
    /**
71
     * A column based on which we should sort the results
72
     * @var string|null
73
     */
74
    private $sortBy = null;
75
76
    /**
77
     * Whether to reverse the results
78
     * @var bool
79
     */
80
    private $reverseSort = false;
81
82
    /**
83
     * The currently selected column
84
     * @var string|null
85
     */
86
    private $currentColumn = null;
87
88
    /**
89
     * The currently selected column without the table name (unless it was
90
     * explicitly provided)
91
     * @var string|null
92
     */
93
    protected $currentColumnRaw = null;
94
95
    /**
96
     * A column to consider the name of the model
97
     * @var string|null
98
     */
99
    private $nameColumn = null;
100
101
    /**
102
     * Whether to return the results as arrays instead of models
103
     * @var bool
104
     */
105
    private $returnArray = false;
0 ignored issues
show
Unused Code introduced by
The property $returnArray is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
106
107
    /**
108
     * The page to return
109
     * @var int|null
110
     */
111
    private $page = null;
112
113
    /**
114
     * Whether the ID of the first/last element has been provided
115
     * @var bool
116
     */
117
    private $limited = false;
118
119
    /**
120
     * The number of elements on every page
121
     * @var int
122
     */
123
    protected $resultsPerPage = 30;
124
125
    /**
126
     * Create a new QueryBuilder
127
     *
128
     * A new query builder should be created on a static getQueryBuilder()
129
     * method on each model. The options array can contain the following
130
     * properties:
131
     *
132
     * - `columns`: An associative array - the key of each entry is the column
133
     *   name that will be used by other methods, while the value is
134
     *   is the column name that is used in the database structure
135
     *
136
     * - `activeStatuses`: If the model has a status column, this should be
137
     *                     a list of values that make the entry be considered
138
     *                     "active"
139
     *
140
     * - `name`: The name of the column which represents the name of the object
141
     *
142
     * @param string $type    The type of the Model (e.g. "Player" or "Match")
143
     * @param array  $options The options to pass to the builder (see above)
144
     */
145 4
    public function __construct($type, $options = array())
146
    {
147 4
        $this->type = $type;
148
149 4
        if (isset($options['columns'])) {
150 4
            $this->columns += $options['columns'];
151
        }
152
153 4
        if (isset($options['name'])) {
154 2
            $this->nameColumn = $options['name'];
155
        }
156 4
    }
157
158
    /**
159
     * Select a column
160
     *
161
     * `$queryBuilder->where('username')->equals('administrator');`
162
     *
163
     * @param  string $column The column to select
164
     * @return self
165
     */
166 4
    public function where($column)
167
    {
168 4
        if (!isset($this->columns[$column])) {
169
            throw new InvalidArgumentException("Unknown column '$column'");
170
        }
171
172 4
        $this->column($this->columns[$column]);
173
174 4
        return $this;
175
    }
176
177
    /**
178
     * Request that a column equals a string (case-insensitive)
179
     *
180
     * @param  string $string The string that the column's value should equal to
181
     * @return self
182
     */
183 2
    public function equals($string)
184
    {
185 2
        $this->addColumnCondition("= ?", $string);
186
187 2
        return $this;
188
    }
189
190
    /**
191
     * Request that a column doesNOT equals a string (case-insensitive)
192
     *
193
     * @param  string $string The string that the column's value should equal to
194
     * @return self
195
     */
196 1
    public function notEquals($string)
197
    {
198 1
        $this->addColumnCondition("!= ?", $string);
199
200 1
        return $this;
201
    }
202
203
    /**
204
     * Request that a column is greater than a quantity
205
     *
206
     * @param  string $quantity The quantity to test against
207
     * @return self
208
     */
209
    public function greaterThan($quantity)
210
    {
211
        $this->addColumnCondition("> ?", $quantity);
212
213
        return $this;
214
    }
215
216
    /**
217
     * Request that a column is less than a quantity
218
     *
219
     * @param  string $quantity The quantity to test against
220
     * @return self
221
     */
222
    public function lessThan($quantity)
223
    {
224
        $this->addColumnCondition("< ?", $quantity);
225
226
        return $this;
227
    }
228
229
    /**
230
     * Request that a timestamp is before the specified time
231
     *
232
     * @param string|TimeDate $time      The timestamp to compare to
233
     * @param bool            $inclusive Whether to include the given timestamp
234
     * @param bool            $reverse   Whether to reverse the results
235
     */
236
    public function isBefore($time, $inclusive = false, $reverse = false)
237
    {
238
        return $this->isAfter($time, $inclusive, !$reverse);
239
    }
240
241
    /**
242
     * Request that a timestamp is after the specified time
243
     *
244
     * @param string|TimeDate $time      The timestamp to compare to
245
     * @param bool            $inclusive Whether to include the given timestamp
246
     * @param bool            $reverse   Whether to reverse the results
247
     */
248 1
    public function isAfter($time, $inclusive = false, $reverse = false)
249
    {
250 1
        if ($time instanceof TimeDate) {
251 1
            $time = $time->toMysql();
252
        }
253
254 1
        $comparison  = ($reverse)   ? '<' : '>';
255 1
        $comparison .= ($inclusive) ? '=' : '';
256
257 1
        $this->addColumnCondition("$comparison ?",  $time);
258
259 1
        return $this;
260
    }
261
262
    /**
263
     * Request that a column equals a number
264
     *
265
     * @param  int|Model|null $number The number that the column's value should
266
     *                                equal to. If a Model is provided, use the
267
     *                                model's ID, while null values are ignored.
268
     * @return self
269
     */
270 1 View Code Duplication
    public function is($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
271
    {
272 1
        if ($number === null) {
273
            return $this;
274
        }
275
276 1
        if ($number instanceof Model) {
277 1
            $number = $number->getId();
278
        }
279
280 1
        $this->addColumnCondition("= ?", $number);
281
282 1
        return $this;
283
    }
284
285
    /**
286
     * Request that a column equals one of some strings
287
     *
288
     * @todo   Improve for PDO
289
     * @param  string[] $strings The list of accepted values for the column
290
     * @return self
291
     */
292 3
    public function isOneOf($strings)
293
    {
294 3
        $count = count($strings);
295 3
        $questionMarks = str_repeat(',?', $count);
296
297
        // Remove first comma from questionMarks so that MySQL can read our query
298 3
        $questionMarks = ltrim($questionMarks, ',');
299
300 3
        $this->addColumnCondition("IN ($questionMarks)", $strings);
301
302 3
        return $this;
303
    }
304
305
    /**
306
     * Request that a column value starts with a string (case-insensitive)
307
     *
308
     * @param  string $string The substring that the column's value should start with
309
     * @return self
310
     */
311
    public function startsWith($string)
312
    {
313
        $this->addColumnCondition("LIKE CONCAT(?, '%')", $string);
314
315
        return $this;
316
    }
317
318
    /**
319
     * Request that a specific model is not returned
320
     *
321
     * @param  Model|int $model The ID or model you don't want to get
322
     * @return self
323
     */
324 View Code Duplication
    public function except($model)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
325
    {
326
        if ($model instanceof Model) {
327
            $model = $model->getId();
328
        }
329
330
        $this->where('id');
331
        $this->addColumnCondition("!= ?", $model);
332
333
        return $this;
334
    }
335
336
    /**
337
     * Return the results sorted by the value of a column
338
     *
339
     * @param  string $column The column based on which the results should be ordered
340
     * @return self
341
     */
342 2
    public function sortBy($column)
343
    {
344 2
        if (!isset($this->columns[$column])) {
345
            throw new Exception("Unknown column");
346
        }
347
348 2
        $this->sortBy = $this->columns[$column];
349
350 2
        return $this;
351
    }
352
353
    /**
354
     * Reverse the order
355
     *
356
     * Note: This only works if you have specified a column in the sortBy() method
357
     *
358
     * @return self
359
     */
360 2
    public function reverse()
361
    {
362 2
        $this->reverseSort = !$this->reverseSort;
363
364 2
        return $this;
365
    }
366
367
    /**
368
     * Specify the number of results per page
369
     *
370
     * @param  int  $count The number of results
371
     * @return self
372
     */
373 2
    public function limit($count)
374
    {
375 2
        $this->resultsPerPage = $count;
376
377 2
        return $this;
378
    }
379
380
    /**
381
     * Only show results from a specific page
382
     *
383
     * @param  int|null $page The page number (or null to show all pages - counting starts from 0)
384
     * @return self
385
     */
386 2
    public function fromPage($page)
387
    {
388 2
        $this->page = $page;
389
390 2
        return $this;
391
    }
392
393
    /**
394
     * End with a specific result
395
     *
396
     * @param  int|Model $model     The model (or database ID) after the first result
397
     * @param  bool   $inclusive Whether to include the provided model
398
     * @param  bool   $reverse   Whether to reverse the results
399
     * @return self
400
     */
401 1
    public function endAt($model, $inclusive = false, $reverse = false)
402
    {
403 1
        return $this->startAt($model, $inclusive, !$reverse);
404
    }
405
406
    /**
407
     * Start with a specific result
408
     *
409
     * @param  int|Model $model     The model (or database ID) before the first result
410
     * @param  bool   $inclusive Whether to include the provided model
411
     * @param  bool   $reverse   Whether to reverse the results
412
     * @return self
413
     */
414 1
    public function startAt($model, $inclusive = false, $reverse = false)
415
    {
416 1
        if (!$model) {
417 1
            return $this;
418
        } elseif ($model instanceof Model && !$model->isValid()) {
419
            return $this;
420
        }
421
422
        $this->column($this->sortBy);
423
        $this->limited = true;
424
        $column = $this->currentColumn;
425
        $table  = $this->getTable();
426
427
        $comparison  = $this->reverseSort ^ $reverse;
428
        $comparison  = ($comparison) ? '>' : '<';
429
        $comparison .= ($inclusive)  ? '=' : '';
430
        $id = ($model instanceof Model) ? $model->getId() : $model;
431
432
        // Compare an element's timestamp to the timestamp of $model; if it's the
433
        // same, perform the comparison using IDs
434
        $this->addColumnCondition(
435
            "$comparison (SELECT $column FROM $table WHERE id = ?) OR ($column = (SELECT $column FROM $table WHERE id = ?) AND id $comparison ?)",
436
            array($id, $id, $id)
437
        );
438
439
        return $this;
440
    }
441
442
    /**
443
     * Request that only "active" Models should be returned
444
     *
445
     * @return self
446
     */
447 3
    public function active()
448
    {
449 3
        if (!isset($this->columns['status'])) {
450
            return $this;
451
        }
452
453 3
        $type = $this->type;
454
455 3
        return $this->where('status')->isOneOf($type::getActiveStatuses());
456
    }
457
458
    /**
459
     * Make sure that Models invisible to a player are not returned
460
     *
461
     * Note that this method does not take PermissionModel::canBeSeenBy() into
462
     * consideration for performance purposes, so you will have to override this
463
     * in your query builder if necessary.
464
     *
465
     * @param  Player  $player      The player in question
466
     * @param  bool $showDeleted false to hide deleted models even from admins
467
     * @return self
468
     */
469 1
    public function visibleTo($player, $showDeleted = false)
470
    {
471 1
        $type = $this->type;
472
473 1
        if (is_subclass_of($type, "PermissionModel")
474 1
         && $player->hasPermission($type::EDIT_PERMISSION)) {
475
            // The player is an admin who can see hidden models
476 1
            if (!$showDeleted) {
477 1
                if (isset($this->columns['status'])) {
478 1
                    return $this->where('status')->notEquals('deleted');
479
                }
480
            }
481
        } else {
482 1
            return $this->active();
483
        }
484
485
        return $this;
486
    }
487
488
    /**
489
     * Perform the query and get back the results in an array of names
490
     *
491
     * @return string[] An array of the type $id => $name
492
     */
493 1
    public function getNames()
494
    {
495 1
        if (!$this->nameColumn) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->nameColumn of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
496
            throw new Exception("You haven't specified a name column");
497
        }
498
499 1
        $results = $this->getArray($this->nameColumn);
500
501 1
        return array_column($results, $this->nameColumn, 'id');
502
    }
503
504
    /**
505
     * Perform the query and get back the results in a list of arrays
506
     *
507
     * @todo   Play with partial models?
508
     * @param  string|string[] $columns The column(s) that should be returned
509
     * @return array[]
510
     */
511 1
    public function getArray($columns)
512
    {
513 1
        if (!is_array($columns)) {
514 1
            $columns = array($columns);
515
        }
516
517 1
        $db = Database::getInstance();
518
519 1
        return $db->query($this->createQuery($columns), $this->getParameters());
520
    }
521
522
    /**
523
     * An alias for QueryBuilder::getModels(), with fast fetching on by default
524
     * and no return of results
525
     *
526
     * @param  bool $fastFetch Whether to perform one query to load all
527
     *                            the model data instead of fetching them
528
     *                            one by one
529
     * @return void
530
     */
531
    public function addToCache($fastFetch = true)
532
    {
533
        $this->getModels($fastFetch);
534
    }
535
536
    /**
537
     * Perform the query and get the results as Models
538
     *
539
     * @todo Fix fast fetch for queries with multiple tables
540
     * @param  bool $fastFetch Whether to perform one query to load all
541
     *                            the model data instead of fetching them
542
     *                            one by one (ignores cache)
543
     * @return array
544
     */
545 4
    public function getModels($fastFetch = false)
546
    {
547 4
        $db   = Database::getInstance();
548 4
        $type = $this->type;
549
550 4
        $columns = ($fastFetch) ? $type::getEagerColumns() : array();
551
552 4
        $results = $db->query($this->createQuery($columns), $this->getParameters());
553
554 4
        if ($fastFetch) {
555 2
            return $type::createFromDatabaseResults($results);
556
        } else {
557 3
            return $type::arrayIdToModel(array_column($results, 'id'));
558
        }
559
    }
560
561
    /**
562
     * Count the results
563
     *
564
     * @return int
565
     */
566 1
    public function count()
567
    {
568 1
        $table  = $this->getTable();
569 1
        $params = $this->createQueryParams(false);
570 1
        $db     = Database::getInstance();
571 1
        $query  = "SELECT COUNT(*) FROM $table $params";
572
573
        // We don't want pagination to affect our results so don't use the functions that combine
574
        // pagination results
575 1
        $results = $db->query($query, $this->parameters);
576
577 1
        return $results[0]['COUNT(*)'];
578
    }
579
580
    /**
581
     * Count the number of pages that all the models could be separated into
582
     */
583 1
    public function countPages()
584
    {
585 1
        return ceil($this->count() / $this->getResultsPerPage());
586
    }
587
588
    /**
589
     * Find if there is any result
590
     *
591
     * @return bool
592
     */
593 1
    public function any()
594
    {
595
        // Make sure that we don't mess with the user's options
596 1
        $query = clone $this;
597
598 1
        $query->limit(1);
599
600 1
        return $query->count() > 0;
601
    }
602
603
    /**
604
     * Get the amount of results that are returned per page
605
     * @return int
606
     */
607 1
    public function getResultsPerPage()
608
    {
609 1
        return $this->resultsPerPage;
610
    }
611
612
    /**
613
     * Select a column to perform opeations on
614
     *
615
     * This is identical to the `where()` method, except that the column is
616
     * specified as a MySQL column and not as a column name given by the model
617
     *
618
     * @param  string $column The column to select
619
     * @return self
620
     */
621 4
    protected function column($column)
622
    {
623 4
        if (strpos($column, '.') === false) {
624
            // Add the table name to the column if it isn't there already so that
625
            // MySQL knows what to do when handling multiple tables
626 4
            $table = $this->getTable();
627 4
            $this->currentColumn = "`$table`.`$column`";
628
        } else {
629 1
            $this->currentColumn = $column;
630
        }
631
632 4
        $this->currentColumnRaw = $column;
633
634 4
        return $this;
635
    }
636
637
    /**
638
     * Add a condition for the column
639
     * @param  string $condition The MySQL condition
640
     * @param  mixed  $value     Value(s) to pass to MySQL
641
     * @return void
642
     */
643 4
    protected function addColumnCondition($condition, $value)
644
    {
645 4
        if (!$this->currentColumn) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->currentColumn of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
646
            throw new Exception("You haven't selected a column!");
647
        }
648
649 4
        if (!is_array($value)) {
650 2
            $value = array($value);
651
        }
652
653 4
        $this->conditions[] = "{$this->currentColumn} $condition";
654 4
        $this->parameters   = array_merge($this->parameters, $value);
655
656 4
        $this->currentColumn = null;
657 4
        $this->currentColumnRaw = null;
658 4
    }
659
660
    /**
661
     * Get the MySQL extra parameters
662
     *
663
     * @param  bool $respectPagination Whether to respect pagination or not; useful for when pagination should be ignored such as count
664
     * @return string
665
     */
666 4
    protected function createQueryParams($respectPagination = true)
667
    {
668 4
        $extras     = $this->extras;
669 4
        $conditions = $this->createQueryConditions();
670 4
        $groupQuery = $this->groupQuery;
671 4
        $order      = $this->createQueryOrder();
672 4
        $pagination = "";
673
674 4
        if ($respectPagination) {
675 4
            $pagination = $this->createQueryPagination();
676
        }
677
678 4
        return "$extras $conditions $groupQuery $order $pagination";
679
    }
680
681
    /**
682
     * Get the query parameters
683
     *
684
     * @return array
685
     */
686 4
    protected function getParameters()
687
    {
688 4
        return array_merge($this->parameters, $this->paginationParameters);
689
    }
690
691
    /**
692
     * Get the table of the model
693
     *
694
     * @return string
695
     */
696 4
    protected function getTable()
697
    {
698 4
        $type = $this->type;
699
700 4
        return $type::TABLE;
701
    }
702
703
    /**
704
     * Get a MySQL query string in the requested format
705
     * @param  string|string[] $columns The columns that should be included
706
     *                                  (without the ID, if an array is provided)
707
     * @return string The query
708
     */
709 4
    protected function createQuery($columns = array())
710
    {
711 4
        $type     = $this->type;
712 4
        $table    = $type::TABLE;
713 4
        $params   = $this->createQueryParams();
714
715 4
        if (is_array($columns)) {
716 3
            $columns = $this->createQueryColumns($columns);
717
        } elseif (empty($columns)) {
718
            $columns = $this->createQueryColumns();
719
        }
720
721 4
        return "SELECT $columns FROM $table $params";
722
    }
723
724
    /**
725
     * Generate the columns for the query
726
     * @param  string[] $columns The columns that should be included (without the ID)
727
     * @return string
728
     */
729 3
    private function createQueryColumns($columns = array())
730
    {
731 3
        $type = $this->type;
732 3
        $table = $type::TABLE;
733 3
        $columnStrings = array("`$table`.id");
734
735 3
        foreach ($columns as $returnName) {
736 1
            if (strpos($returnName, ' ') === false) {
737 1
                $dbName = $this->columns[$returnName];
738 1
                $columnStrings[] = "`$table`.`$dbName` as `$returnName`";
739
            } else {
740
                // "Column" contains a space, pass it as is
741 1
                $columnStrings[] = $returnName;
742
            }
743
        }
744
745 3
        return implode(',', $columnStrings);
746
    }
747
748
    /**
749
     * Generates all the WHERE conditions for the query
750
     * @return string
751
     */
752 4
    private function createQueryConditions()
753
    {
754 4
        if ($this->conditions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->conditions of type string[] 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...
755
            // Add parentheses around the conditions to prevent conflicts due
756
            // to the order of operations
757
            $conditions = array_map(function ($value) { return "($value)"; }, $this->conditions);
758
759 4
            return 'WHERE ' . implode(' AND ', $conditions);
760
        }
761
762
        return '';
763
    }
764
765
    /**
766
     * Generates the sorting instructions for the query
767
     * @return string
768
     */
769 4
    private function createQueryOrder()
770
    {
771 4
        if ($this->sortBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sortBy of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
772 2
            $order = 'ORDER BY ' . $this->sortBy;
773
774
            // Sort by ID if the sorting columns are equal
775 2
            $id = '`' . $this->getTable() . '`.`id`';
776 2
            if ($this->reverseSort) {
777 2
                $order .= " DESC, $id DESC";
778
            } else {
779 2
                $order .= ", $id";
780
            }
781
        } else {
782 3
            $order = '';
783
        }
784
785 4
        return $order;
786
    }
787
788
    /**
789
     * Generates the pagination instructions for the query
790
     * @return string
791
     */
792 4
    private function createQueryPagination()
793
    {
794
        // Reset mysqli params just in case createQueryParagination()
795
        // had been called earlier
796 4
        $this->paginationParameters = array();
797
798 4
        if (!$this->page && !$this->limited) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->page of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
799 3
            return '';
800
        }
801
802 2
        $offset = '';
803 2
        if ($this->page) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->page of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
804 2
            $firstElement = ($this->page - 1) * $this->resultsPerPage;
805 2
            $this->paginationParameters[] = $firstElement;
806
807 2
            $offset = '?,';
808
        }
809
810 2
        $this->paginationParameters[] = $this->resultsPerPage;
811
812 2
        return "LIMIT $offset ?";
813
    }
814
}
815