Completed
Push — master ( f2ef3e...a98c98 )
by Bartko
01:49
created

Doctrine2DBAL::getChildrenNodeInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 13
cts 13
cp 1
rs 9.504
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace StefanoTree\NestedSet\Adapter;
6
7
use Doctrine\DBAL\Connection as DbConnection;
8
use Doctrine\DBAL\Query\QueryBuilder;
9
use StefanoTree\NestedSet\NodeInfo;
10
use StefanoTree\NestedSet\Options;
11
12
class Doctrine2DBAL extends AdapterAbstract implements AdapterInterface
13
{
14
    private $connection;
15
16
    /**
17
     * @param Options      $options
18
     * @param DbConnection $connection
19
     */
20 56
    public function __construct(Options $options, DbConnection $connection)
21
    {
22 56
        $this->setOptions($options);
23 56
        $this->setConnection($connection);
24 56
    }
25
26
    /**
27
     * @param DbConnection $dbAdapter
28
     */
29 56
    protected function setConnection(DbConnection $dbAdapter): void
30
    {
31 56
        $this->connection = $dbAdapter;
32 56
    }
33
34
    /**
35
     * @return DbConnection
36
     */
37 53
    private function getConnection(): DbConnection
38
    {
39 53
        return $this->connection;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     *
45
     * @return QueryBuilder
46
     */
47 32
    public function getBlankDbSelect(): QueryBuilder
48
    {
49 32
        $queryBuilder = $this->getConnection()
50 32
                             ->createQueryBuilder();
51
52 32
        $queryBuilder->select(sprintf('%s.*', $this->getOptions()->getTableName()))
53 32
                     ->from($this->getOptions()->getTableName());
54
55 32
        return $queryBuilder;
56
    }
57
58
    /**
59
     * Return default db select. Always new instance.
60
     *
61
     * @return QueryBuilder
62
     */
63 17
    public function getDefaultDbSelect(): QueryBuilder
64
    {
65 17
        return $this->getDbSelectBuilder()();
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 1
    public function lockTree(): void
72
    {
73 1
        $options = $this->getOptions();
74
75 1
        $connection = $this->getConnection();
76
77 1
        $sql = $this->getBlankDbSelect();
78 1
        $sql->select($options->getIdColumnName(true).' AS i');
79
80 1
        $sql = $sql->getSQL().' FOR UPDATE';
81
82 1
        $connection->executeQuery($sql);
83 1
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 1
    public function beginTransaction(): void
89
    {
90 1
        $this->getConnection()
91 1
             ->beginTransaction();
92 1
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 1
    public function commitTransaction(): void
98
    {
99 1
        $this->getConnection()
100 1
             ->commit();
101 1
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 1
    public function rollbackTransaction(): void
107
    {
108 1
        $this->getConnection()
109 1
             ->rollBack();
110 1
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 3
    public function update($nodeId, array $data): void
116
    {
117 3
        $options = $this->getOptions();
118
119 3
        $connection = $this->getConnection();
120
121 3
        $data = $this->cleanData($data);
122
123 3
        $sql = $connection->createQueryBuilder();
124
125 3
        $sql->update($options->getTableName(), null)
126 3
            ->where($options->getIdColumnName().' = :'.$options->getIdColumnName());
127
128 3
        foreach ($data as $key => $value) {
129 3
            $sql->set($connection->quoteIdentifier($key), ':'.$key);
130
        }
131
132 3
        $data[$options->getIdColumnName()] = $nodeId;
133
134 3
        $connection->executeUpdate($sql->getSQL(), $data);
135 3
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 4
    public function insert(NodeInfo $nodeInfo, array $data)
141
    {
142 4
        $options = $this->getOptions();
143
144 4
        $connection = $this->getConnection();
145
146 4
        $data[$options->getParentIdColumnName()] = $nodeInfo->getParentId();
147 4
        $data[$options->getLevelColumnName()] = $nodeInfo->getLevel();
148 4
        $data[$options->getLeftColumnName()] = $nodeInfo->getLeft();
149 4
        $data[$options->getRightColumnName()] = $nodeInfo->getRight();
150
151 4
        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...
152 1
            $data[$options->getScopeColumnName()] = $nodeInfo->getScope();
153
        }
154
155 4
        $connection->insert($options->getTableName(), $data);
156
157 4
        if (array_key_exists($options->getIdColumnName(), $data)) {
158 1
            return $data[$options->getIdColumnName()];
159
        } else {
160 3
            return $connection->lastInsertId($options->getSequenceName());
161
        }
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167 2
    public function delete($nodeId): void
168
    {
169 2
        $options = $this->getOptions();
170
171 2
        $connection = $this->getConnection();
172
173 2
        $sql = $connection->createQueryBuilder();
174 2
        $sql->delete($options->getTableName())
175 2
            ->where($options->getIdColumnName().' = :id');
176
177
        $params = array(
178 2
            ':id' => $nodeId,
179
        );
180
181 2
        $connection->executeQuery($sql->getSQL(), $params);
182 2
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 2
    public function moveLeftIndexes($fromIndex, $shift, $scope = null): void
188
    {
189 2
        $options = $this->getOptions();
190
191 2
        if (0 == $shift) {
192
            return;
193
        }
194
195 2
        $connection = $this->getConnection();
196
197 2
        $sql = $connection->createQueryBuilder();
198 2
        $sql->update($options->getTableName())
199 2
            ->set($options->getLeftColumnName(), $options->getLeftColumnName().' + :shift')
200 2
            ->where($options->getLeftColumnName().' > :fromIndex');
201
202
        $params = array(
203 2
            ':shift' => $shift,
204 2
            ':fromIndex' => $fromIndex,
205
        );
206
207 2
        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...
208 1
            $sql->andWhere($options->getScopeColumnName().' = :scope');
209 1
            $params[':scope'] = $scope;
210
        }
211
212 2
        $connection->executeUpdate($sql->getSQL(), $params);
213 2
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 2
    public function moveRightIndexes($fromIndex, $shift, $scope = null): void
219
    {
220 2
        $options = $this->getOptions();
221
222 2
        if (0 == $shift) {
223
            return;
224
        }
225
226 2
        $connection = $this->getConnection();
227
228 2
        $sql = $connection->createQueryBuilder();
229 2
        $sql->update($options->getTableName())
230 2
            ->set($options->getRightColumnName(), $options->getRightColumnName().' + :shift')
231 2
            ->where($options->getRightColumnName().' > :fromIndex');
232
233
        $params = array(
234 2
            ':shift' => $shift,
235 2
            ':fromIndex' => $fromIndex,
236
        );
237
238 2
        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...
239 1
            $sql->andWhere($options->getScopeColumnName().' = :scope');
240 1
            $params[':scope'] = $scope;
241
        }
242
243 2
        $connection->executeUpdate($sql->getSQL(), $params);
244 2
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249 1
    public function updateParentId($nodeId, $newParentId): void
250
    {
251 1
        $options = $this->getOptions();
252
253 1
        $connection = $this->getConnection();
254
255 1
        $sql = $connection->createQueryBuilder();
256 1
        $sql->update($options->getTableName())
257 1
            ->set($options->getParentIdColumnName(), ':parentId')
258 1
            ->where($options->getIdColumnName().' = :nodeId');
259
260
        $params = array(
261 1
            ':parentId' => $newParentId,
262 1
            ':nodeId' => $nodeId,
263
        );
264
265 1
        $connection->executeUpdate($sql->getSQL(), $params);
266 1
    }
267
268
    /**
269
     * {@inheritdoc}
270
     */
271 2
    public function updateLevels(int $leftIndexFrom, int $rightIndexTo, int $shift, $scope = null): void
272
    {
273 2
        $options = $this->getOptions();
274
275 2
        if (0 == $shift) {
276
            return;
277
        }
278
279 2
        $connection = $this->getConnection();
280
281 2
        $sql = $connection->createQueryBuilder();
282 2
        $sql->update($options->getTableName())
283 2
            ->set($options->getLevelColumnName(), $options->getLevelColumnName().' + :shift')
284 2
            ->where($options->getLeftColumnName().' >= :leftFrom'
285 2
                    .' AND '.$options->getRightColumnName().' <= :rightTo');
286
287
        $params = array(
288 2
            ':shift' => $shift,
289 2
            ':leftFrom' => $leftIndexFrom,
290 2
            ':rightTo' => $rightIndexTo,
291
        );
292
293 2
        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...
294 1
            $sql->andWhere($options->getScopeColumnName().' = :scope');
295 1
            $params[':scope'] = $scope;
296
        }
297
298 2
        $connection->executeUpdate($sql->getSQL(), $params);
299 2
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 2
    public function moveBranch(int $leftIndexFrom, int $rightIndexTo, int $shift, $scope = null): void
305
    {
306 2
        if (0 == $shift) {
307
            return;
308
        }
309
310 2
        $options = $this->getOptions();
311
312 2
        $connection = $this->getConnection();
313
314 2
        $sql = $connection->createQueryBuilder();
315 2
        $sql->update($options->getTableName())
316 2
            ->set($options->getLeftColumnName(), $options->getLeftColumnName().' + :shift')
317 2
            ->set($options->getRightColumnName(), $options->getRightColumnName().' + :shift')
318 2
            ->where($options->getLeftColumnName().' >= :leftFrom'
319 2
                    .' AND '.$options->getRightColumnName().' <= :rightTo');
320
321
        $params = array(
322 2
            ':shift' => $shift,
323 2
            ':leftFrom' => $leftIndexFrom,
324 2
            ':rightTo' => $rightIndexTo,
325
        );
326
327 2
        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...
328 1
            $sql->andWhere($options->getScopeColumnName().' = :scope');
329 1
            $params[':scope'] = $scope;
330
        }
331
332 2
        $connection->executeUpdate($sql->getSQL(), $params);
333 2
    }
334
335
    /**
336
     * {@inheritdoc}
337
     */
338 4
    public function getRoots($scope = null): array
339
    {
340 4
        $options = $this->getOptions();
341
342 4
        $connection = $this->getConnection();
343
344 4
        $sql = $this->getBlankDbSelect();
345 4
        $sql->where($options->getParentIdColumnName(true).' IS NULL');
346 4
        $sql->orderBy($options->getIdColumnName());
347
348 4
        $params = array();
349
350 4
        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...
351 1
            $sql->where($options->getScopeColumnName(true).' = :scope');
352 1
            $params[':scope'] = $scope;
353
        }
354
355 4
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
356
357 4
        $node = $stmt->fetchAll();
358
359 4
        return $node;
360
    }
361
362
    /**
363
     * {@inheritdoc}
364
     */
365 2
    public function getRoot($scope = null): array
366
    {
367 2
        $roots = $this->getRoots($scope);
368
369 2
        return ($roots) ? $roots[0] : array();
370
    }
371
372
    /**
373
     * {@inheritdoc}
374
     */
375 2
    public function getNode($nodeId): ?array
376
    {
377 2
        $options = $this->getOptions();
378
379 2
        $nodeId = (int) $nodeId;
380
381 2
        $connection = $this->getConnection();
382
383 2
        $sql = $this->getDefaultDbSelect();
384 2
        $sql->where($options->getIdColumnName(true).' = :id');
385
386
        $params = array(
387 2
            'id' => $nodeId,
388
        );
389
390 2
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
391
392 2
        $node = $stmt->fetch();
393
394 2
        return is_array($node) ? $node : null;
395
    }
396
397
    /**
398
     * {@inheritdoc}
399
     */
400 19
    public function getNodeInfo($nodeId): ?NodeInfo
401
    {
402 19
        $options = $this->getOptions();
403
404 19
        $connection = $this->getConnection();
405
406 19
        $sql = $this->getBlankDbSelect();
407 19
        $sql->where($options->getIdColumnName(true).' = :id');
408
409
        $params = array(
410 19
            'id' => $nodeId,
411
        );
412
413 19
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
414
415 19
        $node = $stmt->fetch();
416
417 19
        $data = is_array($node) ? $node : null;
418
419 19
        $result = ($data) ? $this->_buildNodeInfoObject($data) : null;
420
421 19
        return $result;
422
    }
423
424
    /**
425
     * {@inheritdoc}
426
     */
427 4
    public function getChildrenNodeInfo($parentNodeId): array
428
    {
429 4
        $connection = $this->getConnection();
430 4
        $options = $this->getOptions();
431
432 4
        $sql = $this->getBlankDbSelect();
433
434 4
        $sql = $sql->where($options->getParentIdColumnName(true).' = :parentId')
435 4
                   ->orderBy($options->getLeftColumnName(true), 'ASC');
436
437
        $params = array(
438 4
            'parentId' => $parentNodeId,
439
        );
440
441 4
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
442
443 4
        $data = $stmt->fetchAll();
444
445 4
        $result = array();
446
447 4
        foreach ($data as $nodeData) {
448 3
            $result[] = $this->_buildNodeInfoObject($nodeData);
449
        }
450
451 4
        return $result;
452
    }
453
454
    /**
455
     * {@inheritdoc}
456
     */
457 2
    public function updateNodeMetadata(NodeInfo $nodeInfo): void
458
    {
459 2
        $options = $this->getOptions();
460
461 2
        $connection = $this->getConnection();
462
463 2
        $sql = $connection->createQueryBuilder();
464 2
        $sql->update($options->getTableName())
465 2
            ->set($options->getRightColumnName(), (string) $nodeInfo->getRight())
466 2
            ->set($options->getLeftColumnName(), (string) $nodeInfo->getLeft())
467 2
            ->set($options->getLevelColumnName(), (string) $nodeInfo->getLevel())
468 2
            ->where($options->getIdColumnName().' = :nodeId');
469
470
        $params = array(
471 2
            ':nodeId' => $nodeInfo->getId(),
472
        );
473
474 2
        $connection->executeUpdate($sql->getSQL(), $params);
475 2
    }
476
477
    /**
478
     * {@inheritdoc}
479
     */
480 6
    public function getAncestors($nodeId, int $startLevel = 0, int $excludeLastNLevels = 0): array
481
    {
482 6
        $options = $this->getOptions();
483
484
        // node does not exist
485 6
        $nodeInfo = $this->getNodeInfo($nodeId);
486 6
        if (!$nodeInfo) {
487 1
            return array();
488
        }
489
490 5
        $connection = $this->getConnection();
491
492 5
        $sql = $this->getDefaultDbSelect();
493 5
        $params = array();
494
495 5
        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...
496 2
            $sql->andWhere($options->getScopeColumnName(true).' = :scope');
497 2
            $params['scope'] = $nodeInfo->getScope();
498
        }
499
500 5
        $sql->andWhere($options->getLeftColumnName(true).' <= :leftIndex')
501 5
            ->andWhere($options->getRightColumnName(true).' >= :rightIndex')
502 5
            ->orderBy($options->getLeftColumnName(true), 'ASC');
503
504 5
        $params['leftIndex'] = $nodeInfo->getLeft();
505 5
        $params['rightIndex'] = $nodeInfo->getRight();
506
507 5
        if (0 < $startLevel) {
508 2
            $sql->andWhere($options->getLevelColumnName(true).' >= :startLevel');
509
510 2
            $params['startLevel'] = $startLevel;
511
        }
512
513 5
        if (0 < $excludeLastNLevels) {
514 2
            $sql->andWhere($options->getLevelColumnName(true).' <= :level');
515
516 2
            $params['level'] = $nodeInfo->getLevel() - $excludeLastNLevels;
517
        }
518
519 5
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
520
521 5
        $result = $stmt->fetchAll();
522
523 5
        return (is_array($result)) ? $result : array();
524
    }
525
526
    /**
527
     * {@inheritdoc}
528
     */
529 9
    public function getDescendants($nodeId, int $startLevel = 0, ?int $levels = null, $excludeBranch = null): array
530
    {
531 9
        $options = $this->getOptions();
532
533 9
        if (!$nodeInfo = $this->getNodeInfo($nodeId)) {
534 1
            return array();
535
        }
536
537 8
        $connection = $this->getConnection();
538 8
        $sql = $this->getDefaultDbSelect();
539 8
        $sql->orderBy($options->getLeftColumnName(true), 'ASC');
540
541 8
        $params = array();
542
543 8
        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...
544 3
            $sql->andWhere($options->getScopeColumnName(true).' = :scope');
545 3
            $params['scope'] = $nodeInfo->getScope();
546
        }
547
548 8
        if (0 != $startLevel) {
549 3
            $sql->andWhere($options->getLevelColumnName(true).' >= :startLevel');
550
551 3
            $params['startLevel'] = $nodeInfo->getLevel() + $startLevel;
552
        }
553
554 8
        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...
555 2
            $sql->andWhere($options->getLevelColumnName(true).'< :endLevel');
556 2
            $params['endLevel'] = $nodeInfo->getLevel() + $startLevel + abs($levels);
557
        }
558
559 8
        if (null != $excludeBranch && null != ($excludeNodeInfo = $this->getNodeInfo($excludeBranch))) {
560 2
            $sql->andWhere('('.$options->getLeftColumnName(true).' BETWEEN :left AND :exLeftMinusOne'
561 2
                           .') OR ('.$options->getLeftColumnName(true).' BETWEEN :exRightPlusOne AND :right)')
562 2
                ->andWhere('('.$options->getRightColumnName(true).' BETWEEN :exRightPlusOne AND :right'
563 2
                           .') OR ('.$options->getRightColumnName(true).' BETWEEN :left AND :exLeftMinusOne)');
564
565 2
            $params['left'] = $nodeInfo->getLeft();
566 2
            $params['exLeftMinusOne'] = $excludeNodeInfo->getLeft() - 1;
567 2
            $params['exRightPlusOne'] = $excludeNodeInfo->getRight() + 1;
568 2
            $params['right'] = $nodeInfo->getRight();
569
        } else {
570 6
            $sql->andWhere($options->getLeftColumnName(true).' >= :left')
571 6
                ->andWhere($options->getRightColumnName(true).' <= :right');
572
573 6
            $params['left'] = $nodeInfo->getLeft();
574 6
            $params['right'] = $nodeInfo->getRight();
575
        }
576
577 8
        $stmt = $connection->executeQuery($sql->getSQL(), $params);
578
579 8
        $result = $stmt->fetchAll();
580
581 8
        return (0 < count($result)) ? $result : array();
582
    }
583
}
584