Completed
Push — develop ( 0a2cdf...062ac6 )
by Bartko
03:22
created

Doctrine2DBAL::lockTree()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
541
            $sql->andWhere($options->getLevelColumnName() . ' < :level');
542
543
            $params['level'] = $nodeInfo->getLevel();
544
        }
545
546
        $stmt = $connection->executeQuery($sql, $params);
547
548
        $result = $stmt->fetchAll();
549
550
        return (is_array($result)) ? $result : array();
551
    }
552
553
    public function getDescendants($nodeId = 1, $startLevel = 0, $levels = null, $excludeBranch = null)
554
    {
555
        $options = $this->getOptions();
556
557
        if (!$nodeInfo = $this->getNodeInfo($nodeId)) {
558
            return array();
559
        }
560
561
        $connection = $this->getConnection();
562
        $sql = $this->getDefaultDbSelect();
563
        $sql->orderBy($options->getLeftColumnName(), 'ASC');
564
565
        $params = array();
566
567
        if ($options->getScopeColumnName()) {
568
            $sql->andWhere($options->getScopeColumnName() . ' = :scope');
569
            $params['scope'] = $nodeInfo->getScope();
570
        }
571
572
        if (0 != $startLevel) {
573
            $sql->andWhere($options->getLevelColumnName() . ' >= :startLevel');
574
575
            $params['startLevel'] = $nodeInfo->getLevel() + (int) $startLevel;
576
        }
577
578
        if (null != $levels) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $levels of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
579
            $sql->andWhere($options->getLevelColumnName() . '< :endLevel');
580
            $params['endLevel'] = $nodeInfo->getLevel() + (int) $startLevel + abs($levels);
581
        }
582
583
        if (null != $excludeBranch && null != ($excludeNodeInfo = $this->getNodeInfo($excludeBranch))) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $excludeBranch of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
584
            $sql->andWhere('(' . $options->getLeftColumnName() . ' BETWEEN :left AND :exLeftMinusOne'
585
                . ') OR (' . $options->getLeftColumnName() . ' BETWEEN :exRightPlusOne AND :right)')
586
                ->andWhere('(' . $options->getRightColumnName() . ' BETWEEN :exRightPlusOne AND :right'
587
                    . ') OR (' . $options->getRightColumnName() . ' BETWEEN :left AND :exLeftMinusOne)');
588
589
            $params['left']           = $nodeInfo->getLeft();
590
            $params['exLeftMinusOne'] = $excludeNodeInfo->getLeft() - 1;
591
            $params['exRightPlusOne'] = $excludeNodeInfo->getRight() + 1;
592
            $params['right']          = $nodeInfo->getRight();
593
        } else {
594
            $sql->andWhere($options->getLeftColumnName() . ' >= :left')
595
                ->andWhere($options->getRightColumnName() . ' <= :right');
596
597
            $params['left']  = $nodeInfo->getLeft();
598
            $params['right'] = $nodeInfo->getRight();
599
        }
600
601
        $stmt = $connection->executeQuery($sql, $params);
602
603
        $result = $stmt->fetchAll();
604
605
        return (0 < count($result)) ? $result : array();
606
    }
607
}
608