@@ -37,18 +37,7 @@ public function addLeaf(Leaf $parent, Leaf $child) {
3737 try {
3838 $ this ->connection ->beginTransaction ();
3939
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- }
40+ list ($ right , $ depth ) = $ this ->getInsertionPosition ($ parent );
5241
5342 // Move everything across two places.
5443 $ this ->connection ->executeUpdate ('UPDATE tree SET nested_right = nested_right + 2 WHERE nested_right > ? ' ,
@@ -183,7 +172,7 @@ public function deleteLeaf(Leaf $leaf) {
183172 $ this ->connection ->beginTransaction ();
184173
185174 // Delete the leaf.
186- $ this ->connection ->executeUpdate ('DELETE from tree WHERE nested_left = ? ' ,
175+ $ this ->connection ->executeUpdate ('DELETE FROM tree WHERE nested_left = ? ' ,
187176 [$ left ]
188177 );
189178
@@ -225,7 +214,7 @@ public function deleteSubTree(Leaf $leaf) {
225214 $ this ->connection ->beginTransaction ();
226215
227216 // Delete the leaf.
228- $ this ->connection ->executeUpdate ('DELETE from tree WHERE nested_left BETWEEN ? AND ? ' ,
217+ $ this ->connection ->executeUpdate ('DELETE FROM tree WHERE nested_left BETWEEN ? AND ? ' ,
229218 [$ left , $ right ]
230219 );
231220
@@ -249,8 +238,106 @@ public function deleteSubTree(Leaf $leaf) {
249238 /**
250239 * {@inheritdoc}
251240 */
252- public function moveSubTree (Leaf $ parent , Leaf $ leaf ) {
253- // TODO: Implement moveSubTree() method.
241+ public function moveSubTree ($ newLeftPostion , Leaf $ leaf ) {
242+
243+ try {
244+ // Calculate position adjustment variables.
245+ $ width = $ leaf ->getRight () - $ leaf ->getLeft () + 1 ;
246+ $ distance = $ newLeftPostion - $ leaf ->getLeft ();
247+ $ tempPos = $ leaf ->getLeft ();
248+
249+ $ this ->connection ->beginTransaction ();
250+
251+ // Calculate depth difference.
252+ $ newLeaf = $ this ->getLeafAtPosition ($ newLeftPostion );
253+ $ depthDiff = $ newLeaf ->getDepth () - $ leaf ->getDepth ();
254+
255+ // Backwards movement must account for new space.
256+ if ($ distance < 0 ) {
257+ $ distance -= $ width ;
258+ $ tempPos += $ width ;
259+ }
260+
261+ // Create new space for subtree.
262+ $ this ->connection ->executeUpdate ('UPDATE tree SET nested_left = nested_left + ? WHERE nested_left >= ? ' ,
263+ [$ width , $ newLeftPostion ]
264+ );
265+
266+ $ this ->connection ->executeUpdate ('UPDATE tree SET nested_right = nested_right + ? WHERE nested_right >= ? ' ,
267+ [$ width , $ newLeftPostion ]
268+ );
269+
270+ // Move subtree into new space.
271+ $ this ->connection ->executeUpdate ('UPDATE tree SET nested_left = nested_left + ?, nested_right = nested_right + ?, depth = depth + ? WHERE nested_left >= ? AND nested_right < ? ' ,
272+ [$ distance , $ distance , $ depthDiff , $ tempPos , $ tempPos + $ width ]
273+ );
274+
275+ // Remove old space vacated by subtree.
276+ $ this ->connection ->executeUpdate ('UPDATE tree SET nested_left = nested_left - ? WHERE nested_left > ? ' ,
277+ [$ width , $ leaf ->getRight ()]
278+ );
279+
280+ $ this ->connection ->executeUpdate ('UPDATE tree SET nested_right = nested_right - ? WHERE nested_right > ? ' ,
281+ [$ width , $ leaf ->getRight ()]
282+ );
283+ }
284+ catch (Exception $ e ) {
285+ $ this ->connection ->rollBack ();
286+ throw $ e ;
287+ }
288+
289+ }
290+
291+ /**
292+ * Determines if this leaf is a 'leaf', i.e. has no children.
293+ *
294+ * @param \PNX\Tree\Leaf $leaf
295+ * The leaf to check.
296+ *
297+ * @return bool
298+ * TRUE if there are no children. FALSE otherwise.
299+ */
300+ protected function isLeaf (Leaf $ leaf ) {
301+ return $ leaf ->getRight () - $ leaf ->getLeft () === 1 ;
302+ }
303+
304+ /**
305+ * {@inheritdoc}
306+ */
307+ public function getLeafAtPosition ($ left ) {
308+ $ result = $ this ->connection ->fetchAssoc ("SELECT id, revision_id, nested_left, nested_right, depth FROM tree WHERE nested_left = ? " ,
309+ [$ left ]
310+ );
311+ if ($ result ) {
312+ return new Leaf ($ result ['id ' ], $ result ['revision_id ' ], $ result ['nested_left ' ], $ result ['nested_right ' ], $ result ['depth ' ]);
313+ }
314+ }
315+
316+ /**
317+ * Gets the insertion position under the given parent.
318+ *
319+ * Takes into account if the parent has no children.
320+ *
321+ * @param \PNX\Tree\Leaf $parent
322+ * The parent leaf.
323+ *
324+ * @return int[]
325+ * The right and depth postiions.
326+ */
327+ protected function getInsertionPosition (Leaf $ parent ) {
328+ if ($ this ->isLeaf ($ parent )) {
329+ // We are on a leaf node.
330+ $ right = $ parent ->getLeft ();
331+ $ depth = $ parent ->getDepth () + 1 ;
332+ }
333+ else {
334+ // Find right most child.
335+ /** @var Leaf $rightChild */
336+ $ rightChild = $ this ->findRightMostChild ($ parent );
337+ $ right = $ rightChild ->getRight ();
338+ $ depth = $ rightChild ->getDepth ();
339+ }
340+ return [$ right , $ depth ];
254341 }
255342
256343}
0 commit comments