Completed
Push — master ( 2b0446...afde37 )
by Bartko
04:01 queued 01:40
created

Validator::_rebuild()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 16
cts 16
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 17
nc 6
nop 4
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace StefanoTree\NestedSet\Validator;
6
7
use Exception;
8
use StefanoTree\Exception\TreeIsBrokenException;
9
use StefanoTree\Exception\ValidationException;
10
use StefanoTree\NestedSet\Adapter\AdapterInterface;
11
use StefanoTree\NestedSet\NodeInfo;
12
13
class Validator implements ValidatorInterface
14
{
15
    private $adapter = null;
16
17
    /**
18
     * @param AdapterInterface $adapter
19
     */
20 8
    public function __construct(AdapterInterface $adapter)
21
    {
22 8
        $this->adapter = $adapter;
23 8
    }
24
25
    /**
26
     * @return AdapterInterface
27
     */
28 8
    private function getAdapter(): AdapterInterface
29
    {
30 8
        return $this->adapter;
31
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36 5
    public function isValid($rootNodeId): bool
37
    {
38 5
        $adapter = $this->getAdapter();
39
40 5
        $adapter->beginTransaction();
41
        try {
42 5
            $adapter->lockTree();
43
44 5
            $rootNodeInfo = $this->getAdapter()->getNodeInfo($rootNodeId);
45
46 5
            if (!$rootNodeInfo instanceof NodeInfo) {
47 1
                throw new ValidationException('Node does not exists.');
48
            }
49
50 4
            $this->_checkIfNodeIsRootNode($rootNodeInfo);
51 2
            $this->_rebuild($rootNodeInfo, true);
52
53 1
            $adapter->commitTransaction();
54 4
        } catch (TreeIsBrokenException $e) {
55 1
            $adapter->rollbackTransaction();
56
57 1
            return false;
58 3
        } catch (Exception $e) {
59 3
            $adapter->rollbackTransaction();
60 3
            throw $e;
61
        }
62
63 1
        return true;
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 3
    public function rebuild($rootNodeId): void
70
    {
71 3
        $adapter = $this->getAdapter();
72
73 3
        $adapter->beginTransaction();
74
        try {
75 3
            $adapter->lockTree();
76
77 3
            $rootNodeInfo = $this->getAdapter()->getNodeInfo($rootNodeId);
78
79 3
            if (!$rootNodeInfo instanceof NodeInfo) {
80 1
                throw new ValidationException('Node does not exists.');
81
            }
82
83 2
            $this->_checkIfNodeIsRootNode($rootNodeInfo);
84 1
            $this->_rebuild($rootNodeInfo);
85
86 1
            $adapter->commitTransaction();
87 2
        } catch (Exception $e) {
88 2
            $adapter->rollbackTransaction();
89 2
            throw $e;
90
        }
91 1
    }
92
93
    /**
94
     * @param NodeInfo $parentNodeInfo
95
     * @param bool     $onlyValidate
96
     * @param int      $left
97
     * @param int      $level
98
     *
99
     * @return int
100
     *
101
     * @throws TreeIsBrokenException if tree is broken and $onlyValidate is true
102
     */
103 3
    private function _rebuild(NodeInfo $parentNodeInfo, bool $onlyValidate = false, int $left = 1, int $level = 0): int
104
    {
105 3
        $adapter = $this->getAdapter();
106
107 3
        $right = $left + 1;
108
109 3
        $children = $adapter->getChildrenNodeInfo($parentNodeInfo->getId());
110
111 3
        foreach ($children as $childNode) {
112 3
            $right = $this->_rebuild($childNode, $onlyValidate, $right, $level + 1);
113
        }
114
115 3
        if ($parentNodeInfo->getLeft() != $left
116 1
            || $parentNodeInfo->getRight() != $right
117 3
            || $parentNodeInfo->getLevel() != $level) {
118 2
            $parentNodeInfo->setLeft($left);
119 2
            $parentNodeInfo->setRight($right);
120 2
            $parentNodeInfo->setLevel($level);
121
122 2
            if ($onlyValidate) {
123 1
                throw new TreeIsBrokenException();
124
            } else {
125 1
                $adapter->updateNodeMetadata($parentNodeInfo);
126
            }
127
        }
128
129 2
        return $right + 1;
130
    }
131
132
    /**
133
     * @param NodeInfo $node
134
     *
135
     * @throws ValidationException
136
     */
137 6
    private function _checkIfNodeIsRootNode(NodeInfo $node): void
138
    {
139 6
        if (null != $node->getParentId()) {
140 3
            throw new ValidationException('Given node is not root node.');
141
        }
142 3
    }
143
}
144