0

I have product categories, that I have a built a navigation for. However, some of the categories have sub-categories, and some of the categories have sub-sub-categories, and so forth, perhaps as deep as sub-sub-sub-sub-sub-categories.

I want a nice tidy navigation that shows the structure like this:

main-category (parent)
-sub-category
-sub-category (parent)
--sub-sub-category
--sub-sub-category
--sub-sub-category(current category)
--sub-sub-category
-sub-category
-sub-category
main-category

So essentially you can view all other child categories, for the current category and parent categories. I have achieved this with a lot of code, but I am sure it could be done with a recursive function.

The information I have to build the navigation is an array of all the categories, starting with the main category, and going to child to child from there.

here is my current code:

$structure = array( [0] => 0 [1] => 72 [2] => 51); // example
$navigation = '<ul id="category_side_nav">';
$qry0 = mysqli_query($con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$structure[0]." AND `categories_status`=1 ORDER BY `sort_order`");
while($row0 = mysqli_fetch_assoc($qry0)) {
    $navigation .= '<li class="navigation0"><a class="';
    if((isset($structure[1]) && $row0['categories_id'] == $structure[1]) || $row0['categories_id']==$id) $navigation .= 'current_navigation';
    else $navigation .= 'other_navigation';
    $navigation .= '" href="'.$base.$row0['categories_url'].'/">'.ucwords($row0['categories_name']).'</a></li>';
    if((isset($structure[1]) && $row0['categories_id'] == $structure[1]) || $row0['categories_id'] == $id) {  
        $cat1 = preg_replace("/_c[0-9_]+/", "", $row0['categories_url']).'/';
        $qry1 = mysqli_query($con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$row0['categories_id']." AND `categories_status`=1 ORDER BY `sort_order`");
        while($row1 = mysqli_fetch_assoc($qry1)) {  
            $navigation .= '<li class="navigation1"><a class="';
            if((isset($structure[2]) && $row1['categories_id'] == $structure[2]) || $row1['categories_id']==$id) $navigation .= ' current_navigation';
            else $navigation .= 'other_navigation';
            $navigation .= '" href="'.$base.$cat1.$row1['categories_url'].'/">'.ucwords($row1['categories_name']).'</a></li>';
            if((isset($structure[2]) && $row1['categories_id'] == $structure[2]) || $row1['categories_id'] == $id) {
                $cat2 = $cat1.preg_replace("/_c[0-9_]+/", "", $row1['categories_url']).'/';
                $qry2 = mysqli_query($con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$row1['categories_id']." AND `categories_status`=1 ORDER BY `sort_order`");
                while($row2 = mysqli_fetch_assoc($qry2)) {
                    $navigation .= '<li class="navigation2"><a class="';
                    if((isset($structure[3]) && $row2['categories_id'] == $structure[3]) || $row2['categories_id']==$id) $navigation .= ' current_navigation';
                    else $navigation .= 'other_navigation';
                    $navigation .= '" href="'.$base.$cat2.$row2['categories_url'].'/">'.ucwords($row2['categories_name']).'</a></li>';
                    if((isset($structure[3]) && $row2['categories_id'] == $structure[3]) || $row2['categories_id'] == $id) {
                        $cat3 = $cat2.preg_replace("/_c[0-9_]+/", "", $row2['categories_url']).'/';
                        $qry3 = mysqli_query($con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$row2['categories_id']." AND `categories_status`=1 ORDER BY `sort_order`");  
                        while($row3 = mysqli_fetch_assoc($qry3)) {
                            $navigation .= '<li class="navigation3"><a class="';
                            if((isset($structure[4]) && $row3['categories_id'] == $structure[4]) || $row3['categories_id']==$id) $navigation .= ' current_navigation';  
                            else $navigation .= 'other_navigation';                         
                            $navigation .= '" href="'.$base.$cat3.$row3['categories_url'].'/">'.ucwords($row3['categories_name']).'</a></li>';
                            if((isset($structure[4]) && $row3['categories_id'] == $structure[4]) || $row3['categories_id'] == $id) {
                                $cat4 = $cat3.preg_replace("/_c[0-9_]+/", "", $row3['categories_url']).'/';
                                $qry4 = mysqli_query($con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$row3['categories_id']." AND `categories_status`=1 ORDER BY `sort_order`");  
                                while($row4 = mysqli_fetch_assoc($qry4)) {
                                    $navigation .= '<li class="navigation4"><a class="';
                                    if((isset($structure[5]) && $row4['categories_id'] == $structure[5]) || $row4['categories_id']==$id) $navigation .= ' current_navigation';  
                                    else $navigation .= 'other_navigation';
                                    $navigation .= '" href="'.$base.$cat4.$row4['categories_url'].'/">'.ucwords($row4['categories_name']).'</a></li>';                              
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
$navigation .= '</ul>';

The database only has category_id, and parent_id.

I couldnt create recursive function, because I needed to know the next entry in the array, so I could style the navigation accordingly, and that got me really stuck.

Can anyone help me turn this into a recursive function or at least some functions to make it tidier with less code.

// UPDATE

Ok I tried to think of a different way by incrementing the array like this, its a bit buggy though, but might be the way to proceed, if anyone has any ideas thinking more like this

function nav() {
    $cat_level = structure($this->id);
    $this->cats = array_reverse($cat_level);

    $nav = '<ul id="category_side_nav">';

    $nav .= $this->structure(0,BASE);

    $navigation .= '</ul>';
    return $navigation; 

}

function structure($i,$url) {
    if($i==0) $nav = '';
    $qry = mysqli_query($this->con,"SELECT `categories_id`,`categories_name`,`categories_url` FROM `categories` WHERE `parent_id`=".$this->cats[$i]." AND `categories_status`=1 ORDER BY `sort_order`");
    while($row = mysqli_fetch_assoc($qry)) {
        $nav .= '<li class="navigation'.$i.'"><a class="';
        if((isset($structure[$i+1]) && $row['categories_id'] == $this->cats[$i+1]) || $row['categories_id']==$this->id) $nav .= 'current_navigation';
        else $nav .= 'other_navigation';
        $nav .= '" href="'.$url.$row['categories_url'].'/">'.ucwords($row['categories_name']).'</a></li>';  
        if((isset($this->cats[$i+1]) && $row['categories_id'] == $this->cats[$i+1]) || $row['categories_id'] == $this->id) {  
            $url .= preg_replace("/_c[0-9_]+/", "", $row['categories_url']).'/';
            $this->structure($i+1,$url);
        }
    }
    return $nav;
}
Vivian Kennedy
  • 121
  • 1
  • 8
  • Basically is there a better way to code this, with a function, possible recursive? – Vivian Kennedy Jun 26 '15 at 18:21
  • You question seems too broad to be a good fit for this site. And that code makes me sad. My one suggestion would be to do one query (using JOINs) that gets all the data in an associative array, then just work with that array. – Dan Jun 26 '15 at 18:22
  • But how can you do that with an arbitrary number of categories. The only thing broad is your answer. If the code makes you sad show me some improvement. – Vivian Kennedy Jun 26 '15 at 18:28
  • Can you update the question with how your db looks like? ( only relevant info) – Clyff Jun 26 '15 at 18:36
  • I have done a few of these... maybe useful? [display multilevel database driven menu in php](https://stackoverflow.com/questions/29910284/display-multilevel-database-driven-menu-in-php/29915324#29915324). Another? [Category hierarchy from array(cat id => parent id)](https://stackoverflow.com/questions/27162873/category-hierarchy-from-arraycat-id-parent-id/27169144#27169144) This may give you ideas? [recursively database filling with multidimensional array](https://stackoverflow.com/questions/29792309/recursively-database-filling-with-multidimensional-array/29838522#29838522). – Ryan Vincent Jun 26 '15 at 20:00
  • Check this answers, maybe one will be useful for you. http://stackoverflow.com/questions/3116330/recursive-categories-with-a-single-query – Clyff Jun 26 '15 at 20:07
  • @VivianKennedy take a look at this solution, one table, one query for recursion. http://stackoverflow.com/a/3368622/3199530 – HddnTHA Jun 27 '15 at 10:37

1 Answers1

0

Pseudocode, you can create single DB table which stores label and parent item ID:

function render(parent_id){
select * from menu_table where parent_id = parent_id // null is not compared with = in mysql, use is null
foreach menu item as item {
render item - echo ul li etc, create indent
render(item->id) //recursive call with current item ID
closing tags for this menu item
}
return
}

DB table:

You need to design your DB table, here are the columns:
ID, Label, Parent_ID

Then your main menu item would look like this:
1, Main, null (or zero)
Then subitems for this main menu would have the parent_id of 1
2, Submenu1, 1
3, Submenu2, 1

And so on...
Maciej Asembler
  • 656
  • 3
  • 5