Completed
Push — master ( b97768...587712 )
by Bartko
31:36
created

Manipulator::getDescendants()   B

Complexity

Conditions 8
Paths 33

Size

Total Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 63
rs 7.5628
c 0
b 0
f 0
cc 8
nc 33
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace StefanoTree\NestedSet\Manipulator;
6
7
use StefanoTree\NestedSet\Adapter\AdapterInterface;
8
use StefanoTree\NestedSet\NodeInfo;
9
use StefanoTree\NestedSet\Options;
10
11
class Manipulator implements ManipulatorInterface
12
{
13
    private $adapter;
14
15
    private $options;
16
17
    private $dbSelectBuilder;
18
19
    public function __construct(Options $options, AdapterInterface $adapter)
20
    {
21
        $this->setOptions($options);
22
        $this->setAdapter($adapter);
23
    }
24
25
    /**
26
     * @param \StefanoTree\NestedSet\Adapter\AdapterInterface $adapter
27
     */
28
    private function setAdapter(AdapterInterface $adapter): void
29
    {
30
        $this->adapter = $adapter;
31
    }
32
33
    /**
34
     * @return AdapterInterface
35
     */
36
    public function getAdapter(): AdapterInterface
37
    {
38
        return $this->adapter;
39
    }
40
41
    /**
42
     * @param Options $options
43
     */
44
    protected function setOptions(Options $options): void
45
    {
46
        $this->options = $options;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function getOptions(): Options
53
    {
54
        return $this->options;
55
    }
56
57
    /**
58
     * Data cannot contain keys like idColumnName, levelColumnName, ...
59
     *
60
     * @param array $data
61
     *
62
     * @return array
63
     */
64
    protected function cleanData(array $data): array
65
    {
66
        $options = $this->getOptions();
67
68
        $disallowedDataKeys = array(
69
            $options->getIdColumnName(),
70
            $options->getLeftColumnName(),
71
            $options->getRightColumnName(),
72
            $options->getLevelColumnName(),
73
            $options->getParentIdColumnName(),
74
        );
75
76
        if (null !== $options->getScopeColumnName()) {
77
            $disallowedDataKeys[] = $options->getScopeColumnName();
78
        }
79
80
        return array_diff_key($data, array_flip($disallowedDataKeys));
81
    }
82
83
    /**
84
     * @param array $data
85
     *
86
     * @return NodeInfo
87
     */
88
    protected function _buildNodeInfoObject(array $data)
89
    {
90
        $options = $this->getOptions();
91
92
        $id = $data[$options->getIdColumnName()];
93
        $parentId = $data[$options->getParentIdColumnName()];
94
        $level = (int) $data[$options->getLevelColumnName()];
95
        $left = (int) $data[$options->getLeftColumnName()];
96
        $right = (int) $data[$options->getRightColumnName()];
97
98
        if (isset($data[$options->getScopeColumnName()])) {
99
            $scope = $data[$options->getScopeColumnName()];
100
        } else {
101
            $scope = null;
102
        }
103
104
        return new NodeInfo($id, $parentId, $level, $left, $right, $scope);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function setDbSelectBuilder(callable $selectBuilder): void
111
    {
112
        $this->dbSelectBuilder = $selectBuilder;
113
    }
114
115
    /**
116
     * @return callable
117
     */
118
    public function getDbSelectBuilder(): callable
119
    {
120
        if (null === $this->dbSelectBuilder) {
121
            $this->dbSelectBuilder = function () {
122
                return $this->getBlankDbSelect();
123
            };
124
        }
125
126
        return $this->dbSelectBuilder;
127
    }
128
129
    /**
130
     * @return string
131
     */
132
    public function getBlankDbSelect(): string
133
    {
134
        return 'SELECT * FROM '.$this->getAdapter()->quoteIdentifier($this->getOptions()->getTableName()).' ';
135
    }
136
137
    /**
138
     * Return default db select.
139
     *
140
     * @return string
141
     */
142
    public function getDefaultDbSelect()
143
    {
144
        return $this->getDbSelectBuilder()();
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function lockTree(): void
151
    {
152
        $options = $this->getOptions();
153
        $adapter = $this->getAdapter();
154
155
        $sql = 'SELECT '.$adapter->quoteIdentifier($options->getIdColumnName())
156
            .' FROM '.$adapter->quoteIdentifier($options->getTableName())
157
            .' FOR UPDATE';
158
159
        $adapter->executeSQL($sql);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function beginTransaction(): void
166
    {
167
        $this->getAdapter()
168
             ->beginTransaction();
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function commitTransaction(): void
175
    {
176
        $this->getAdapter()
177
             ->commitTransaction();
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function rollbackTransaction(): void
184
    {
185
        $this->getAdapter()
186
             ->rollbackTransaction();
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function update($nodeId, array $data): void
193
    {
194
        $options = $this->getOptions();
195
        $adapter = $this->getAdapter();
196
        $data = $this->cleanData($data);
197
198
        $setPart = array_map(function ($item) use ($adapter) {
199
            return $adapter->quoteIdentifier($item).' = :'.$item;
200
        }, array_keys($data));
201
202
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
203
            .' SET '.implode(', ', $setPart)
204
            .' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName()).' = :__nodeID';
205
206
        $data['__nodeID'] = $nodeId;
207
208
        $adapter->executeSQL($sql, $data);
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214
    public function insert(NodeInfo $nodeInfo, array $data)
215
    {
216
        $options = $this->getOptions();
217
218
        $adapter = $this->getAdapter();
219
220
        $data[$options->getParentIdColumnName()] = $nodeInfo->getParentId();
221
        $data[$options->getLevelColumnName()] = $nodeInfo->getLevel();
222
        $data[$options->getLeftColumnName()] = $nodeInfo->getLeft();
223
        $data[$options->getRightColumnName()] = $nodeInfo->getRight();
224
225
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
226
            $data[$options->getScopeColumnName()] = $nodeInfo->getScope();
227
        }
228
229
        $columns = array_map(function ($item) use ($adapter) {
230
            return $adapter->quoteIdentifier($item);
231
        }, array_keys($data));
232
233
        $values = array_map(function ($item) {
234
            return ':'.$item;
235
        }, array_keys($data));
236
237
        $sql = 'INSERT INTO '.$adapter->quoteIdentifier($options->getTableName())
238
            .' ('.implode(', ', $columns).')'
239
            .' VALUES('.implode(', ', $values).')';
240
241
        return $adapter->executeInsertSQL($sql, $data);
242
    }
243
244
    /**
245
     * {@inheritdoc}
246
     */
247
    public function delete($nodeId): void
248
    {
249
        $options = $this->getOptions();
250
        $adapter = $this->getAdapter();
251
252
        $sql = 'DELETE FROM '.$adapter->quoteIdentifier($options->getTableName())
253
            .' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName()).' = :__nodeID';
254
255
        $params = array(
256
            '__nodeID' => $nodeId,
257
        );
258
259
        $adapter->executeSQL($sql, $params);
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265
    public function moveLeftIndexes($fromIndex, $shift, $scope = null): void
266
    {
267
        $options = $this->getOptions();
268
269
        if (0 == $shift) {
270
            return;
271
        }
272
273
        $adapter = $this->getAdapter();
274
275
        $params = array(
276
            ':shift' => $shift,
277
            ':fromIndex' => $fromIndex,
278
        );
279
280
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
281
            .' SET '
282
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' = '
283
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' + :shift'
284
            .' WHERE '
285
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' > :fromIndex';
286
287
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
288
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName()).' = :__scope';
289
            $params['__scope'] = $scope;
290
        }
291
292
        $adapter->executeSQL($sql, $params);
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298
    public function moveRightIndexes($fromIndex, $shift, $scope = null): void
299
    {
300
        $options = $this->getOptions();
301
302
        if (0 == $shift) {
303
            return;
304
        }
305
306
        $adapter = $this->getAdapter();
307
308
        $params = array(
309
            ':shift' => $shift,
310
            ':fromIndex' => $fromIndex,
311
        );
312
313
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
314
            .' SET '
315
            .$adapter->quoteIdentifier($options->getRightColumnName()).' = '
316
            .$adapter->quoteIdentifier($options->getRightColumnName()).' + :shift'
317
            .' WHERE '
318
            .$adapter->quoteIdentifier($options->getRightColumnName()).' > :fromIndex';
319
320
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
321
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName()).' = :__scope';
322
            $params['__scope'] = $scope;
323
        }
324
325
        $adapter->executeSQL($sql, $params);
326
    }
327
328
    /**
329
     * {@inheritdoc}
330
     */
331
    public function updateParentId($nodeId, $newParentId): void
332
    {
333
        $options = $this->getOptions();
334
335
        $adapter = $this->getAdapter();
336
337
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
338
            .' SET '.$adapter->quoteIdentifier($options->getParentIdColumnName()).' = :__parentId'
339
            .' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName()).' = :__nodeId';
340
341
        $params = array(
342
            '__parentId' => $newParentId,
343
            '__nodeId' => $nodeId,
344
        );
345
346
        $adapter->executeSQL($sql, $params);
347
    }
348
349
    /**
350
     * {@inheritdoc}
351
     */
352
    public function updateLevels(int $leftIndexFrom, int $rightIndexTo, int $shift, $scope = null): void
353
    {
354
        $options = $this->getOptions();
355
356
        if (0 == $shift) {
357
            return;
358
        }
359
360
        $adapter = $this->getAdapter();
361
362
        $binds = array(
363
            ':shift' => $shift,
364
            ':leftFrom' => $leftIndexFrom,
365
            ':rightTo' => $rightIndexTo,
366
        );
367
368
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
369
            .' SET '
370
            .$adapter->quoteIdentifier($options->getLevelColumnName()).' = '
371
            .$adapter->quoteIdentifier($options->getLevelColumnName()).' + :shift'
372
            .' WHERE '
373
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' >= :leftFrom'
374
            .' AND '.$adapter->quoteIdentifier($options->getRightColumnName()).' <= :rightTo';
375
376
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
377
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName()).' = :__scope';
378
            $binds['__scope'] = $scope;
379
        }
380
381
        $adapter->executeSQL($sql, $binds);
382
    }
383
384
    /**
385
     * {@inheritdoc}
386
     */
387
    public function moveBranch(int $leftIndexFrom, int $rightIndexTo, int $shift, $scope = null): void
388
    {
389
        if (0 == $shift) {
390
            return;
391
        }
392
393
        $options = $this->getOptions();
394
395
        $adapter = $this->getAdapter();
396
397
        $binds = array(
398
            ':shift' => $shift,
399
            ':leftFrom' => $leftIndexFrom,
400
            ':rightTo' => $rightIndexTo,
401
        );
402
403
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
404
            .' SET '
405
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' = '
406
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' + :shift, '
407
            .$adapter->quoteIdentifier($options->getRightColumnName()).' = '
408
            .$adapter->quoteIdentifier($options->getRightColumnName()).' + :shift'
409
            .' WHERE '
410
            .$adapter->quoteIdentifier($options->getLeftColumnName()).' >= :leftFrom'
411
            .' AND '.$adapter->quoteIdentifier($options->getRightColumnName()).' <= :rightTo';
412
413
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
414
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName()).' = :__scope';
415
            $binds['__scope'] = $scope;
416
        }
417
418
        $adapter->executeSQL($sql, $binds);
419
    }
420
421
    /**
422
     * {@inheritdoc}
423
     */
424
    public function getRoots($scope = null): array
425
    {
426
        $options = $this->getOptions();
427
428
        $adapter = $this->getAdapter();
429
430
        $params = array();
431
432
        $sql = $this->getBlankDbSelect();
433
        $sql .= ' WHERE '.$adapter->quoteIdentifier($options->getParentIdColumnName(true)).' IS NULL';
434
435
        if (null != $scope && $options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
436
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName(true)).' = :__scope';
437
            $params['__scope'] = $scope;
438
        }
439
440
        $sql .= ' ORDER BY '.$adapter->quoteIdentifier($options->getIdColumnName(true)).' ASC';
441
442
        return $adapter->executeSelectSQL($sql, $params);
443
    }
444
445
    /**
446
     * {@inheritdoc}
447
     */
448
    public function getRoot($scope = null): array
449
    {
450
        $roots = $this->getRoots($scope);
451
452
        return (0 < count($roots)) ? $roots[0] : array();
453
    }
454
455
    /**
456
     * {@inheritdoc}
457
     */
458
    public function getNode($nodeId): ?array
459
    {
460
        $options = $this->getOptions();
461
        $nodeId = (int) $nodeId;
462
        $adapter = $this->getAdapter();
463
464
        $params = array(
465
            '__nodeID' => $nodeId,
466
        );
467
468
        $sql = $this->getDefaultDbSelect();
469
        $sql .= ' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName(true)).' = :__nodeID';
470
471
        $result = $adapter->executeSelectSQL($sql, $params);
472
473
        return (0 < count($result)) ? $result[0] : null;
474
    }
475
476
    /**
477
     * {@inheritdoc}
478
     */
479
    public function getNodeInfo($nodeId): ?NodeInfo
480
    {
481
        $options = $this->getOptions();
482
        $adapter = $this->getAdapter();
483
484
        $params = array(
485
            '__nodeID' => $nodeId,
486
        );
487
488
        $sql = $this->getBlankDbSelect();
489
        $sql .= ' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName(true)).' = :__nodeID';
490
491
        $array = $adapter->executeSelectSQL($sql, $params);
492
493
        $result = ($array) ? $this->_buildNodeInfoObject($array[0]) : null;
494
495
        return $result;
496
    }
497
498
    /**
499
     * {@inheritdoc}
500
     */
501
    public function getChildrenNodeInfo($parentNodeId): array
502
    {
503
        $adapter = $this->getAdapter();
504
        $options = $this->getOptions();
505
506
        $params = array(
507
            '__parentID' => $parentNodeId,
508
        );
509
510
        $sql = 'SELECT *'
511
            .' FROM '.$adapter->quoteIdentifier($this->getOptions()->getTableName())
512
            .' WHERE '.$adapter->quoteIdentifier($options->getParentIdColumnName(true)).' = :__parentID'
513
            .' ORDER BY '.$adapter->quoteIdentifier($options->getLeftColumnName(true)).' ASC';
514
515
        $data = $adapter->executeSelectSQL($sql, $params);
516
517
        $result = array();
518
        foreach ($data as $nodeData) {
519
            $result[] = $this->_buildNodeInfoObject($nodeData);
520
        }
521
522
        return $result;
523
    }
524
525
    /**
526
     * {@inheritdoc}
527
     */
528
    public function updateNodeMetadata(NodeInfo $nodeInfo): void
529
    {
530
        $adapter = $this->getAdapter();
531
        $options = $this->getOptions();
532
533
        $data = array(
534
            $options->getRightColumnName() => $nodeInfo->getRight(),
535
            $options->getLeftColumnName() => $nodeInfo->getLeft(),
536
            $options->getLevelColumnName() => $nodeInfo->getLevel(),
537
        );
538
539
        $setPart = array_map(function ($item) use ($adapter) {
540
            return $adapter->quoteIdentifier($item).' = :'.$item;
541
        }, array_keys($data));
542
543
        $sql = 'UPDATE '.$adapter->quoteIdentifier($options->getTableName())
544
            .' SET '.implode(', ', $setPart)
545
            .' WHERE '.$adapter->quoteIdentifier($options->getIdColumnName()).' = :__nodeID';
546
547
        $data['__nodeID'] = $nodeInfo->getId();
548
549
        $adapter->executeSQL($sql, $data);
550
    }
551
552
    /**
553
     * {@inheritdoc}
554
     */
555
    public function getAncestors($nodeId, int $startLevel = 0, int $excludeLastNLevels = 0): array
556
    {
557
        $options = $this->getOptions();
558
559
        // node does not exist
560
        $nodeInfo = $this->getNodeInfo($nodeId);
561
        if (!$nodeInfo) {
562
            return array();
563
        }
564
565
        $adapter = $this->getAdapter();
566
567
        $params = array(
568
            '__leftIndex' => $nodeInfo->getLeft(),
569
            '__rightIndex' => $nodeInfo->getRight(),
570
        );
571
572
        $sql = $this->getDefaultDbSelect();
573
574
        $sql .= ' WHERE '.$adapter->quoteIdentifier($options->getLeftColumnName(true)).' <= :__leftIndex'
575
            .' AND '.$adapter->quoteIdentifier($options->getRightColumnName(true)).' >= :__rightIndex';
576
577
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
578
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getScopeColumnName(true)).' = :__scope';
579
            $params['__scope'] = $nodeInfo->getScope();
580
        }
