Problem with a recursive menu builder...

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
sinecure
Forum Newbie
Posts: 1
Joined: Thu Aug 01, 2002 11:57 am
Location: Anchorage, AK, USA
Contact:

Problem with a recursive menu builder...

Post by sinecure »

As an effort to learn a bit about PHP and MySQL, I decided that a nice simple link database would be sufficient. Boy was I dumb. ;] Been banging my head against this one for long enough, I think I need other eyes to tell me what I'm doing wrong.

I've got a links database that I'm playing around with, each link category identified with a unique (auto_increment) id, and a field containing the id of it's parent (or NULL if it's at the bottom of the tree) category.

I pulled a three function 'menu' builder from http://www.evolt.org/article/BBS_style_ ... To/17/3962, and have been using that as my base.

So far, I've been successful in making it display in both definition lists, and select lists (forms) by passing variables. I select a category, and click edit, that reloads the page with the link category editor loaded up. However, on the link editor page, I can't make it select the parent of the link category that is being edited... sort of.

The following link categories exist in my database (test data - there are also descriptions, but that shouldn't apply as I'm not using them yet):

Code: Select all

cat_ID  p_cat_ID    category
1       NULL        'Zeitgeist'
2       1           'Earthquakes'
3       NULL        'Tiddly bits o info'
4       1           'Tsunamis'
5       1           'Meteor Strikes'
6       3           'Randomness'
7       1           'Moon Disappearances'
8       3           'Miscellany'
9       6           'Now it is getting silly.'
When I pull them from the database, query is:

Code: Select all

SELECT cat_ID, category, p_cat_ID FROM b2_linkcategories ORDER BY p_cat_ID, category
Now, the select list looks similar to this (I'm including the cat_ID in this illustration):

Code: Select all

cat_ID  menu entry
3       Tiddly bits o info
8          Miscellany
6          Randomness
9             Now it is getting silly.
1       Zeitgeist
2          Earthquakes
5          Meteor Strikes
7          Moon Disappearances
4          Tsunamis
If I select 'Zeitgeist', 'Tiddly bits o info', 'Miscellany' or 'Randomness' all works as it should (for the first two, nothing is selected in the 'sub-category of' drop-down box, and for the last two, 'Tiddy bits o info' is selected). If I select 'Now it is getting silly then the selected attribute is added to every option from 'Randomness' on down. If I select any of the subcategories of 'Zeitgeist', then the selected attributed is added to each option from 'Zeitgeist' on down.

This is frustrating, so if anyone could peruse the following code, and possibly tell me where my error is, I'd appreciate it.

Snippet from the form:

Code: Select all

<select name="link_cat_parent_ID">
            <option value="">
<?php
    echo "\t<option value="" . $result_array&#1111;0]&#1111;0] . """;
    if ($result_array&#1111;0]&#1111;0] == $link_cat_p_ID) echo " selected";
    echo ">";
    echo stripslashes($result_array&#1111;0]&#1111;1]) . "</option>\n";
    $selectedOnce = false;
    find_child($parentID, $startSeed, $depth, false, $link_cat_p_ID, "\t<option value="", "">", "", "</option>\n");
?>
        </select><br />
The file 'mainlinktree.php' that is included on the form page:

Code: Select all

<?php
    $q_tree = "SELECT cat_ID, category, p_cat_ID, description FROM $tablelinkcategories ORDER BY p_cat_ID, category";
    $r_tree = mysql_query($q_tree) or die("Couldn't query '" . $tablelinkcategories . "' for link category list.");
    $i = 0;
    while ($row_tree = mysql_fetch_row($r_tree)) &#123;
        $result_array&#1111;$i] = $row_tree;
        $i++;
    &#125;
    $parentID = $result_array&#1111;0]&#1111;0];
    $arr_size = count($result_array);
    $depth = 1;
    $startSeed = 1;
    $selectedOnce = false;

    function depth($depth) &#123;
        for ($i=1; $i<= $depth; ++$i) &#123;
            echo "&nbsp;&nbsp;&nbsp;";
        &#125;
    &#125;

/*
    Here's where the real meat of the program starts.
    It's actually the second of two functions that format
    our results. Because functions are defined before
    they are called in PHP it comes first in the script.
    You might want to read the description of find_child first!

    Okay, step-up is called by the find_child function
    when it cannot find any more children in a
    particular branch of categories.
*/
    function step_up ($parentID, $startSeed, $depth, $showdescr, $link_cat_p_ID, $befID, $aftID, $befCat, $aftCat, $befDescr, $aftDescr) &#123;
        global $result_array;
        global $arr_size;

        // The first thing we'll check is that $start_seed
        // hasn't gone off the end of our result_array.
        // If it has, we'll stop execution of this function.

        if ($startSeed > $arr_size) &#123;
            return;
        &#125; else &#123;
            // if the CatParent of the last category is
            // equal to the CatParent of the next category
            // then they are on the same level. We'll move
            // along to it and check to see if it has children
            // This was why we chose to order our results by
            // CatParent first in our SELECT statement.
            if ($result_array&#1111;$startSeed-1]&#1111;2] == $result_array&#1111;$startSeed]&#1111;2]) &#123;
                $depth--;

                return find_child($result_array&#1111;$startSeed-1]&#1111;2], $startSeed, $depth, $showdescr, $link_cat_p_ID, $befID, $aftID, $befCat, $aftCat, $befDescr, $aftDescr);

            // Otherwise, if the last CatParent was NULL and
            // the current is 1, we stop processing
            // as we have covered all the top level
            // (again, we know this thanks to our SELECT
            // statement ordering)
            &#125; else &#123;
                if ($result_array&#1111;$startSeed-1]&#1111;2] == "" &&
                    $result_array&#1111;$startSeed]&#1111;2] == 1) &#123;
                    return;
                &#125;
                // If they were unequal, and we haven't reached
                // the end of the list, we need to
                // climb back to this category's parent and
                // restart this checking process. More recursion!
                for ($j = 0; $j <= $arr_size; ++$j) &#123;
                    if ($result_array&#1111;$j]&#1111;0] == $result_array&#1111;$startSeed-1]&#1111;2]) &#123;
                        $depth--;
                        // end this definition list if this is the end of a branch
                        if ($showdescr) echo "</dl>\n";
                        return
                        step_up($result_array&#1111;$j+1]&#1111;0], $j+1, $depth, $showdescr, $link_cat_p_ID, $befID, $aftID, $befCat, $aftCat, $befDescr, $aftDescr);
                    &#125;
                &#125;
                // I've forgotten what this return does, but it's
                // probably something important!
                // Best leave it there :-)
                return;
            &#125;
        &#125;
    &#125;

/*
    The find_child function is the part of our recursive
    process that steps down the category list.
    It is fed the CatID of the last category we got
    from our results. This is the potential "parent"
    of other categories, so I have called it $parentID. It is
    also fed a $startSeed, which is the position of the current
    result we are looking at in $result_array. There's also a
    $depth variable which is used for formatting purposes.
*/
    function find_child ($parentID, $startSeed, $depth, $showdescr = true, $link_cat_p_ID = "0", $befID = "<dt>&#1111; ", $aftID = " ] ", $befCat = "<b>", $aftCat = "</b></dt>\n", $befDescr = "<dd>", $aftDescr = "</dd>\n") &#123;
        global $result_array;
        global $arr_size;
        global $selectedOnce;

        // This for loop starts us where we left off in
        // $result_array and steps through the results.
        for ($k = $startSeed; $k <= $arr_size; ++$k) &#123;
            // If we find a result where the catParent is
            // equal to the CatID of our current result
            // then we've found a child.
            if ($result_array&#1111;$k]&#1111;2] == $parentID) &#123;
                // make a new definition list if this is the start of a new branch
                if (($showdescr) && ($result_array&#1111;$k-1]&#1111;2] != $result_array&#1111;$k]&#1111;2])) echo "<dl>\n";
                // We can then do whatever we need to do
                // with our category.
                // In this case we'll print it out with some
                // depth formatting.

                if ((!$selectedOnce) && ($result_array&#1111;$k]&#1111;0] == $link_cat_p_ID)) &#123;
                    $aftID = substr($aftID, 0, -1) . " selected" . ">" . $selectedOnce;
                    $selectedOnce = true;
                &#125;
                echo $befID . $result_array&#1111;$k]&#1111;0] . $aftID;
                if (!$showdescr) depth($depth);
                echo $befCat . stripslashes($result_array&#1111;$k]&#1111;1]) . $aftCat;
                if ($showdescr) echo $befDescr . stripslashes($result_array&#1111;$k]&#1111;3]) . $aftDescr;
                // then we need to reset our startup variables - our
                // current result could now be a parent itself.
                $parentID = $result_array&#1111;$k]&#1111;0];
                $startSeed = ++$k;
                $depth++;
                // Finally we recall find_child with our new values
                // to see if this category contains sub-categories.
                // This is recursion at work!
                return find_child($parentID, $startSeed, $depth, $showdescr, $link_cat_p_ID, $befID, $aftID, $befCat, $aftCat, $befDescr, $aftDescr);

            &#125; elseif ($result_array&#1111;$k]&#1111;2] > $parentID) &#123;
                // Simply a little code to save some processing time.
                // We'll break out of the loop if we've gone
                // past the parent value
                break;
            &#125;
        &#125;
    /*
        If we didn't find a child, then we've gone as far down
        this particular branch of categories as we can.
        We need to keep our startup variables the same and
        use our second function - step_up to either find
        further results on the same level, or to take us
        back up the category tree.
    */
        step_up ($parentID, $startSeed, $depth, $showdescr, $link_cat_p_ID, $befID, $aftID, $befCat, $aftCat, $befDescr, $aftDescr);
    &#125;
?>
Well, that's it. Hopefully someone will say, 'You're a dumb-a**, here's your problem!' :?

Regards,
Post Reply