ソースを参照

added children method

also,first attempt of moveUp()
QuickApps 12 年 前
コミット
eb97bbccc8

+ 145 - 0
src/Model/Behavior/TreeBehavior.php

@@ -96,6 +96,151 @@ class TreeBehavior extends Behavior {
 		return ($node->{$right} - $node->{$left} - 1) / 2;
 	}
 
+	public function children($id, $direct = false, $fields = [], $order = null, $limit = null, $page = 1) {
+		extract($this->config());
+		$primaryKey = $this->_table->primaryKey();
+
+		if ($direct) {
+			return $this->_scope($this->_table->find())
+				->where([$parent => $id])
+				->all();
+		}
+
+		$node = $this->_scope($this->_table->find())
+			->select([$right, $left])
+			->where([$primaryKey => $id])
+			->first();
+
+		if (!$node) {
+			return false;
+		}
+
+		$order = !$order ? [$left => 'ASC'] : $order;
+		$query = $this->_scope($this->_table->find());
+
+		if ($fields) {
+			$query->select($fields);
+		}
+
+		$query->where([
+			"{$right} <" => $node->{$right},
+			"{$left} >" => $node->{$left}
+		]);
+
+		if ($limit) {
+			$query->limit($limit);
+		}
+
+		if ($page) {
+			$query->page($page);
+		}
+
+		return $query->order($order)->all();
+	}
+
+	public function moveUp($id, $number = 1) {
+		$primaryKey = $this->_table->primaryKey();
+		$config = $this->config();
+		extract($config);
+
+		if (!$number) {
+			return false;
+		}
+
+		$node = $this->_scope($this->_table->find())
+			->select([$primaryKey, $parent, $left, $right])
+			->where([$primaryKey => $id])
+			->first();
+
+		if ($node->{$parent}) {
+			$parentNode = $this->_scope($this->_table->find())
+				->select([$primaryKey, $left, $right])
+				->where([$primaryKey => $node->{$parent}])
+				->first();
+
+			if (($node->{$left} - 1) == $parentNode->{$left}) {
+				return false;
+			}
+		}
+
+		$previousNode = $this->_scope($this->_table->find())
+			->select([$primaryKey, $left, $right])
+			->where([$right => ($node->{$left} - 1)]);
+
+		$previousNode = $previousNode->first();
+
+		if (!$previousNode) {
+			return false;
+		}
+
+		$edge = $this->_getMax();
+		$this->_sync($edge - $previousNode->{$left} + 1, '+', "BETWEEN {$previousNode->{$left}} AND {$previousNode->{$right}}");
+		$this->_sync($node->{$left} - $previousNode->{$left}, '-', "BETWEEN {$node->{$left}} AND {$node->{$right}}");
+		$this->_sync($edge - $previousNode->{$left} - ($node->{$right} - $node->{$left}), '-', "> {$edge}");
+
+		$number--;
+
+		if ($number) {
+			$this->moveUp($id, $number);
+		}
+
+		return true;
+	}
+
+	protected function _getMax() {
+		return $this->__getMaxOrMin('max');
+	}
+
+	protected function _getMin() {
+		return $this->__getMaxOrMin('min');
+	}
+
+/**
+ * Get the maximum index value in the table.
+ *
+ * @return integer
+ */
+	private function __getMaxOrMin($maxOrMin = 'max') {
+		extract($this->config());
+		$LorR = $maxOrMin == 'max' ? $right : $left;
+		$DorA = $maxOrMin == 'max' ? 'DESC' : 'ASC';
+
+		$edge = $this->_scope($this->_table->find())
+			->select([$LorR])
+			->order([$LorR => $DorA])
+			->first();
+
+		if (empty($edge->{$LorR})) {
+			return 0;
+		}
+
+		return $edge->{$LorR};
+	}
+
+	protected function _sync($shift, $dir = '+', $conditions = null, $field = 'both') {
+		extract($this->config());
+
+		if ($field === 'both') {
+			$this->_sync($shift, $dir, $conditions, $left);
+			$field = $right;
+		}
+
+		// updateAll + scope
+		$query = $this->_scope($this->_table->query());
+		$query->update()
+			->set([$field => "{$field} {$dir} {$shift}"]);
+
+		if ($conditions) {
+			$conditions = "{$field} {$conditions}";
+			$query->where($conditions);
+		}
+
+		$statement = $query->execute();
+		$success = $statement->rowCount() > 0;
+
+		return $success;
+	}
+
 	protected function _scope($query) {
 		$config = $this->config();
 

+ 55 - 1
tests/TestCase/Model/Behavior/TreeBehaviorTest.php

@@ -63,7 +63,7 @@ class TreeBehaviorTest extends TestCase {
 
 		$nodes = $this->table->find('path', ['for' => 1]);
 		$this->assertEquals([1], $nodes->extract('id')->toArray());
-		
+
 		// find path with scope
 		$table = TableRegistry::get('MenuLinkTrees');
 		$table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
@@ -119,4 +119,58 @@ class TreeBehaviorTest extends TestCase {
 		$count = $table->childCount(1, false);
 		$this->assertEquals(4, $count);
 	}
+
+/**
+ * Tests the children() method
+ *
+ * @return void
+ */
+	public function testChildren() {
+		$table = TableRegistry::get('MenuLinkTrees');
+		$table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
+
+		// root
+		$nodeIds = [];
+		foreach ($table->children(1) as $node) {
+			$nodeIds[] = $node->id;
+		}
+		$this->assertEquals([2, 3, 4, 5], $nodeIds);
+
+		// unexisting node
+		$this->assertEquals(false, $table->children(500));
+
+		// leaf
+		$nodeIds = [];
+		foreach ($table->children(5) as $node) {
+			$nodeIds[] = $node->id;
+		}
+		$this->assertEquals(0, count($nodeIds));
+	}
+
+/**
+ * Tests the moveUp() method
+ *
+ * @return void
+ */
+	public function testMoveUp() {
+		$table = TableRegistry::get('MenuLinkTrees');
+		$table->addBehavior('Tree', ['scope' => ['menu' => 'main-menu']]);
+
+		// top level, wont move
+		$this->assertEquals(false, $this->table->moveUp(1, 10));
+
+		// edge cases
+		$this->assertEquals(false, $this->table->moveUp(1, 0));
+		$this->assertEquals(false, $this->table->moveUp(1, -10));
+
+		// move inner node
+		$nodeIds = [];
+		$result = $table->moveUp(3, 1);
+		foreach ($table->children(1) as $node) {
+			$nodeIds[] = $node->id;
+		}
+
+		$this->assertEquals([3, 4, 5, 2], $nodeIds);
+		$this->assertEquals(true, $result);
+	}
 }