Skip to content

Commit 8bd7d6c

Browse files
committed
Adds delete leaf and not children
1 parent 32bce1d commit 8bd7d6c

File tree

3 files changed

+141
-48
lines changed

3 files changed

+141
-48
lines changed

src/NestedSetInterface.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ interface NestedSetInterface {
2020
*/
2121
public function addLeaf(Leaf $parent, Leaf $child);
2222

23+
/**
24+
* Deletes a leaf and moves descendants up a level.
25+
*
26+
* @param \PNX\Tree\Leaf $leaf
27+
* The leaf to delete.
28+
*/
29+
public function deleteLeaf(Leaf $leaf);
30+
2331
/**
2432
* Deletes a leaf and all it's descendants.
2533
*
@@ -53,7 +61,7 @@ public function findDescendants(Leaf $leaf, $depth = 0);
5361
public function findAncestors(Leaf $leaf);
5462

5563
/**
56-
* Gets a leaf with for the ID and Revision ID.
64+
* Gets a leaf for the ID and Revision ID.
5765
*
5866
* @param int|string $id
5967
* The ID.

src/Storage/DbalNestedSet.php

Lines changed: 94 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,49 +34,54 @@ public function __construct(Connection $connection) {
3434
*/
3535
public function addLeaf(Leaf $parent, Leaf $child) {
3636

37-
$this->connection->beginTransaction();
38-
39-
if ($parent->getRight() - $parent->getLeft() === 1) {
40-
// We are on a leaf node.
41-
$right = $parent->getLeft();
42-
$depth = $parent->getDepth() + 1;
43-
}
44-
else {
45-
// Find right most child.
46-
/** @var Leaf $rightChild */
47-
$rightChild = $this->findRightMostChild($parent);
48-
$right = $rightChild->getRight();
49-
$depth = $rightChild->getDepth();
50-
}
51-
52-
// Move everything across two places.
53-
$this->connection->executeUpdate('UPDATE tree SET nested_right = nested_right + 2 WHERE nested_right > ?',
54-
[$right]
55-
);
56-
$this->connection->executeUpdate('UPDATE tree SET nested_left = nested_left + 2 WHERE nested_left > ?',
57-
[$right]
58-
);
37+
try {
38+
$this->connection->beginTransaction();
5939

60-
// Create a new leaf object to be returned.
61-
$newLeaf = new Leaf(
62-
$child->getId(),
63-
$child->getRevisionId(),
64-
$right + 1,
65-
$right + 2,
66-
$depth
67-
);
40+
if ($parent->getRight() - $parent->getLeft() === 1) {
41+
// We are on a leaf node.
42+
$right = $parent->getLeft();
43+
$depth = $parent->getDepth() + 1;
44+
}
45+
else {
46+
// Find right most child.
47+
/** @var Leaf $rightChild */
48+
$rightChild = $this->findRightMostChild($parent);
49+
$right = $rightChild->getRight();
50+
$depth = $rightChild->getDepth();
51+
}
52+
53+
// Move everything across two places.
54+
$this->connection->executeUpdate('UPDATE tree SET nested_right = nested_right + 2 WHERE nested_right > ?',
55+
[$right]
56+
);
57+
$this->connection->executeUpdate('UPDATE tree SET nested_left = nested_left + 2 WHERE nested_left > ?',
58+
[$right]
59+
);
6860

69-
// Insert the new leaf.
70-
$this->connection->insert('tree', [
71-
'id' => $newLeaf->getId(),
72-
'revision_id' => $newLeaf->getRevisionId(),
73-
'nested_left' => $newLeaf->getLeft(),
74-
'nested_right' => $newLeaf->getRight(),
75-
'depth' => $newLeaf->getDepth(),
76-
]);
61+
// Create a new leaf object to be returned.
62+
$newLeaf = new Leaf(
63+
$child->getId(),
64+
$child->getRevisionId(),
65+
$right + 1,
66+
$right + 2,
67+
$depth
68+
);
7769

78-
$this->connection->commit();
70+
// Insert the new leaf.
71+
$this->connection->insert('tree', [
72+
'id' => $newLeaf->getId(),
73+
'revision_id' => $newLeaf->getRevisionId(),
74+
'nested_left' => $newLeaf->getLeft(),
75+
'nested_right' => $newLeaf->getRight(),
76+
'depth' => $newLeaf->getDepth(),
77+
]);
7978

79+
$this->connection->commit();
80+
}
81+
catch (Exception $e) {
82+
$this->connection->rollBack();
83+
throw $e;
84+
}
8085
return $newLeaf;
8186

8287
}
@@ -93,7 +98,9 @@ public function addLeaf(Leaf $parent, Leaf $child) {
9398
protected function findRightMostChild(Leaf $parent) {
9499
$result = $this->connection->fetchAssoc('SELECT id, revision_id, nested_left, nested_right, depth FROM tree WHERE nested_right = ? - 1',
95100
[$parent->getRight()]);
96-
return new Leaf($result['id'], $result['revision_id'], $result['nested_left'], $result['nested_right'], $result['depth']);
101+
if ($result) {
102+
return new Leaf($result['id'], $result['revision_id'], $result['nested_left'], $result['nested_right'], $result['depth']);
103+
}
97104
}
98105

99106
/**
@@ -129,7 +136,9 @@ public function getLeaf($id, $revision_id) {
129136
$result = $this->connection->fetchAssoc("SELECT id, revision_id, nested_left, nested_right, depth FROM tree WHERE id = ? AND revision_id = ?",
130137
[$id, $revision_id]
131138
);
132-
return new Leaf($id, $revision_id, $result['nested_left'], $result['nested_right'], $result['depth']);
139+
if ($result) {
140+
return new Leaf($id, $revision_id, $result['nested_left'], $result['nested_right'], $result['depth']);
141+
}
133142
}
134143

135144
/**
@@ -161,6 +170,49 @@ public function getTree() {
161170
return $tree;
162171
}
163172

173+
/**
174+
* {@inheritdoc}
175+
*/
176+
public function deleteLeaf(Leaf $leaf) {
177+
$left = $leaf->getLeft();
178+
$right = $leaf->getRight();
179+
$width = $right - $left + 1;
180+
181+
try {
182+
$this->connection->setAutoCommit(FALSE);
183+
$this->connection->beginTransaction();
184+
185+
// Delete the leaf.
186+
$this->connection->executeUpdate('DELETE from tree WHERE nested_left = ?',
187+
[$left]
188+
);
189+
190+
// Move children up a level.
191+
$this->connection->executeUpdate('UPDATE tree SET nested_right = nested_right - 1, nested_left = nested_left - 1, depth = depth -1 WHERE nested_left BETWEEN ? AND ?',
192+
[$left, $right]
193+
);
194+
195+
// Move everything back two places.
196+
$this->connection->executeUpdate('UPDATE tree SET nested_right = nested_right - 2 WHERE nested_right > ?',
197+
[$right]
198+
);
199+
$this->connection->executeUpdate('UPDATE tree SET nested_left = tree.nested_left - 2 WHERE nested_left > ?',
200+
[$right]
201+
);
202+
203+
$this->connection->commit();
204+
205+
}
206+
catch (Exception $e) {
207+
$this->connection->rollBack();
208+
throw $e;
209+
}
210+
finally {
211+
$this->connection->setAutoCommit(TRUE);
212+
}
213+
214+
}
215+
164216
/**
165217
* {@inheritdoc}
166218
*/
@@ -172,6 +224,7 @@ public function deleteLeafAndDescendants(Leaf $leaf) {
172224
try {
173225
$this->connection->beginTransaction();
174226

227+
// Delete the leaf.
175228
$this->connection->executeUpdate('DELETE from tree WHERE nested_left BETWEEN ? AND ?',
176229
[$left, $right]
177230
);

tests/Functional/DbalNestedSetTest.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,28 +180,60 @@ public function testAddLeafWithNoChildren() {
180180
$this->assertEquals(8, $newLeaf->getRight());
181181
$this->assertEquals(4, $newLeaf->getDepth());
182182

183-
$tree = $this->nestedSet->getTree();
184-
$this->printTree($tree);
185-
186183
// Parent leaf right should have incremented.
187184
$newParent = $this->nestedSet->getLeaf(6, 1);
188185
$this->assertEquals(6, $newParent->getLeft());
189186
$this->assertEquals(9, $newParent->getRight());
190187
}
191188

192189
/**
193-
* Tests deleting a leaf and its descendants.
190+
* Tests deleting a leaf.
194191
*/
195-
public function testDeleteNodeAndDescendants() {
192+
public function testDeleteLeaf() {
193+
$leaf = $this->nestedSet->getLeaf(4, 1);
194+
195+
$this->nestedSet->deleteLeaf($leaf);
196196

197+
// Leaf should be deleted.
197198
$leaf = $this->nestedSet->getLeaf(4, 1);
199+
$this->assertNull($leaf);
198200

199-
$this->nestedSet->deleteLeafAndDescendants($leaf);
201+
// Children should be moved up.
202+
$leaf = $this->nestedSet->getLeaf(5, 1);
203+
$this->assertEquals(3, $leaf->getLeft());
204+
$this->assertEquals(4, $leaf->getRight());
205+
$this->assertEquals(2, $leaf->getDepth());
206+
207+
$leaf = $this->nestedSet->getLeaf(6, 1);
208+
$this->assertEquals(5, $leaf->getLeft());
209+
$this->assertEquals(6, $leaf->getRight());
210+
$this->assertEquals(2, $leaf->getDepth());
200211

201212
$tree = $this->nestedSet->getTree();
202213
$this->printTree($tree);
203214
}
204215

216+
/**
217+
* Tests deleting a leaf and its descendants.
218+
*/
219+
public function testDeleteLeafAndDescendants() {
220+
221+
$leaf = $this->nestedSet->getLeaf(4, 1);
222+
223+
$this->nestedSet->deleteLeafAndDescendants($leaf);
224+
225+
// Leaf should be deleted.
226+
$leaf = $this->nestedSet->getLeaf(4, 1);
227+
$this->assertNull($leaf);
228+
229+
// Children should be deleted.
230+
$leaf = $this->nestedSet->getLeaf(5, 1);
231+
$this->assertNull($leaf);
232+
233+
$leaf = $this->nestedSet->getLeaf(6, 1);
234+
$this->assertNull($leaf);
235+
}
236+
205237
/**
206238
* Creates the table.
207239
*/

0 commit comments

Comments
 (0)