underscore
underscore

Reputation: 6887

categorize dynamic menu for add specific classes php

 <?php
// Select all entries from the menu table
$sql1 = $pardConfig->prepare("SELECT id, menu_title, menu_link, parent FROM pard_menu ORDER BY parent, sort, menu_title");
// Create a multidimensional array to conatin a list of items and parents
$sql1->execute();

$menu = array(
    'items' => array(),
    'parents' => array()
);
// Builds the array lists with data from the menu table
while ($items = $sql1->fetch()) {
    // Creates entry into items array with current menu item id ie. $menu['items'][1]
    $menu['items'][$items['id']]         = $items;
    // Creates entry into parents array. Parents array contains a list of all items with children
    $menu['parents'][$items['parent']][] = $items['id'];
}


// Menu builder function, parentId 0 is the root
function buildMenu($pardConfig, $parent, $menu)
{
    $html = "";
    if (isset($menu['parents'][$parent])) {
        $html .= "
<ul>\n";
        foreach ($menu['parents'][$parent] as $itemId) {
            if (!isset($menu['parents'][$itemId])) {
                $html .= "<li>\n <a href='" . $menu['items'][$itemId]['menu_link'] . "'>" . $menu['items'][$itemId]['menu_title'] . "</a>\n</li> \n";
            }
            if (isset($menu['parents'][$itemId])) {
                $html .= "
<li>\n <a href='" . $menu['items'][$itemId]['menu_link'] . "'>" . $menu['items'][$itemId]['menu_title'] . "</a> \n";
                $html .= buildMenu($pardConfig, $itemId, $menu);
                $html .= "</li> \n";
            }
        }
        $html .= "</ul> \n";
    }
    return $html;
}
echo buildMenu($pardConfig, 0, $menu);

?>

Above code is contain a php code for creating a dynamic menu with multilevel sub menus.I have predefined classes for this ...

  1. For main ul (First Ul) i have .nav class
  2. For any li where has first level sub menus i have .dropdown class for li
  3. Additionally for 2nd step i have .dropdown-menu class for ul elemnts which it has a parent element as a li
  4. And i have a .dropdown-submenu class for 3rd level sub menus

So i want to modify my code adding those 4 setps to it ? any help would be appreciated ?

I'm going to use bootsramp menu API for this

TAKE A LOOK @ THIS

This approach i done with Jquery like bellow.but it's not good 100%..

   $(".nav-collapse").find("ul").first().addClass("nav");
   $(".nav-collapse").find("li").has("ul").addClass("nav");
   $(".nav").find("li:has(ul) a").attr("data-toggle", "dropdown");
   $('ul').filter(function () {
       return $(this).parent().is('li')
   }).addClass('dropdown-menu');
   $(".nav").find("li ul li").addClass("dropdown-submenu");
   $('.dropdown-toggle').dropdown();
   });

Upvotes: 2

Views: 1396

Answers (1)

Danack
Danack

Reputation: 25711

You're making your code difficult for yourself by not using an object to represent the menu elements. Because of that, and because you're effectively building the menu structure at the same time as rendering it, your code is hard to read, as well as hard to think about, which is why you're finding it difficult to make it do what you want.

Refactoring the code to use a class for the menu elements completely separates the building of the menu from rendering it, as well as makes it easier to put clear functions in for deciding how it will be rendered.

class MenuElement {

    var $parent = null;
    var $children = array();

    var $menuTitle;
    var $menuLink;

    function __construct($parent, $menuTitle, $menuLink){
        $this->parent = $parent;
        $this->menuTitle = $menuTitle;
        $this->menuLink = $menuLink;
    }

    function hasParent(){
        if ($this->parent) {
            return true;
        }
        return false;
    }

    function addChild($child){
        $this->children[] = $child;
    }

    function hasChildren(){
        return count($children);
    }    

    function hasGrandParent(){
        if ($this->parent) {
            return $this->parent->hasParent();
        }
        return false;
    }

    function render() {
        $navClass = '';
        $ulClass = '';
        $liClass = '';

        if ($this->parent == false) {
            //For main ul (First Ul) i have .nav class
            $navClass = 'nav';
        }

        if (count($this->children)){
            //For any li where has first level sub menus i have .dropdown class for li
            $liClass = 'dropdown';
        }


        if($this->parent) {
            //Additionally for 2nd step i have .dropdown-menu class for 
            //ul elemnts which it has a parent element as a li
            $ulClass = 'dropdown-menu';
        }
        if ($this->hasGrandParent() == true){
            //And i have a .dropdown-submenu class for 3rd level sub menus
            $ulClass = 'dropdown-submenu';
            //$ulClass .= ' dropdown-submenu'; if you wanted both classes
        }

        $output = "<ul class='$navClass'>";
        $output .= "<li class='$liClass'>";
        $output .= "<a href='".$this->menuLink."'>".$this->menuTitle."</a>";

        foreach ($this->children as $child) {
            $output .= $child->render();
        }

        $output .= "</li>";
        $output .= '</ul>';

        return $output;
    }
}


//Builds a menu and returns the root element of it.
function buildMenu(){
    $rootElement = null;
    // Select all entries from the menu table
    $sql1 = $pardConfig->prepare("SELECT id, menu_title, menu_link, parent FROM pard_menu ORDER BY parent, sort, menu_title");
    // Create a multidimensional array to conatin a list of items and parents
    $sql1->execute();

    $menuElements = array();

    while ($items = $sql1->fetch()) {
        $parent = null;

        $parentID = $items['parent'];

        if ($parentID) {
            if(array_key_exists($parentID, $menuElements) == true) {
                $parent = $menuElements[$parentID];
            }
            else{
                throw \Exception("Tried to reference parent which doesn't exist");
            }
        }

        $id = $items['id'];
        $menuElements[$id] = new MenuElement($parent, $items['menu_title'], $items['menu_link']);

        if ($id == 0) {
            $rootElement = $menuElements[$id];
        }

        if ($parent) {
            $parent->addChild($menuElements[$id]);
        }
    }

    if ($rootElement == null) {
        throw Exception("Root element not found, menu is borked");
    }

    //TODO - you may prefer to return $menuElements
    return $rootElement;
}


$menu = buildMenu();
$output = $rootElement->render();
echo $output;

btw this is still bad code in one place as it mixes the retrieving of the menu from the database with building it. It would be better to separate them as well, so that you can dynamically build menus from whichever data source you wish.

Also the column name for the parent in the table would be better as parentID as that would prevent confusion between the $parentID variable (which is an ID) and the $parent variable which is an object.

And no I haven't debugged this - the answer is just to show how it could be done.

Upvotes: 4

Related Questions