How to create a ZF2 nested bootstrap dropdown menu

To create a nested menu in ZF2 you will need to tell the ZF2 navigation service to use a partial containing code that creates new markup.

Here is a step by step example of how to output a nested menu in bootstraps markup in ZF2.

First you need to tell the navigation service to use a partial to render the menu, find where the navigation is called, typically this is the layout file found in

/module/Application/view/layout/layout.phtml

And looks something like:

echo $this->navigation('navigation')->menu();

To tell ZF2 navigation to use the partial myfile.phtml you simple append to the request setPartial and pass an array of arguments, the first argument contains the name of the partial file and the second the name of the module for example the first argument is ‘myfile.phtml’ and the second ‘default’ which is module Application.

echo $this->navigation('navigation')->menu()->setPartial(array('myfile.phtml','default'));

Note you would normally organise partials into their own folder to do this just prepend the folder name to the partial name for example ‘->setPartial(array(‘partial/myfile.phtml’,’default’)’

The ul class needs to be declared as ‘nav nav-bar’ which is used by bootstrap. Simply append this to the end as ->setUlClass(‘nav navbar-nav’) so your complete code should now look like

echo $this->navigation('navigation')->menu()->setPartial(array('myfile.phtml','default'))->setUlClass('nav navbar-nav');

Now create the partial file for the navigation service to use:

/module/Application/view/myfile.phtml

The partial will be used to render the navigation and this is where new logic to display the mark up should be contained.

The following example code will create a nested menu, with the correct mark up and classes that bootstrap recognises, add this to the partial created above.

<ul class="<?php echo $this->navigation()->menu()->getUlClass(); ?>">
 
    <?php call_user_func($selfRef = function($pages, $navigation, $level = 0, $id='lvl-%d-pos-%d') use (&$selfRef) {
 
        foreach ($pages as $pos=>$page){
 
            $page->setClass('nav-header');
            $liClasses          = array();
            $page->id           = sprintf($id, $level, $pos);
 
            /* manually check for ACL conditions */
            if(!$page->isVisible() || !$navigation->accept($page)) {
                continue;
            } 
 
            switch($level){
                case '0': 
                    if ($page->isActive()){
                        $liClasses[]    = 'active';
                    }
                    if($page->hasPages()){ 
                        $liClasses[]    = 'dropdown';
                        $page->setClass('dropdown-toggle');
                        $page->setLabel($page->getLabel().'&nbsp;<span class="caret"></span>');
                    } 
                break;
                default:
                    if($page->hasPages()){ 
                        $liClasses[]    = 'dropdown-submenu';
                    }
                break;
            }
 
            ?>
            <li id="<?php echo $page->id; ?>" <?php echo !empty($liClasses) ? 'class="'.implode(' ',$liClasses).'"' : ''; ?>>
                <a class="<?php echo $page->getClass(); ?>" href="<?php echo $page->getHref(); ?>" <?php if ($page->hasPages()) echo 'data-toggle="dropdown"'; ?>>
                    <?php echo $page->getLabel(); ?>
                </a>
                <?php if ($page->hasPages()) : ?>
                    <ul class="dropdown-menu" role="menu">
                        <?php $selfRef($page->getPages(), $navigation, $level+1, $id); // recursion ?>
                    </ul>
                <?php endif; ?>
            </li>
 
            <?php
        }
 
    }, 
    $this->container, 
    $this->navigation()
    );
 
?>
</ul>

The above code contains a simple function that loops through the pages checking if the current iteration has children or not.

If the iteration does not, a page link is rendered with the correct classes for that level. If the iteration does contain children additional markup is created the function then makes a recursive call to itself to render those pages when the call returns it generates closing markup.

When generating the markup the loop is aware of how deep within the menu structure it currently is. By being aware of the levels and positions CSS classes have been able to be applied at specific levels in addition to this extra markup such as carets has been injected into the top layer, indicating the presence of a drop down menu where present.

Happy coding!

VN:F [1.9.9_1125]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.9_1125]
Rating: +1 (from 1 vote)

1,144 views

This entry was posted on Thursday, December 15th, 2016 at 11:15 am and is filed under ZF2. You can follow any responses to this entry through the RSS 2.0 feed.

Leave a Reply