581
582
        if (0 < $startLevel) {
583
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getLevelColumnName(true)).' >= :__startLevel';
584
            $params['__startLevel'] = $startLevel;
585
        }
586
587
        if (0 < $excludeLastNLevels) {
588
            $sql .= ' AND '.$adapter->quoteIdentifier($options->getLevelColumnName(true)).' <= :__excludeLastNLevels';
589
            $params['__excludeLastNLevels'] = $nodeInfo->getLevel() - $excludeLastNLevels;
590
        }
591
592
        $sql .= ' ORDER BY '.$adapter->quoteIdentifier($options->getLeftColumnName(true)).' ASC';
593
594
        return $adapter->executeSelectSQL($sql, $params);
595
    }
596
597
    /**
598
     * {@inheritdoc}
599
     */
600
    public function getDescendants($nodeId, int $startLevel = 0, ?int $levels = null, $excludeBranch = null): array
601
    {
602
        $options = $this->getOptions();
603
604
        if (!$nodeInfo = $this->getNodeInfo($nodeId)) {
605
            return array();
606
        }
607
608
        $adapter = $this->getAdapter();
609
        $sql = $this->getDefaultDbSelect();
610
611
        $params = array();
612
        $wherePart = array();
613
614
        if ($options->getScopeColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options->getScopeColumnName() 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...
615
            $wherePart[] = $adapter->quoteIdentifier($options->getScopeColumnName(true)).' = :__scope';
616
            $params['__scope'] = $nodeInfo->getScope();
617
        }
618
619
        if (0 != $startLevel) {
620
            $wherePart[] = $adapter->quoteIdentifier($options->getLevelColumnName(true)).' >= :__level';
621
            $params['__level'] = $nodeInfo->getLevel() + $startLevel;
622
        }
623
624
        if (null != $levels) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $levels of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
625
            $wherePart[] = $adapter->quoteIdentifier($options->getLevelColumnName(true)).' < :__endLevel';
626
            $params['__endLevel'] = $nodeInfo->getLevel() + $startLevel + abs($levels);
627
        }
