CREATE TABLE categories ( category_id int(11) NOT NULL auto_increment, name varchar(20) NOT NULL, left_node int(11) NOT NULL, right_node int(11) NOT NULL, PRIMARY KEY (category_id) ); CREATE TABLE products ( product_id int(11) NOT NULL auto_increment, name varchar(40) default NULL, category_id int(11) NOT NULL, PRIMARY KEY (product_id) );
class hierachy{
public function fullTree($parent){
$stmt = conn::getInstance()->prepare("
SELECT node.name
FROM categories AS node,
categories AS parent
WHERE node.left_node
BETWEEN parent.left_node AND parent.right_node
AND parent.name = :parent
ORDER BY node.left_node");
$stmt->bindParam('parent', $parent);
$stmt->execute();
$res = $stmt->fetchALL(PDO::FETCH_ASSOC);
return $res;
}
}
/*** a new hierachy instance ***/
$hierachy = new hierachy;
$iterator = new RecursiveIteratorIterator(
new recursiveArrayIterator($hierachy->fullTree('electronics')));
try
{
foreach($iterator as $key=>$value)
{
echo $value.'
';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
You could change the parent to televisions, and get a result like this.
televisions
public function leafNodes(){
$stmt = conn::getInstance()->prepare("
SELECT name
FROM categories
WHERE right_node = left_node + 1
");
$stmt->execute();
return $stmt->fetchALL(PDO::FETCH_ASSOC);
}
$iterator = new RecursiveIteratorIterator(
new recursiveArrayIterator($hierachy->leafNodes()));
try
{
foreach($iterator as $key=>$value)
{
echo $value.'
';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
public function singlePath($node_name){
$stmt = conn::getInstance()->prepare("
SELECT parent.name
FROM categories AS node,
categories AS parent
WHERE node.left_node
BETWEEN parent.left_node AND parent.right_node
AND node.name = '{$node_name}'
ORDER BY node.left_node
");
$stmt->execute();
return $stmt->fetchALL(PDO::FETCH_ASSOC);
electronics
portable electronics
mp3 players
flash
public function getNodeDepth(){
$stmt = conn::getInstance()->prepare("
SELECT node.name,
COUNT(parent.name) - 1)
AS depth
FROM categories AS node,
categories AS parent
WHERE node.left_node BETWEEN parent.left_node AND parent.right_node
GROUP BY node.name
ORDER BY node.left_node");
$stmt->execute();
return $stmt->fetchALL(PDO::FETCH_ASSOC);
}
$iterator = new RecursiveIteratorIterator(
new recursiveArrayIterator($hierachy->getNodeDepth()));
try
{
foreach($iterator as $key=>$value)
{
echo $key.' -- '.$value.'
';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
public function getLocalSubNodes($node_name){
$stmt = conn::getInstance()->prepare("SELECT node.name,
(COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM categories AS node,
categories AS parent,
categories AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM categories AS node,
categories AS parent
WHERE node.left_node BETWEEN parent.left_node AND parent.right_node
AND node.name = :node_name
GROUP BY node.name
ORDER BY node.left_node
)AS sub_tree
WHERE node.left_node BETWEEN parent.left_node AND parent.right_node
AND node.left_node BETWEEN sub_parent.left_node AND sub_parent.right_node
AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.left_node");
$stmt-&ft;bindParam(':node_name', $node_name, PDO::PARAM_STR);
$stmt->execute();
return $stmt->fetchALL(PDO::FETCH_ASSOC);
}
$iterator = new RecursiveIteratorIterator(
new recursiveArrayIterator($hierachy->getLocalSubNodes('portable electronics')));
try {
foreach($iterator as $key=>$value)
{
echo $key.' -- '.$value.'
';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
public function productCount(){
$stmt = conn::getInstance()->prepare("
SELECT parent.name,
COUNT(products.name) AS product_count
FROM categories AS node ,
categories AS parent,
products
WHERE node.left_node BETWEEN parent.left_node AND parent.right_node
AND node.category_id = products.category_id
GROUP BY parent.name
ORDER BY node.left_node ");
$stmt->execute();
return $stmt->fetchALL(PDO::FETCH_ASSOC);
}
$iterator = new RecursiveIteratorIterator(
new recursiveArrayIterator($hierachy->productCount()));
try
{
foreach($iterator as $key=>$value)
{
echo $key.' -- '.$value.'
';
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
public function addNode($left_node, $new_node){
try {
conn::getInstance()->beginTransaction();
$stmt = conn::getInstance()->prepare("SELECT @myRight := right_node FROM categories WHERE name = :left_node");
$stmt->bindParam(':left_node', $left_node);
$stmt->execute();
/*** increment the nodes by two ***/
conn::getInstance()->exec("UPDATE categories SET right_node = right_node + 2 WHERE right_node > @myRight");
conn::getInstance()->exec("UPDATE categories SET left_node = left_node + 2 WHERE left_node > @myRight");
/*** insert the new node ***/
$stmt = conn::getInstance()->prepare("INSERT INTO categories(name, left_node, right_node) VALUES(:new_node, @myRight + 1, @myRight + 2)");
$stmt->bindParam(':new_node', $new_node);
$stmt->execute();
/*** commit the transaction ***/
conn::getInstance()->commit();
}
catch(Exception $e)
{
conn::getInstance()->rollBack();
throw new Exception($e);
}
}
public function addChildNode($node_name, $new_node){
try {
conn::getInstance()->beginTransaction();
$stmt = conn::getInstance()->prepare("SELECT @myLeft := left_node FROM categories WHERE name=:node_name");
$stmt->bindParam(':node_name', $node_name);
$stmt->execute();
conn::getInstance()->exec("
UPDATE categories SET right_node = right_node + 2
WHERE right_node > @myLeft");
conn::getInstance()->exec("
UPDATE categories SET left_node = left_node + 2
WHERE left_node > @myLeft");
$stmt = conn::getInstance()->prepare("
INSERT INTO categories(name, left_node, right_node)
VALUES(:new_node, @myLeft + 1, @myLeft + 2)");
$stmt->bindParam(':new_node', $new_node);
$stmt->execute();
conn::getInstance()->commit();
}
catch(Exception $e)
{
conn::getInstance()->rollBack();
throw new Exception($e);
}
}
public function deleteLeafNode($node_name){
try {
conn::getInstance()->beginTransaction();
$stmt = conn::getInstance()->prepare("SELECT @myLeft := left_node, @myRight := right_node, @myWidth := right_node - left_node + 1 FROM categories WHERE name = :node_name");
$stmt->bindParam(':node_name', $node_name);
$stmt->execute();
conn::getInstance()->exec("DELETE FROM categories WHERE left_node BETWEEN @myLeft AND @myRight;");
conn::getInstance()->exec("UPDATE categories SET right_node = right_node - @myWidth WHERE right_node > @myRight");
conn::getInstance()->exec("UPDATE categories SET left_node = left_node - @myWidth WHERE left_node > @myRight");
conn::getInstance()->commit();
}
catch(Exception $e)
{
conn::getInstance()->rollBack();
throw new Exception($e);
}
}
public function deleteNodeRecursive($node_name){
try
{
conn::getInstance()->beginTransaction();
$stmt = conn::getInstance()->prepare("
SELECT @myLeft := left_node,
@myRight := right_node,
@myWidth := right_node - left_node + 1
FROM categories WHERE name = :node_name");
$stmt->bindParam(':node_name', $node_name);
$stmt->execute();
conn::getInstance()->exec("DELETE FROM categories WHERE left_node BETWEEN @myLeft AND @myRight");
conn::getInstance()->exec("UPDATE categories SET right_node = right_node - @myWidth WHERE right_node > @myRight");
conn::getInstance()->exec("UPDATE categories SET left_node = left_node - @myWidth WHERE left_node > @myRight");
conn::getInstance()->commit();
}
catch(Exception $e)
{
conn::getInstance()->rollBack();
throw new Exception($e);
}
}