3

I am trying to create a way to retrieve and display an endless amount of nested categories.

Table Example

Now I could select them like this (not tested, not important):

$resulttable1 = mysql_query("SELECT name, id FROM categories WHERE childof=0");
while($rowtable1 = mysql_fetch_array($resulttable1)){ 
$cat1 = $rowtable['name']; 

$resulttable2 = mysql_query("SELECT name, id FROM categories WHERE childof=$rowtable1[id]");
while($rowtable2 = mysql_fetch_array($resulttable2)){ 
$cat2 = $rowtable2['name']; 

$resulttable3 = mysql_query("SELECT name, id FROM categories WHERE childof=$rowtable2[id]");
while($rowtable3 = mysql_fetch_array($resulttable3)){ 
$cat3 = $rowtable3['name'];
}
}
}

But what if a user wants more than 3 "levels" of nesting? How can I make the mysql SELECT in a way that it retrieves an endless amount of nested categories?

UPDATE: Ok, using the link paul provided I got it done like this:

endless nesting mysql

It's not really important how the lft and rgt fields work as they get updated automatically when you insert and remove categories. It's interesting to find out, though. Also, the first entry is a static value. It's basically just the start of the tree, just leave it in as-is. My script below doesn't echo it based on it's title "products".

<?php
include_once("config.php");

display_tree('products');

function display_tree($root) {  

echo'<table>';

    // retrieve the left and right value of the $root node  
    $result = mysql_query('SELECT lft, rgt FROM tree '.  
                           'WHERE title="'.$root.'";');  
    $row = mysql_fetch_array($result);  

    // start with an empty $right stack  
    $right = array();  

    // now, retrieve all descendants of the $root node  
    $result = mysql_query('SELECT title, lft, rgt FROM tree '.  
                           'WHERE lft BETWEEN '.$row['lft'].' AND '.  
                           $row['rgt'].' ORDER BY lft ASC;');  

    // display each row  
    while ($row = mysql_fetch_array($result)) {
        // only check stack if there is one  
        if (count($right)>0) {  
            // check if we should remove a node from the stack  
            while ($right[count($right)-1]<$row['rgt']) {  
                array_pop($right);  
            }  
        }  
        // display indented node title  
        $repeatamount = (count($right)) - 1;
        if($repeatamount < 0){ $repeatamount = 0; }

        if($row['title'] != 'products'){
        echo'<tr>';
        echo "<td>".str_repeat('-->',$repeatamount ).$row['title']."</td>";  
        echo'</tr>';
        }

        // add this node to the stack  
        $right[] = $row['rgt'];  
    }
echo'</table>';
}
?>

Which will display something like:

example

And this is an example for inserting a new category: addnew.php?parent=3&title=Shooter which would add the category "Shooter" under "PC" (which is in turn under "Games").

<?php
include_once("config.php");

if(isset($_GET['parent']) && is_numeric($_GET['parent']) && isset($_GET['title']))
{
    $parent = $_GET['parent'];
    $title = mysql_real_escape_string($_GET['title']);
    mysql_query("INSERT INTO tree (parent, title) VALUES ('$parent', '$title')");
    rebuild_tree(0, 0);
}

function rebuild_tree($parent, $left) {     
        // the right value of this node is the left value + 1     
        $right = $left+1;     

        // get all children of this node     
        $result = mysql_query('SELECT id FROM tree '.     
                               'WHERE parent="'.$parent.'";');     
        while ($row = mysql_fetch_array($result)) {     
            // recursive execution of this function for each     
            // child of this node     
            // $right is the current right value, which is     
            // incremented by the rebuild_tree function     
            $right = rebuild_tree($row['id'], $right);     
        }     

        // we've got the left value, and now that we've processed     
        // the children of this node we also know the right value     
        mysql_query('UPDATE tree SET lft='.$left.', rgt='.     
                     $right.' WHERE id="'.$parent.'";');     

        // return the right value of this node + 1     
        return $right+1;     
    }

    ?>  

I hope that helps anyone else looking for the same thing.

natli
  • 3,782
  • 11
  • 51
  • 82
  • You are probably looking for [Recursion](http://en.wikipedia.org/wiki/Recursion). – hakre Oct 04 '11 at 10:16
  • 1
    See http://stackoverflow.com/questions/679431/mysql-possible-nested-category-problem and http://stackoverflow.com/questions/609328/mysql-query-for-selecting-children/609924 – Lepidosteus Oct 04 '11 at 10:17
  • Nice work, glad to see you got it working! – Paul Oct 05 '11 at 11:10

1 Answers1

0

If you use childof = 0 to show that the category is a child of no-one you would be able to go backwards until you reached 0. So starting at:

id  name           childof
7   Motor-Racing   6

You would have a while loop that would continue until you got a rowtable['childof'] value of 0.

Paul
  • 6,572
  • 2
  • 39
  • 51
  • Good observation, that should work. But isn't there a more "official" way of doing this? Like [common table expression](http://stackoverflow.com/questions/3004337/multilevel-nested-product-categories-display-with-asp-net-and-sql-server) for asp.net ? – natli Oct 04 '11 at 10:21
  • @natli: I think you mean MS SQL. You can use MS SQL with PHP, too. – hakre Oct 04 '11 at 10:24
  • It gets complicated, but for serious hierarchical data - [Modified Preorder Tree Traversal](http://www.sitepoint.com/hierarchical-data-database-2/) – Paul Oct 04 '11 at 10:25
  • @Paul: I think the OP is looking for a solution to do this within MySQL, not within PHP. – hakre Oct 04 '11 at 10:44
  • @hakre yes, good point. MPTT is good at returning hierarchical data though. – Paul Oct 04 '11 at 11:05