628
629
        if (null != $excludeBranch && null != ($excludeNodeInfo = $this->getNodeInfo($excludeBranch))) {
630
            $wherePart[] = '( '
631
                .$adapter->quoteIdentifier($options->getLeftColumnName(true)).' BETWEEN :__l1 AND :__p1'
632
                .' OR '
633
                .$adapter->quoteIdentifier($options->getLeftColumnName(true)).' BETWEEN :__l2 AND :__p2'
634
                .') AND ('
635
                .$adapter->quoteIdentifier($options->getRightColumnName(true)).' BETWEEN :__l3 AND :__p3'
636
                .' OR '
637
                .$adapter->quoteIdentifier($options->getRightColumnName(true)).' BETWEEN :__l4 AND :__p4'
638
                .')';
639
640
            $params['__l1'] = $nodeInfo->getLeft();
641
            $params['__p1'] = $excludeNodeInfo->getLeft() - 1;
642
            $params['__l2'] = $excludeNodeInfo->getRight() + 1;
643
            $params['__p2'] = $nodeInfo->getRight();
644
            $params['__l3'] = $excludeNodeInfo->getRight() + 1;
645
            $params['__p3'] = $nodeInfo->getRight();
646
            $params['__l4'] = $nodeInfo->getLeft();
647
            $params['__p4'] = $excludeNodeInfo->getLeft() - 1;
648
        } else {
649
            $wherePart[] = $adapter->quoteIdentifier($options->getLeftColumnName(true)).' >= :__left'
650
                .' AND '.$adapter->quoteIdentifier($options->getRightColumnName(true)).' <= :__right';
651
652
            $params['__left'] = $nodeInfo->getLeft();
653
            $params['__right'] = $nodeInfo->getRight();
654
        }
655
656
        $sql .= ' WHERE '.implode(' AND ', $wherePart);
657
        $sql .= ' ORDER BY '.$adapter->quoteIdentifier($options->getLeftColumnName(true)).' ASC';
658
659
        $result = $adapter->executeSelectSQL($sql, $params);
660
661
        return (0 < count($result)) ? $result : array();
662
    }
663
}
664