Torbjorn Zetterlund

Wed 07 2022
Image

How to code dynamic menus using CodeIgniter?

by bernt & torsten

I recently worked on a project in which we initially hardcoded the menus in the index.php, as the menu options started to increase I decided it was time to make the menus dynamic. What do I mean by dynamic, in my view dynamic menus allow any admin to build up a menu tree.

The dynamic menus I code looks like this:

So how did I go about making the dynamic menu, let us start with the database tables, I create two different tables navigation_group and navigation_links.

I create a table called navigation_groups which is used to define different areas of a web page, like Header, Sidebar, Footer or what you want it to be. The navigation_group table is straightforward:

--
-- Tabellstruktur `navigation_groups`
--

CREATE TABLE `navigation_groups` (
  `id` int NOT NULL,
  `title` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `abbrev` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- Dumpning av Data i tabell `navigation_groups`
--

INSERT INTO `navigation_groups` (`id`, `title`, `abbrev`) VALUES
(1, 'Header', 'header'),
(2, 'Sidebar', 'sidebar'),
(3, 'Footer', 'footer'),
(4, 'Topbar', 'topbar');

The main menu is navigation_links, here all the menu links are stored, and when the table is read creates the menus. In this table, there are some fields that are important to know about.

  • title – this is the name of the menu that will be shown in the dynamic menu
  • link_Type – this is used to define what type of link – url, uri, page, module
  • page_id – is used for linking specific pages
  • page_id – stores the page id
  • module_name – stores the module name
  • url – stores the URL name
  • uri – stores the URI name
  • module_name – my Codeigniter is set up as an MVC with modules, this is the name of the module
  • navigation_group_id – here I define which navigation group the menu link entry belongs to
  • position – position in the menu
  • target – this is the link target new window or current window
  • restricted_to – here I assign which user group that has access to the menu
  • parent_id – which top-level menu a menu belongs to, 0 = top level
  • is_parent – to define if the menu is the top level (parent) menu, default 1 = top level with children’s menus.
  • class – allow some CSS styling
  • show_menu – you can disable an entry by not showing it – 0 = not show
--
-- Tabellstruktur `navigation_links`
--

CREATE TABLE `navigation_links` (
  `id` int NOT NULL,
  `title` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `link_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 'uri',
  `page_id` int NOT NULL DEFAULT '0',
  `module_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `navigation_group_id` int NOT NULL DEFAULT '0',
  `position` int NOT NULL DEFAULT '0',
  `target` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `restricted_to` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `parent_id` int NOT NULL DEFAULT '0',
  `is_parent` tinyint(1) NOT NULL DEFAULT '0',
  `class` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `show_menu` enum('0','1') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `navigation_links` (`id`, `title`, `link_type`, `page_id`, `module_name`, `url`, `uri`, `navigation_group_id`, `position`, `target`, `restricted_to`, `parent_id`, `is_parent`, `class`, `show_menu`) VALUES
(1, 'Dashboard', 'page', 0, '', 'dashboard', '', 1, 0, NULL, '1,20', 0, 0, '', '1'),
(2, 'Warehouse', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '0'),
(3, 'Passworx', 'page', 0, '', 'passworx', '', 1, 0, NULL, '1,20', 0, 0, '', '1'),
(4, 'EMC', 'page', 0, '', 'personal/password_box_emergency', '', 1, 0, NULL, '1,20', 0, 0, '', '1'),
(5, 'Grades', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '0'),
(6, 'Convert Grades', 'page', 0, '', 'hrgradeapp/grade', '', 1, 0, NULL, '1', 5, 0, '', '1'),
(7, 'Evaluation', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '1'),
(8, 'Emily Config', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '1'),
(9, 'Users', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '1'),
(10, 'Projects', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '0'),
(11, 'Users', 'page', 0, '', 'admin/users', '', 1, 0, NULL, '1', 9, 0, '', '1'),
(12, 'User Groups', 'page', 0, '', 'admin/groups', '', 1, 0, NULL, '1', 9, 0, '', '1'),
(13, 'Projects Dashboard', 'page', 0, '', 'taskmgr', '', 1, 0, NULL, '1', 10, 0, '', '1'),
(14, 'Create Project\r\n', 'page', 0, '', 'taskmgr/projects', '', 1, 0, NULL, '1', 10, 0, '', '1'),
(15, 'Admin HR Grade', 'page', 0, '', '', '', 1, 0, NULL, '1', 0, 1, '', '0'),
(16, 'Admin: Grade Job Titles', 'page', 0, '', 'hrgradeapp', '', 1, 0, NULL, '1', 15, 0, '', '1'),
(17, 'Admin: Job Titles', 'page', 0, '', 'admin/hrgradeapp/hrgrade_title', '', 1, 0, NULL, '1', 15, 0, '', '1');

Let’s get into the mechanics of how I get the menu to show up by reading the navigation_links table and showing it on my pages. In my index.php I add the following code.

<!--- This is the regular Menu Items -->
<div class="collapse navbar-collapse" id="bs-navbar-collapse-1">
	<ul class="nav navbar-nav">
	  <?php
    	   echo $this->dynamic_menu->build_menu();
	  ?>
	</ul>
</div>

We have our view and we have our tables, I need to build the menus – here is how I do that, and I added the build dynamic menu by creating a dynamic_menu.php file and located it in the libraries folder:

    private $ci;                // for CodeIgniter Super Global Reference.

    private $id_menu        = 'id="menu"';
    private $class_menu        = 'class="menu"';
    private $class_parent    = 'class="parent"';
    private $class_last        = 'class="last"';

    // --------------------------------------------------------------------

    /**
     * PHP5        Constructor
     *
     */
    function __construct()
    {
        $this->ci =& get_instance();    // get a reference to CodeIgniter.
    }

    // --------------------------------------------------------------------

    /**
     * build_menu($table, $type)
     *
     * Description:
     *
     * builds the Dynaminc dropdown menu
     * $table allows for passing in a MySQL table name for different menu tables.
     * $type is for the type of menu to display ie; topmenu, mainmenu, sidebar menu
     * or a footer menu.
     *
     * @param    string    the MySQL database table name.
     * @param    string    the type of menu to display.
     * @return    string    $html_out using CodeIgniter achor tags.
     */
    function build_menu($table = 'navigation_links')
    {
        $menu = array();

        // use active record database to get the menu.
        $query = $this->ci->db->get($table);

        if ($query->num_rows() > 0)
        {
            foreach ($query->result() as $row)
            {
                $menu[$row->id]['id']                   = $row->id;
                $menu[$row->id]['title']                = $row->title;
                $menu[$row->id]['link']                 = $row->link_type;
                $menu[$row->id]['page']                 = $row->page_id;
                $menu[$row->id]['module']               = $row->module_name;
                $menu[$row->id]['url']                  = $row->url;
                $menu[$row->id]['uri']                  = $row->uri;
                $menu[$row->id]['navigation_group']     = $row->navigation_group_id;
                $menu[$row->id]['position']             = $row->position;
                $menu[$row->id]['target']               = $row->target;
                $menu[$row->id]['parent']               = $row->parent_id;
                $menu[$row->id]['restricted_to']        = $row->restricted_to;
                $menu[$row->id]['is_parent']            = $row->is_parent;
                $menu[$row->id]['show']                 = $row->show_menu;
            }
        }
        $query->free_result();    // The $query result object will no longer be available

        // ----------------------------------------------------------------------     
        // now we will build the dynamic menus.

        $html_out  = '<div class="collapse navbar-collapse" id="bs-navbar-collapse-1">' . "\n";
        $html_out  .= '<ul class="nav navbar-nav">' . "\n";

        // loop through the $menu array() and build the parent menus.
        for ($i = 1; $i <= count($menu); $i++)
        {
            $rest = explode(',', $menu[$i]['restricted_to']);

            if (is_array($menu[$i]))    // must be by construction but let's keep the errors home
            {
                if ($menu[$i]['show'] && $menu[$i]['parent'] == 0 && in_array($this->ci->current_user->group_id, $rest))    // are we allowed to see this menu?
                {
                    if ($menu[$i]['is_parent'] == TRUE)
                    {
                        // CodeIgniter's anchor(uri segments, text, attributes) tag.
                        $html_out .= '<li class="dropdown">'.anchor('#' . '" class="dropdown-toggle" data-toggle="dropdown"', '<span>'.$menu[$i]['title'].'</span> <b class="caret"></b>');
                    }
                    else
                    {
                        $html_out .= '<li>'.anchor($menu[$i]['url'], '<span>'.$menu[$i]['title'].'</span>');
                    }

                    // loop through and build all the child submenus.
                    $html_out .= $this->get_childs($menu, $i);

                    $html_out .= '</li>'."\n";
                }
            }  else {
                exit (sprintf('menu nr %s must be an array', $i));
            }
        }

        $html_out .= '</ul>' . "\n";
        $html_out .= '</div>' . "\n";

        return $html_out;
    }  
	/**
     * get_childs($menu, $parent_id) - SEE Above Method.
     *
     * Description:
     *
     * Builds all child submenus using a recurse method call.
     *
     * @param    mixed    $menu    array()
     * @param    string    $parent_id    id of parent calling this method.
     * @return    mixed    $html_out if has subcats else FALSE
     */
    function get_childs($menu, $parent_id)
    {
        $has_subcats = FALSE;
        
        $html_out  = '';
        $html_out .= '<ul class="dropdown-menu">'."\n";

        for ($i = 1; $i <= count($menu); $i++)
        {
            $restricted = explode(',', $menu[$i]['restricted_to']);

            if ($menu[$i]['show'] && $menu[$i]['parent'] == $parent_id && in_array($this->ci->current_user->group_id, $restricted))    // are we allowed to see this menu?
            {
                $has_subcats = TRUE;

                if ($menu[$i]['is_parent'] == TRUE)
                {
                    $html_out .= '<li>'.anchor('#', '<span>'.$menu[$i]['title'].'</span>');
                }
                else
                {
                    $html_out .= '<li>'.anchor($menu[$i]['url'], '<span>'.$menu[$i]['title'].'</span>');
                }

                // Recurse call to get more child submenus.
                $html_out .= $this->get_childs($menu, $i);
                $html_out .= '</li>' . "\n";
            }
        }
        $html_out .= "\t\t\t\t\t".'</ul>' . "\n";

        return ($has_subcats) ? $html_out : FALSE;
    }
}

That is the part on how to code to get dynamic menus. Now I used bootstrap for my styling, you could use custom or any other CSS framework.

Share: