Learn more Laravel

Want to learn more about going from Development to Deployment? I will be live streaming a learning event Laravel: from development to deployment

 

Creating a dynamic menu

Thanks to this very Laravel topic over at Laravel.io and Josh Benham I managed to find out how to make my very own dynamic menu (which is great if you would like to make a reusable admin package). In this tutorial, I will show you exactly how it’s done and hopefully, you will now be able to make amazing admin packages for Laravel.

For those that just want the package then you can download it the Laravel dynamic navigation package. Otherwise, continue below.

First off, I am doing this in my very own package, so my directory structure will look pretty much like anyone else file structure which will be within the src/Package/Admin. Hopefully, you should be able to follow this along just fine, but if you have any problems or even any suggestions for improvements then feel free to let me know.

Within my Admin folder, I create a new folder called services and create a file called ‘Menu.php’, this is the code you should type within this file…

services/Menu.php

<br />
&lt;?php namespace PackageAdminServices;</p>
<p>use Illuminate\Support\Facades\HTML;<br />
use Illuminate\Support\Facades\Request;<br />
use Illuminate\Support\Facades\URL; </p>
<p>class Menu {<br />
    protected $items;<br />
    protected $current;<br />
    protected $currentKey;</p>
<p>    public function __construct() {<br />
        $this-&gt;current = Request::url();<br />
    }<br />
    /*<br />
     * Shortcut method for create a menu with a callback.<br />
     * This will allow you to do things like fire an even on creation.<br />
     *<br />
     * @param callable $callback Callback to use after the menu creation<br />
     * @return object<br />
     */<br />
    public static function create($callback) {<br />
        $menu = new Menu();<br />
        $callback($menu);<br />
        $menu-&gt;sortItems();<br />
        return $menu;<br />
    }<br />
    /*<br />
     * Add a menu item to the item stack<br />
     *<br />
     * @param string $key Dot separated hierarchy<br />
     * @param string $name Text for the anchor<br />
     * @param string $url URL for the anchor<br />
     * @param integer $sort Sorting index for the items<br />
     * @param string $icon URL to use for the icon<br />
     */<br />
    public function add($key, $name, $url, $sort = 0, $icon = null)<br />
    {<br />
        $item = array(<br />
            'key'       =&gt; $key,<br />
            'name'      =&gt; $name,<br />
            'url'       =&gt; $url,<br />
            'sort'      =&gt; $sort,<br />
            'icon'      =&gt; $icon,<br />
            'children'  =&gt; array()<br />
        );<br />
        $children = str_replace('.', '.children.', $key);<br />
        array_set($this-&gt;items, $children, $item);<br />
        if($url == $this-&gt;current) {<br />
            $this-&gt;currentKey = $key;<br />
        }<br />
    }<br />
    /*<br />
     * Recursive function to loop through items and create a menu<br />
     *<br />
     * @param array $items List of items that need to be rendered<br />
     * @param boolean $level Which level you are currently rendering<br />
     * @return string<br />
     */<br />
    public function render($items = null, $level = 1)<br />
    {<br />
        $items = $items ?: $this-&gt;items;<br />
        $attr = array(<br />
            'class' =&gt; 1 === $level ? 'menu level-1' : 'level-' . $level<br />
        );<br />
        $menu = '&lt;ul' . HTML::attributes($attr) . '&gt;';<br />
        foreach($items as $item) {<br />
            $classes = array('menu__item');<br />
            $classes[] = $this-&gt;getActive($item);<br />
            $has_children = sizeof($item['children']);<br />
            if ($has_children) {<br />
                $classes[] = 'parent';<br />
            }<br />
            $menu .= '&lt;li' . HTML::attributes(array('class' =&gt; implode(' ', $classes))) . '&gt;';<br />
            $menu .= $this-&gt;createAnchor($item);<br />
            $menu .= ($has_children) ? $this-&gt;render($item['children'], ++$level) : '';<br />
            $menu .= '&lt;/li&gt;';<br />
        }<br />
        $menu .= '&lt;/ul&gt;';<br />
        return $menu;<br />
    }<br />
    /*<br />
     * Method to render an anchor<br />
     *<br />
     * @param array $item Item that needs to be turned into a link<br />
     * @return string<br />
     */<br />
    private function createAnchor($item)<br />
    {<br />
        $output = '&lt;a class=&quot;menu__item&quot; href=&quot;' . $item['url'] . '&quot;&gt;';<br />
        $output .= $this-&gt;createIcon($item);<br />
        $output .= $item['name'];<br />
        $output .= '&lt;/a&gt;';<br />
        return $output;<br />
    }<br />
    /*<br />
     * Method to render an icon<br />
     *<br />
     * @param array $item Item that needs to be turned into a icon<br />
     * @return string<br />
     */<br />
    private function createIcon($item)<br />
    {<br />
        $output = '';<br />
        if($item['icon']) {<br />
            $output .= sprintf(<br />
                '&lt;i class=&quot;glyphicon glyphicon-%s&quot;&gt;&lt;/i&gt;',<br />
                $item['icon']<br />
            );<br />
        }<br />
        return $output;<br />
    }<br />
    /*<br />
     * Method to sort through the menu items and put them in order<br />
     *<br />
     * @return void<br />
     */<br />
    private function sortItems() {<br />
        usort($this-&gt;items, function($a, $b) {<br />
            if($a['sort'] == $b['sort']) {<br />
                return 0;<br />
            }<br />
            return ($a['sort'] &lt; $b['sort'] ? -1 : 1); }); } /* * Method to find the active links * * @param array $item Item that needs to be checked if active * @return string */ private function getActive($item) { $url = trim($item['url'], '/'); if ($this-&gt;current === $url)<br />
        {<br />
            return 'active current';<br />
        }<br />
        if(strpos($this-&gt;currentKey, $item['key']) === 0) {<br />
            return 'active';<br />
        }<br />
    }<br />
}<br />
&lt;span style=&quot;display: inline-block; width: 0px; overflow: hidden; line-height: 0;&quot; data-mce-type=&quot;bookmark&quot; class=&quot;mce_SELRES_start&quot;&gt;&lt;/span&gt;<br />

Get all that?

This code will set you up for all the behind-the-scenes stuff. Hopefully, most of it is pretty self-explanatory (like most of Laravel) and should be well commented to give you a grasp on what is going on.

Next, we will make our navigation which will be within the views folder, I created an inc folder within my views to keep things tidy called ‘navigation.blade.php’. And my code goes a little like this…

<br />
 &lt;?php use PackageAdminServicesMenu; ?&gt;<br />
 {{<br />
     Menu::create(function($menu) {<br />
         Event::fire('admin.menu.build', $menu);<br />
     })-&gt;render();<br />
}}<br />

This will display your navigation, but the only trouble is that we haven’t actually built it. Here we have the code to build our dynamic navigation menu. Inside your AdminServiceProvider.php you need to add this line into your public function boot()

<br />
include __DIR__.'/../../events.php';<br />

This will tell your package where to find the events page which will build up your navigation (your events.php needs to be in the same place as your routes and filters, which should be in the src folder)…

<br />
 &lt;?php Event::listen('admin.menu.build', function($menu){<br />
     $menu-&gt;add('index', 'Index', URL::route('Admin::index.index'), 1, 'dashboard');<br />
     $menu-&gt;add('users', 'Users', URL::route('Admin::users.index'), 100, 'users');<br />
 });<br />

Here we use the event listen to the method used in Laravel to build our menu, the first part of our add method gives a key which enables a check to see if it’s the current page or not. The second part adds text to your link, URL::route will be the route name which you use in your routes.php. We then have these numbers which says where they should be displayed on the nav and users or dashboard is a font awesome icon.

Phew, that was a lot to take in…

Hopefully, this is enough to get anyone started on a dynamic nav, if you need to use it on more packages then just add the code to the service provider, add another event .php and then create similar code to that above which will build up your navigation even more.

I would also like to thank Josh Benham for his help and support. Check out the discussion at laravel.io.

If you would like to pull in the package I created then take a look at my next post-Laravel dynamic navigation package.

Feel free to add your own feedback in the comments below


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: