Giter VIP home page Giter VIP logo

laravel-menu's Introduction

Laravel Menu

Latest Stable Version Latest Unstable Version Total Downloads License

A quick and easy way to create menus in Laravel 6, 7 and 8

For Laravel 4.x, check version 1.5.0

Documentation

Installation

composer require lavary/laravel-menu

If you are in Laravel 5.5 you won't need to edit your config/app.php, if you are in a previous version of Laravel, please do the following:

Append Laravel Menu service provider to providers array in config/app.php.

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,

    ...

        Lavary\Menu\ServiceProvider::class,

        ...

],

At the end of config/app.php add 'Menu' => Lavary\Menu\Facade::class to the $aliases array:

'aliases' => [

    'App'       => Illuminate\Support\Facades\App::class,
    'Artisan'   => Illuminate\Support\Facades\Artisan::class,
    ...
    'Menu'      => Lavary\Menu\Facade::class,

],

This registers the package with Laravel and creates an alias called Menu.

To use your own settings, publish config.

php artisan vendor:publish --provider="Lavary\Menu\ServiceProvider"

Getting Started

You can define the menu definitions inside a laravel middleware. As a result anytime a request hits your application, the menu objects will be available to all your views.

php artisan make:middleware GenerateMenus

Be sure to also add the middleware to the app\Http\Kernel.php

    protected $middlewareGroups = [
        'web' => [
            //...
            \App\Http\Middleware\GenerateMenus::class,
        ],
        //...
    ];

Open the middleware you just created app\Http\Middleware\GenerateMenus.php

Then add a basic menu declaration. For example:

public function handle($request, Closure $next)
{
    \Menu::make('MyNavBar', function ($menu) {
        $menu->add('Home');
        $menu->add('About', 'about');
        $menu->add('Services', 'services');
        $menu->add('Contact', 'contact');
    });

    return $next($request);
}

Finally, open a view and add:

{!! $MyNavBar->asUl() !!}

Your menu will be created and displayed on the page.

Note: $MyNavBar is just a hypothetical name used in these examples; You may name your menus whatever you please.

In the above example Menu::make() creates a menu named MyNavBar, Adds the menu instance to the Menu::collection and ultimately makes $myNavBar object available across all application views.

This method accepts a callable inside which you can define your menu items. add method defines a new item. It receives two parameters, the first one is the item title and the second one is options.

The second parameter, options, can be a simple string representing a URL or an associative array of options and HTML attributes which we'll discuss shortly.

You can use Menu::exists() to check if the menu already exists.

Menu::exists('primary'); // returns false
Menu::make('primary', function(){});
Menu::exists('primary'); // returns true

You can use Menu::makeOnce() to ensure the make callback is only called if a menu by the given name does not yet exist. This can be useful if you are creating the same menu in multiple places conditionally, and are unsure whether other conditions have caused the menu to be created already.

Menu::makeOnce('primary', function(){}); // Creates primary, and executes callback.
Menu::makeOnce('primary', function(){}); // No operation.

To render the menu in your view:

Laravel-menu provides three rendering methods out of the box. However you can create your own rendering method using the right methods and attributes.

As noted earlier, laravel-menu provides three rendering formats out of the box, asUl(), asOl() and asDiv(). You can read about the details here.

{!! $MyNavBar->asUl() !!}

You can also access the menu object via the menu collection:

{!! Menu::get('MyNavBar')->asUl() !!}

This will render your menu like so:

<ul>
  <li><a href="http://yourdomain.com">Home</a></li>
  <li><a href="http://yourdomain.com/about">About</a></li>
  <li><a href="http://yourdomain.com/services">Services</a></li>
  <li><a href="http://yourdomain.com/contact">Contact</a></li>
</ul>

And that's all about it!

Routing

URLs

You can simply assign a URL to your menu item by passing the URL as the second argument to add method:

$menu->add('About Us', 'about-us');

Named Routes

laravel-menu supports named routes as well:

This time instead of passing a simple string to add(), we pass an associative with key route and a named route as value:

// Suppose we have these routes defined in our app/routes.php file

//...
Route::get('/',        ['as' => 'home.page',  function(){...}]);
Route::get('about',    ['as' => 'page.about', function(){...}]);
//...

// Now we make the menu:

Menu::make('MyNavBar', function($menu){

  $menu->add('Home',     ['route'  => 'home.page']);
  $menu->add('About',    ['route'  => 'page.about']);

});

Controller Actions

Laravel Menu supports controller actions as well.

You will just need to set action key of your options array to a controller action:

Suppose we have these routes defined in our routes/web.php or the older app/Http/routes.php file:

Route::get('services', 'ServiceController@index');

Then to refer to this route, we can pass the action into the options array.

$menu->add('services', ['action' => 'ServicesController@index']);

Additionaly: if you need to send some parameters to routes, URLs or controller actions as a query string, you can simply include them in an array along with the route, action or URL value:

Menu::make('MyNavBar', function($menu){

  $menu->add('Home',     ['route'  => 'home.page']);
  $menu->add('About',    ['route'  => ['page.about', 'template' => 1]]);
  $menu->add('services', ['action' => ['ServicesController@index', 'id' => 12]]);

  $menu->add('Contact',  'contact');

});

HTTPS

By default, the HTTP vs HTTPS will be determined by Laravel's UrlGenerator which matches the current schema of the page.

If you need to overwrite this behavior, call secure() on the item's link attribute to force https. Alternatively add key secure to the options array and set it to true:

$menu->add('Members', 'members')->link->secure();


// or alternatively use the following method

$menu->add('Members', ['url' => 'members', 'secure' => true]);

The output as <ul> would be:

<ul>
    ...
    <li><a href="https://yourdomain.com/members">Members</a></li>
    ...
</ul>

Sub-items

Items can have sub-items too:

Menu::make('MyNavBar', function($menu){

  //...

  $menu->add('About',    ['route'  => 'page.about']);

  // these items will go under Item 'About'

  // refer to about as a property of $menu object then call `add()` on it
  $menu->about->add('Who We are', 'who-we-are');

  // or

  $menu->get('about')->add('What We Do', 'what-we-do');

  // or

  $menu->item('about')->add('Our Goals', 'our-goals');

  //...

});

You can also chain the item definitions and go as deep as you wish:

  $menu->add('About', ['route'  => 'page.about'])
       ->add('Level2', 'link address')
       ->add('level3', 'Link address')
       ->add('level4', 'Link address');

It is possible to add sub items directly using parent attribute:

$menu->add('About',    ['route'  => 'page.about']);
$menu->add('Level2', ['url' => 'Link address', 'parent' => $menu->about->id]);

Set Item's ID Manually

When you add a new item, a unique ID is automatically assigned to the item. However, there are time when you're loading the menu items from the database and you have to set the ID manually. To handle this, you can call the id() method against the item's object and pass your desired ID:

$menu->add('About', ['route' => 'page.about'])
     ->id('74398247329487')

Alternatively, you can pass the ID as an element of the options array when adding the menu item:

$menu->add('About', ['route' => 'page.about', 'id' => 74398247329487]);

Set Item's Nickname Manually

When you add a new item, a nickname is automatically assigned to the item for further reference. This nickname is the camel-cased form of the item's title. For instance, an item with the title: About Us would have the nickname: aboutUs. However there are times when you have to explicitly define your menu items owing to a special character set you're using. To do this, you may simply use the nickname() method against the item's object and pass your desired nickname to it:

$menu->add('About', ['route' => 'page.about'])
     ->nickname('about_menu_nickname');

// And use it like you normally would
$menu->item('about_menu_nickname');

Alternatively, you can pass the nickname as an element of the options array:

$menu->add('About', ['route' => 'page.about', 'nickname' => 'about_menu_nickname']);

// And use it like you normally would
$menu->item('about_menu_nickname');    

Referring to Items

You can access defined items throughout your code using the methods described below.

Get Item by Title

Use $menu followed by the item's title in camel case:

$menu->itemTitleInCamelCase

// or

$menu->get('itemTitleInCamelCase')

// or

$menu->item('itemTitleInCamelCase')

As an example, let's insert a divider after About us item after we've defined it:

$menu->add('About us', 'about-us')

$menu->aboutUs->divide();

// or

$menu->get('aboutUs')->divide();

// or

$menu->item('aboutUs')->divide();

If you're not comfortable with the above method you can store the item's object reference in a variable for further reference:

$about = $menu->add('About', 'about');
$about->add('Who We Are', 'who-we-are');
$about->add('What We Do', 'what-we-do');

Get Item By Id

You can also get an item by Id if needed:

$menu->add('About', ['url' => 'about', 'id' => 12]);

$about = $menu->find(12)

Get All Items

$menu->all();

// or outside of the builder context

Menu::get('MyNavBar')->all();

The all() method returns a Laravel Collection.

Get the First Item

$menu->first();

// or outside of the builder context

Menu::get('MyNavBar')->first();

Get the Last Item

$menu->last();

// or outside of the builder context

Menu::get('MyNavBar')->last();

Get the Active Item

$menu->active()

// or outside of the builder content

Menu::get('MyNavBar')->active();

Get Sub-Items of the Item

First of all you need to get the item using the methods described above then call children() on it.

To get children of About item:

$aboutSubs = $menu->about->children();

// or outside of the builder context

$aboutSubs = Menu::get('MyNavBar')->about->children();

// or

$aboutSubs = Menu::get('MyNavBar')->item('about')->children();

children() returns a Laravel Collection.

To check if an item has any children or not, you can use hasChildren()

if( $menu->about->hasChildren() ) {
    // Do something
}

// or outside of the builder context

Menu::get('MyNavBar')->about->hasChildren();

// Or

Menu::get('MyNavBar')->item('about')->hasChildren();

To get all descendants of an item you may use all:

$aboutSubs = $menu->about->all();

Get the Parent of the Item

First get the item using one of the methods above then call parent() on it.

To get the parent of About item

$aboutParent = $menu->about->parent();

// or outside of the builder context

$aboutParent = Menu::get('MyNavBar')->about->parent();

// Or

$aboutParent = Menu::get('MyNavBar')->item('about')->parent();

To check if an item has a parent or not, you can use hasParent()

if( $menu->about->hasParent() ) {
    // Do something
}

// or outside of the builder context

Menu::get('MyNavBar')->about->hasParent();

// Or

Menu::get('MyNavBar')->item('about')->hasParent();

Magic Where Methods

You can also search the items collection by magic where methods. These methods are consisted of a where concatenated with a property (object property or even meta data)

For example to get an item with parent equal to 12, you can use it like so:

$subs = $menu->whereParent(12);

Or to get item's with a specific meta data:

$menu->add('Home',     '#')->data('color', 'red');
$menu->add('About',    '#')->data('color', 'blue');
$menu->add('Services', '#')->data('color', 'red');
$menu->add('Contact',  '#')->data('color', 'green');

// Fetch all the items with color set to red:
$reds = $menu->whereColor('red');

This method returns a Laravel collection.

If you need to fetch descendants of the matched items as well, Just set the second argument as true.

$reds = $menu->whereColor('red', true);

This will give all items with color red and their decsendants.

Referring to Menu Instances

You might encounter situations when you need to refer to menu instances out of the builder context.

To get a specific menu by name:

$menu = Menu::get('MyNavBar');

Or to get all menus instances:

$menus = Menu::all();

You can also call getCollection() to get the same result:

$menus = Menu::getCollection();

Both methods return a Laravel Collection

HTML Attributes

Since all menu items would be rendered as HTML entities like list items or divs, you can define as many HTML attributes as you need for each item:

Menu::make('MyNavBar', function($menu){

  // As you see, you need to pass the second parameter as an associative array:
  $menu->add('Home',     ['route'  => 'home.page',  'class' => 'navbar navbar-home', 'id' => 'home']);
  $menu->add('About',    ['route'  => 'page.about', 'class' => 'navbar navbar-about dropdown']);
  $menu->add('services', ['action' => 'ServicesController@index']);
  $menu->add('Contact',  'contact');

});

If we choose HTML lists as our rendering format like ul, the result would be something similar to this:

<ul>
  <li class="navbar navbar-home" id="home"><a href="http://yourdomain.com">Home</a></li>
  <li class="navbar navbar-about dropdown"><a href="http://yourdomain.com/about">About</a></li>
  <li><a href="http://yourdomain.com/services">Services</a></li>
  <li><a href="http://yourdomain.com/contact">Contact</a></li>
</ul>

It is also possible to set or get HTML attributes after the item has been defined using attr() method.

If you call attr() with one argument, it will return the attribute value for you. If you call it with two arguments, It will consider the first and second parameters as a key/value pair and sets the attribute. You can also pass an associative array of attributes if you need to add a group of HTML attributes in one step; Lastly if you call it without any arguments it will return all the attributes as an array.

//...
$menu->add('About', ['url' => 'about', 'class' => 'about-item']);

echo $menu->about->attr('class');  // output:  about-item

$menu->about->attr('class', 'another-class');
echo $menu->about->attr('class');  // output:  about-item another-class

$menu->about->attr(['class' => 'yet-another', 'id' => 'about']);

echo $menu->about->attr('class');  // output:  about-item another-class yet-another
echo $menu->about->attr('id');  // output:  id

print_r($menu->about->attr());

/* Output
Array
(
    [class] => about-item another-class yet-another
    [id] => id
)
*/

//...

You can use attr on a collection, if you need to target a group of items:

$menu->add('About', 'about');

$menu->about->add('Who we are', 'about/whoweare');
$menu->about->add('What we do', 'about/whatwedo');

// add a class to children of About
$menu->about->children()->attr('class', 'about-item');

Manipulating Links

All the HTML attributes will go to the wrapping tags(li, div, etc); You might encounter situations when you need to add some HTML attributes to <a> tags as well.

Each Item instance has an attribute which stores an instance of Link object. This object is provided for you to manipulate <a> tags.

Just like each item, Link also has an attr() method which functions exactly like item's:

Menu::make('MyNavBar', function($menu){

  $about = $menu->add('About',    ['route'  => 'page.about', 'class' => 'navbar navbar-about dropdown']);

  $about->link->attr(['class' => 'dropdown-toggle', 'data-toggle' => 'dropdown']);

});

Link's Href Property

If you don't want to use the routing feature of laravel-menu or you don't want the builder to prefix your URL with anything (your host address for example), you can explicitly set your link's href property:

$menu->add('About')->link->href('#');

Active Item

You can mark an item as activated using active() on that item:

$menu->add('Home', '#')->active();

/* Output
 *
 * <li class="active"><a href="#">#</a></li>
 *
 */

You can also add class active to the anchor element instead of the wrapping element (div or li):

$menu->add('Home', '#')->link->active();

/* Output
 *
 * <li><a class="active" href="#">#</a></li>
 *
 */

Laravel Menu does this for you automatically according to the current URI the time you register the item.

You can also choose the element to be activated (item or the link) in settings.php which resides in package's config directory:

'active_element' => 'item',    // item|link

RESTful URLs

RESTful URLs are also supported as long as restful option is set as true in config/settings.php file, E.g. menu item with url resource will be activated by resource/slug or resource/slug/edit.

You might encounter situations where your app is in a sub directory instead of the root directory or your resources have a common prefix; In such case you need to set rest_base option to a proper prefix for a better restful activation support. rest_base can take a simple string, array of string or a function call as value.

URL Wildcards

laravel-menu makes you able to define a pattern for a certain item, if the automatic activation can't help:

$menu->add('Articles', 'articles')->active('this-is-another-url/*');

So this-is-another-url, this-is-another-url/and-another will both activate Articles item.

Disable activation

Sometimes you may need to disable auto activation for single items. You can pass disableActivationByURL in options like this:

$menu->add('Anchor', ['disableActivationByURL' => true, 'url' => '#']);

This prevents auto activation by matching URL. But activation for items with active children keeps working.

Inserting a Separator

You can insert a separator after each item using divide() method:

//...
$menu->add('Separated Item', 'item-url')->divide()

// You can also use it this way:

$menu->('Another Separated Item', 'another-item-url');

// This line will insert a divider after the last defined item
$menu->divide()

//...

/*
 * Output as <ul>:
 *
 *    <ul>
 *        ...
 *        <li><a href="item-url">Separated Item</a></li>
 *        <li class="divider"></li>
 *
 *        <li><a href="another-item-url">Another Separated Item</a></li>
 *        <li class="divider"></li>
 *        ...
 *    </ul>
 *
 */

divide() also gets an associative array of attributes:

//...
$menu->add('Separated Item', 'item-url')->divide( ['class' => 'my-divider'] );
//...

/*
 * Output as <ul>:
 *
 *    <ul>
 *        ...
 *        <li><a href="item-url">Separated Item</a></li>
 *        <li class="my-divider divider"></li>
 *
 *        ...
 *    </ul>
 *
 */

Append and Prepend

You can append or prepend HTML or plain-text to each item's title after it is defined:

Menu::make('MyNavBar', function($menu){


  $about = $menu->add('About',    ['route'  => 'page.about', 'class' => 'navbar navbar-about dropdown']);

  $menu->about->attr(['class' => 'dropdown-toggle', 'data-toggle' => 'dropdown'])
              ->append(' <b class="caret"></b>')
              ->prepend('<span class="glyphicon glyphicon-user"></span> ');

  // ...            

});

The above code will result:

<ul>
  ...

  <li class="navbar navbar-about dropdown">
   <a href="about" class="dropdown-toggle" data-toggle="dropdown">
     <span class="glyphicon glyphicon-user"></span> About <b class="caret"></b>
   </a>
  </li>
</ul>

You can call prepend and append on collections as well.

Before and After

Allows you to add an arbitrary html block instead of a drop-down list. And many other possibilities. Unlike append and prepend, before and after adds an arbitrary html to the root of the tag li.

Menu::make('MyNavBar', function($menu){


  $menu->add('User', ['title' => Auth::user()->name, 'class' => 'nav-item'])
      ->after(view('layouts.pattern.menu.user_info'))
      ->link->attr([
          'class'         => 'nav-link dropdown-toggle',
          'data-toggle'   => 'dropdown',
          'role'          => 'button',
          'aria-expanded' => 'false',
      ]);

  // ...            

});

Resource of view, pattern: layouts.pattern.menu.user_info

<div class="dropdown-menu" role="menu">    
    <div class="user-info-header">
        <?php echo Auth::user()->name; ?><br>
    </div>
    <div class="pull-left">
        <a href="<?php echo url('tools/profile'); ?>" class="btn btn-primary btn-flat">Profile</a>
    </div>
    <div class="pull-right">
        <a onclick="event.preventDefault(); document.getElementById('logout-form').submit();" class="btn btn-primary btn-flat">
            <i class="fa fa-power-off"></i>&nbsp;Exit
        </a>
        <form id="logout-form" action="<?php echo route('logout'); ?>" method="POST" style="display: none;">
            <?php echo csrf_field(); ?>
        </form>
    </div>
</div>

The above code will result:

<li title="Username" class="nav-item">
    <a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
        User
    </a>
    <div class="dropdown-menu" role="menu">    
        <div class="user-info-header">
            <?php echo Auth::user()->name; ?>
        </div>
        <div class="pull-left">
            <a href="<?php echo url('tools/profile'); ?>" class="btn btn-primary btn-flat">Profile</a>
        </div>
        <div class="pull-right">
            <a onclick="event.preventDefault(); document.getElementById('logout-form').submit();" class="btn btn-primary btn-flat">
                <i class="fa fa-power-off"></i>&nbsp;Exit
            </a>
            <form id="logout-form" action="<?php echo route('logout'); ?>" method="POST" style="display: none;">
                <?php echo csrf_field(); ?>
            </form>
        </div>
    </div>
</li>

Raw Items

To insert items as plain text instead of hyper-links you can use raw():

$menu->raw('Item Title', ['class' => 'some-class']);  

$menu->add('About', 'about');
$menu->About->raw('Another Plain Text Item')

/* Output as an unordered list:
 * <ul>
 *   ...
 *   <li class="some-class">Item's Title</li>
 *   <li>
 *     About
 *     <ul>
 *       <li>Another Plain Text Item</li>
 *     </ul>
 *   </li>
 *   ...
 * </ul>
 */

Menu Groups

Sometimes you may need to share attributes between a group of items. Instead of specifying the attributes and options for each item, you may use a menu group feature:

PS: This feature works exactly like Laravel group routes.

Menu::make('MyNavBar', function($menu){

  $menu->add('Home',     ['route'  => 'home.page', 'class' => 'navbar navbar-home', 'id' => 'home']);

  $menu->group(['style' => 'padding: 0', 'data-role' => 'navigation'], function($m){

        $m->add('About',    ['route'  => 'page.about', 'class' => 'navbar navbar-about dropdown']);
        $m->add('services', ['action' => 'ServicesController@index']);
  }

  $menu->add('Contact',  'contact');

});

Attributes style and data-role would be applied to both About and Services items:

<ul>
    <li class="navbar navbar-home" id="home"><a href="http://yourdomain.com">Home</a></li>
    <li style="padding: 0" data-role="navigation" class="navbar navbar-about dropdown"><a href="http://yourdomain.com/about"About</a></li>
    <li style="padding: 0" data-role="navigation"><a href="http://yourdomain.com/services">Services</a></li>
</ul>

URL Prefixing

Just like Laravel route prefixing feature, a group of menu items may be prefixed by using the prefix option in the array being passed to the group.

Attention: Prefixing only works on the menu items addressed with url but not route or action.

Menu::make('MyNavBar', function($menu){

  $menu->add('Home',     ['route'  => 'home.page', 'class' => 'navbar navbar-home', 'id' => 'home']);

  $menu->add('About', ['url'  => 'about', 'class' => 'navbar navbar-about dropdown']);  // URL: /about

  $menu->group(['prefix' => 'about'], function($m){

    $about->add('Who we are?', 'who-we-are');   // URL: about/who-we-are
    $about->add('What we do?', 'what-we-do');   // URL: about/what-we-do

  });

  $menu->add('Contact',  'contact');

});

This will generate:

<ul>
    <li class="navbar navbar-home" id="home"><a href="/">Home</a></li>

    <li data-role="navigation" class="navbar navbar-about dropdown"><a href="http://yourdomain.com/about/summary"About</a>
        <ul>
           <li><a href="http://yourdomain.com/about/who-we-are">Who we are?</a></li>
           <li><a href="http://yourdomain.com/about/who-we-are">What we do?</a></li>
        </ul>
    </li>

    <li><a href="services">Services</a></li>
    <li><a href="contact">Contact</a></li>
</ul>

Nested Groups

Laravel Menu supports nested grouping feature as well. A menu group merges its own attribute with its parent group then shares them between its wrapped items:

Menu::make('MyNavBar', function($menu){


    $menu->group(['prefix' => 'pages', 'data-info' => 'test'], function($m){

        $m->add('About', 'about');

        $m->group(['prefix' => 'about', 'data-role' => 'navigation'], function($a){

            $a->add('Who we are', 'who-we-are?');
            $a->add('What we do?', 'what-we-do');
            $a->add('Our Goals', 'our-goals');
        });
    });

});

If we render it as a ul:

<ul>
    ...
    <li data-info="test">
        <a href="http://yourdomain.com/pages/about">About</a>
        <ul>
            <li data-info="test" data-role="navigation"><a href="http://yourdomain.com/pages/about/who-we-are"></a></li>
            <li data-info="test" data-role="navigation"><a href="http://yourdomain.com/pages/about/what-we-do"></a></li>
            <li data-info="test" data-role="navigation"><a href="http://yourdomain.com/pages/about/our-goals"></a></li>
        </ul>
    </li>
</ul>

Meta Data

You might encounter situations when you need to attach some meta data to each item; This data can be anything from item placement order to permissions required for accessing the item; You can do this by using data() method.

data() method works exactly like attr() method:

If you call data() with one argument, it will return the data value for you. If you call it with two arguments, It will consider the first and second parameters as a key/value pair and sets the data. You can also pass an associative array of data if you need to add a group of key/value pairs in one step; Lastly if you call it without any arguments it will return all data as an array.

Menu::make('MyNavBar', function($menu){


$menu->add('Users', ['route'  => 'admin.users'])
      ->data('permission', 'manage_users');

});

You can also access a data as if it's a property:

//...

$menu->add('Users', '#')->data('placement', 12);

// you can refer to placement as if it's a public property of the item object
echo $menu->users->placement;    // Output : 12

//...
?>

Meta data don't do anything to the item and won't be rendered in HTML either. It is the developer who would decide what to do with them.

You can use data on a collection, if you need to target a group of items:

$menu->add('Users', 'users');

$menu->users->add('New User', 'users/new');
$menu->users->add('Uses', 'users');

// add a meta data to children of Users
$menu->users->children()->data('anything', 'value');

Filtering the Items

We can filter menu items by a using filter() method. Filter() receives a closure which is defined by you.It then iterates over the items and run your closure on each of them.

You must return false for items you want to exclude and true for those you want to keep.

Let's proceed with a real world scenario:

I suppose your User model can check whether the user has an specific permission or not:

Menu::make('MyNavBar', function($menu){


  $menu->add('Users', ['route'  => 'admin.users'])
       ->data('permission', 'manage_users');

})->filter(function($item){
  if(User::get()->can( $item->data('permission'))) {
      return true;
  }
  return false;
});

As you might have noticed we attached the required permission for each item using data().

As result, Users item will be visible to those who has the manage_users permission.

Sorting the Items

laravel-menu can sort the items based on either a user defined function or a key which can be item properties like id,parent,etc or meta data stored with each item.

To sort the items based on a property and or meta data:

Menu::make('main', function($m){

    $m->add('About', '#')     ->data('order', 2);
    $m->add('Home', '#')      ->data('order', 1);
    $m->add('Services', '#')  ->data('order', 3);
    $m->add('Contact', '#')   ->data('order', 5);
    $m->add('Portfolio', '#') ->data('order', 4);

})->sortBy('order');

sortBy() also receives a second parameter which specifies the ordering direction: Ascending order(asc) and Descending Order(dsc).

Default value is asc.

To sort the items based on Id in descending order:

Menu::make('main', function($m){

    $m->add('About');
    $m->add('Home');
    $m->add('Services');
    $m->add('Contact');
    $m->add('Portfolio');

})->sortBy('id', 'desc');

Sorting the items by passing a closure:

Menu::make('main', function($m){

    $m->add('About')     ->data('order', 2);
    $m->add('Home')      ->data('order', 1);
    $m->add('Services')  ->data('order', 3);
    $m->add('Contact')   ->data('order', 5);
    $m->add('Portfolio') ->data('order', 4);

})->sortBy(function($items) {
    // Your sorting algorithm here...
});

The closure takes the items collection as argument.

Rendering Methods

Several rendering formats are available out of the box:

Menu as Unordered List

  {!! $MenuName->asUl() !!}

asUl() will render your menu in an unordered list. it also takes an optional parameter to define attributes for the <ul> tag itself:

{!! $MenuName->asUl( ['class' => 'awesome-ul'] ) !!}

Result:

<ul class="awesome-ul">
  <li><a href="http://yourdomain.com">Home</a></li>
  <li><a href="http://yourdomain.com/about">About</a></li>
  <li><a href="http://yourdomain.com/services">Services</a></li>
  <li><a href="http://yourdomain.com/contact">Contact</a></li>
</ul>

Menu as Ordered List

{!! $MenuName->asOl() !!}

asOl() method will render your menu in an ordered list. it also takes an optional parameter to define attributes for the <ol> tag itself:

{!! $MenuName->asOl( ['class' => 'awesome-ol'] ) !!}

Result:

<ol class="awesome-ol">
  <li><a href="http://yourdomain.com">Home</a></li>
  <li><a href="http://yourdomain.com/about">About</a></li>
  <li><a href="http://yourdomain.com/services">Services</a></li>
  <li><a href="http://yourdomain.com/contact">Contact</a></li>
</ol>

Menu as Div

{!! $MenuName->asDiv() !!}

asDiv() method will render your menu as nested HTML divs. it also takes an optional parameter to define attributes for the parent <div> tag itself:

{!! $MenuName->asDiv( ['class' => 'awesome-div'] ) !!}

Result:

<div class="awesome-div">
  <div><a href="http://yourdomain.com">Home</a></div>
  <div><a href="http://yourdomain.com/about">About</a></div>
  <div><a href="http://yourdomain.com/services">Services</a></div>
  <div><a href="http://yourdomain.com/contact">Contact</a></div>
</div>

Menu as Bootstrap 3 Navbar

Laravel Menu provides a parital view out of the box which generates menu items in a bootstrap friendly style which you can include in your Bootstrap based navigation bars:

You can access the partial view by config('laravel-menu.views.bootstrap-items').

All you need to do is to include the partial view and pass the root level items to it:

...

@include(config('laravel-menu.views.bootstrap-items'), ['items' => $mainNav->roots()])

...

This is how your Bootstrap code is going to look like:

<nav class="navbar navbar-default" role="navigation">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">Brand</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">

       @include(config('laravel-menu.views.bootstrap-items'), ['items' => $mainNav->roots()])

      </ul>
      <form class="navbar-form navbar-right" role="search">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">

        @include(config('laravel-menu.views.bootstrap-items'), ['items' => $loginNav->roots()])

      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

In case you are using bootstrap 5 (currently in beta) you need to set the data-toggle-attribute option from data-toggle to data-bs-toggle in your config/laravel-menu/settings.php.

Adding class attributes to child items

Like adding a class to the menu ul and ol, classes can be added the submenu too. The three parameters to asUl are arrays as follows:

  • The first array is the attributes for the list: for example, ul
  • The second is the attributes for the child lists, for example, ul>li>ul
  • The third array is attributes that are added to the attributes of the li element

With this you can add a class to the child menu (submenu) like this:

{!! $menu->asUl( ['class' => 'first-level-ul'], ['class' => 'second-level-ul'] ) !!}

Subset Menus

With your menu constructed you can call any of our subset menu functions to get a new Builder to quick generate additional menus.

Top Menu

This generates a Builder of the top level items, items without a parent.

{!! Menu::get('primary')->topMenu()->asUl() !!}

Sub Menu

This generates a Builder of the immediate children of the active item.

{!! Menu::get('primary')->subMenu()->asUl() !!}

Sibling Menu

This generates a Builder of the siblings of the active item.

{!! Menu::get('primary')->siblingMenu()->asUl() !!}

Crumb Menu

This generates a Builder by recursively getting all of the parent items for the active item (including the active item).

{!! Menu::get('primary')->crumbMenu()->asUl() !!}

Advanced Usage

As noted earlier you can create your own rendering formats.

A Basic Example

If you'd like to render your menu(s) according to your own design, you should create two views.

  • View-1 This view contains all the HTML codes like nav or ul or div tags wrapping your menu items.
  • View-2 This view is actually a partial view responsible for rendering menu items (it is going to be included in View-1.)

The reason we use two view files here is that View-2 calls itself recursively to render the items to the deepest level required in multi-level menus.

Let's make this easier with an example:

Menu::make('MyNavBar', function($menu){

  $menu->add('Home');

  $menu->add('About',    ['route'  => 'page.about']);

  $menu->about->add('Who are we?', 'who-we-are');
  $menu->about->add('What we do?', 'what-we-do');

  $menu->add('Services', 'services');
  $menu->add('Contact',  'contact');

});

In this example we name View-1 custom-menu.blade.php and View-2 custom-menu-items.blade.php.

custom-menu.blade.php

<nav class="navbar">
  <ul class="horizontal-navbar">
    @include('custom-menu-items', ['items' => $MyNavBar->roots()])
  </ul>
</nav><!--/nav-->

custom-menu-items.blade.php

@foreach($items as $item)
  <li @if($item->hasChildren()) class="dropdown" @endif>
      <a href="{!! $item->url() !!}">{!! $item->title !!} </a>
      @if($item->hasChildren())
        <ul class="dropdown-menu">
              @include('custom-menu-items', ['items' => $item->children()])
        </ul>
      @endif
  </li>
@endforeach

Let's describe what we did above, In custom-menus.blade.php we put whatever HTML boilerplate code we had according to our design, then we included custom-menu-items.blade.php and passed the menu items at root level to custom-menu-items.blade.php:

...
@include('custom-menu-items', ['items' => $menu->roots()])
...

In custom-menu-items.blade.php we ran a foreach loop and called the file recursively in case the current item had any children.

To put the rendered menu in your application template, you can simply include custom-menu view in your master layout.

Control Structure For Blade

Laravel menu extends Blade to handle special layouts.

@lm_attrs

You might encounter situations when some of your HTML properties are explicitly written inside your view instead of dynamically being defined when adding the item; However you will need to merge these static attributes with your Item's attributes.

@foreach($items as $item)
  <li @if($item->hasChildren()) class="dropdown" @endif data-test="test">
      <a href="{!! $item->url() !!}">{!! $item->title !!} </a>
      @if($item->hasChildren())
        <ul class="dropdown-menu">
              @include('custom-menu-items', ['items' => $item->children()])
        </ul>
      @endif
  </li>
@endforeach

In the above snippet the li tag has class dropdown and data-test property explicitly defined in the view; Laravel Menu provides a control structure which takes care of this.

Suppose the item has also several attributes dynamically defined when being added:

$menu->add('Dropdown', ['class' => 'item item-1', 'id' => 'my-item']);

The view:

@foreach($items as $item)
  <li @lm_attrs($item) @if($item->hasChildren()) class="dropdown" @endif data-test="test" @lm_endattrs>
      <a href="{!! $item->url !!}">{!! $item->title !!} </a>
      @if($item->hasChildren())
        <ul class="dropdown-menu">
              @include('custom-menu-items', ['items' => $item->children()])
        </ul>
      @endif
  </li>
@endforeach

This control structure automatically merges the static HTML properties with the dynamically defined properties.

Here's the result:

...
<li class="item item-1 dropdown" id="my-item" data-test="test">...</li>
...

Attributes and Callback function of item

When printing a list, you can: Set the attributes for the list element; Set the callback function, to add a prefix to each link or by condition ("?id={$id}") and much more.

  • Example of converting a menu into a drop-down list for mobile

Controller:

$items=[
    'copy'=>[
        'icon'=>'fa-copy',
        'title'=>'Copy',
        'text'=>'Copy',
        'link_attribute'=>[
            'class'=>'nav-link',
            'href'=> url(Request::capture()->path()."/copy"),
        ]
    ],
];

$controlItem = Menu::make('controlItem', function($menu) use ($items){
    foreach ($items as $key => $item) if(!isset($item['visible']) || $item['visible']){
        $menu->add($item['text'],['title'=>$item['title']])
            ->append('</span>')
            ->prepend('<i class="fa '.$item['icon'].'"></i> <span>')
            ->link->attr($item['link_attribute']);
    }
});

return view('layouts.table.view',[
    'controlItem' => $controlItem
]);

View: layouts.table.view

<ul class="control-items-min">
    <li title="Menu">
        <a data-toggle="dropdown" aria-expanded="true"><i class="fa fa-bars"></i> <span></span></a>
    <!-- The first array is the attributes for the list: for example, `ul`;
         The second is the attributes for the child lists, for example, `ul>li>ul`;
         The third array is attributes that are added to the attributes of the `li` element. -->
        <?php echo $controlItem->asUl(['class'=>'dropdown-menu', 'role'=>'menu'],[],['class'=>'dropdown-item']); ?>
    </li>
</ul>
<?php echo $controlItem->asUl(['class'=>'control-items'],[],['class'=>'nav-item']); ?>
  • Example of printing the recording management menu

Controller:

$items=[
    'copy'=>[
        'icon'=>'fa-copy',
        'title'=>'Copy',
        'text'=>'Copy',
        'link_attribute'=>[
            'class'=>'nav-link',
            'href'=> url(Request::capture()->path()."/copy"),
        ]
    ],
];

$controlItem = Menu::make('controlItem', function($menu) use ($items){
    foreach ($items as $key => $item) if(!isset($item['visible']) || $item['visible']){
        $menu->add($item['text'],['title'=>$item['title']])
            ->append('</span>')
            ->prepend('<i class="fa '.$item['icon'].'"></i> <span>')
            ->link->attr($item['link_attribute']);
    }
});

return view('layouts.table.view',[
    'controlItem' => $controlItem
]);

View: layouts.table.view (use in a cycle with different IDs)

echo (isset($controlItem)) ? $controlItem->asUl(
    ['class'=>'dropdown-menu control-item'],
    [],
    ['class'=>'nav-item'],
    function($item, &$children_attributes, &$item_attributes, &$link_attr, &$id){
        $link_attr['href'] .= "/".(int)$id;
    },
    $id):'';

Configuration

You can adjust the behavior of the menu builder in config/settings.php file. Currently it provide a few options out of the box:

  • auto_activate Automatically activates menu items based on the current URI
  • activate_parents Activates the parents of an active item
  • active_class Default CSS class name for active items
  • restful Activates RESTful URLS. E.g resource/slug will activate item with resource url.
  • cascade_data If you need descendants of an item to inherit meta data from their parents, make sure this option is enabled.
  • rest_base The base URL that all restful resources might be prefixed with.
  • active_element You can choose the HTML element to which you want to add activation classes (anchor or the wrapping element).

You're also able to override the default settings for each menu. To override settings for menu, just add the lower-cased menu name as a key in the settings array and add the options you need to override:

return [
    'default' => [
        'auto_activate'    => true,
        'activate_parents' => true,
        'active_class'     => 'active',
        'active_element'   => 'item',    // item|link
        'restful'          => true,
    ],
    'yourmenuname' => [
        'auto_activate'    => false
    ],
];

Alternatively, you can override the default settings with the following methods. Or you can add new custom settings to the menu.

return [
    'default' => [
        'auto_activate'    => true,
        'activate_parents' => true,
        'active_class'     => 'active',
        'active_element'   => 'item',    // item|link
        'restful'          => true,
    ],
    'mysidebar' => [
        'active_class'     => 'active-class-mysidebar',
    ],
    'mynavbar' => [
        'active_class'     => 'active-class-mynavbar',
    ],
];

Example 1:

Override default settings

Menu::make('MySidebar', function ($menu) {

    $menu->options([
        'active_class' => 'new-active-class',
    ]);

    $menu->add('Home');
    $menu->add('About', 'about');
    
});

/**
 * Results:
[
    'auto_activate'    => true,
    'activate_parents' => true,
    'active_class'     => 'new-active-class'
    'active_element'   => 'item',
    'restful'          => true,
]
*/

Example 2:

Add new settings specific to the menu.

Menu::make('MySidebar', function ($menu) {

    $menu->options([
        'inactive_class' => 'custom-inactive-class-mysidebar',
    ]);

    $menu->add('Home');
    $menu->add('About', 'about');
    
});

/**
 * Results:
[
    'auto_activate'    => true,
    'activate_parents' => true,
    'active_class'     => 'active-class-mysidebar'
    'active_element'   => 'item',
    'restful'          => true,
    'inactive_class'   => 'custom-inactive-class-mysidebar',
]
*/

Example 3:

Add custom settings and get the rest of the settings from MySidebar.

Menu::make('MyNavbar', function ($menu) {

    $menu->options([
        'inactive_class' => 'custom-inactive-class-mynavbar',
    ], 'MySidebar'); // or mysidebar

    $menu->add('Home');
    $menu->add('About', 'about');
    
});

/**
 * Results:
[
    'auto_activate'    => true,
    'activate_parents' => true,
    'active_class'     => 'active-class-mysidebar'
    'active_element'   => 'item',
    'restful'          => true,
    'inactive_class'   => 'custom-inactive-class-mynavbar',
]
*/

Example 4:

Override all settings (including default settings) and add new ones.

Menu::make('MyNavbar', function ($menu) {

    $menu->options([
        'active_class' => 'active',
    ], null); 

    $menu->add('Home');
    $menu->add('About', 'about');
    
});

/**
 * Results:
[
    'active_class'     => 'active'
]
*/

Example 5:

Or you can use it like this

Menu::make('MyNavbar', function ($menu) {

    $menu->add('Home');
    $menu->add('About', 'about');
    
}, [
    'inactive_class' => 'custom-inactive-class-mynavbar',
]);

/**
 * Results:
[
    'auto_activate'    => true,
    'activate_parents' => true,
    'active_class'     => 'active-class-mynavbar'
    'active_element'   => 'item',
    'restful'          => true,
    'inactive_class'   => 'custom-inactive-class-mynavbar',
]
*/

If You Need Help

Please submit all issues and questions using GitHub issues and I will try to help you.

Contributing

Please feel free to submit pull requests if you can improve or add any features.

We are currently using PSR-2+Symfony formatting. This is easy to implement and check with the PHP Coding Standards Fixer.

Once you have installed php-cs-fixer and added it to your path, simply run the following command in the laravel-menu folder prior to committing.

$ php-cs-fixer fix . --rules=@Symfony

While everyone has different opinions on formatting, this tool will help provide convenient consistency.

Credits

License

Laravel-Menu is free software distributed under the terms of the MIT license.

laravel-menu's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-menu's Issues

Divider Rendering not working b/c Encoding

the item.blade.php should use this:

<li{!! Lavary\Menu\Builder::attributes($item->divider) !!}></li>

instead of

<li{{ Lavary\Menu\Builder::attributes($item->divider) }}></li>

Composer Update Laravel 4.2 Problem

Generating autoload files
{"error":{"type":"Symfony\Component\Debug\Exception\FatalErrorException","message":"Call to undefined method Lavary\Menu\ServiceProvider::mergeConfigFrom()","file":"C:\xampp\htdocs\nanolarave
l\vendor\lavary\laravel-menu\src\Lavary\Menu\ServiceProvider.php","line":21}}Script php artisan clear-compiled handling the post-update-cmd event returned with an error

Many inconsistencies

I was looking for a menu for Laravel 5 and I found this one.
Unfortunately the documentation and the branches do not match, so after a few hours making this work I had to quit.
Can anyone point me to the correct installation for the latest updated and working package?
I also want to encourage you to review the documentation because many of the examples do not work (anymore), and this will confuse of course everyone wanting to use it.

This will be a pity, seen the work has been made to develop it

Any help will be appreciated.

Cheers

Franco

Specify menu depth

Is it possible to specify a menu depth?
Like Menu::get('mainnav')->maxdepth(1) for showing only the top navigation.

New Menu Layout

It would be nice to have the ability to show the menu horizontal. I have an application that I am rewriting in Laravel and need to have the menu go across the top. The menu system I have uses a hidden table for the layout but there maybe a better way to do it. I looked at doing it using CSS but with the list tags, I am not sure how a horizontal menu would work.

Regards,
Ken

Error during removing

I was in need to remove this package. I tried with the command composer remove lavary/laravel-menu but throwing error:
{"error":{"type":"Symfony\\Component\\Debug\\Exception\\FatalErrorException","message":"Class 'Lavary\\Menu\\ServiceProvider' not found","file":"\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/ProviderRepository.php","line":157}}Script php artisan clear-compiled handling the post-update-cmd event returned with an error

Problem with Bootstrap 3.

I use Bootstrap 3, but have problem when render (display don't correct).

..............
<div class="collapse navbar-collapse">
          {{ $MenuName->asBootstrap() }}
</div>
..............

can't add second menu

Sorry for spamming you =)
but if i do:

Menu::make('mainNav', function($menu){
    $menu->add('Home',     '');
    $menu->add('About',    'about');
    $menu->add('services', 'services');
    $menu->add('Contact',  'contact');
});

Menu::make('loginNav', function($menu){
    $menu->add('Login',     '');
    $menu->add('Logout',    'about');
    $menu->add('Name', 'services');
});

I get Call to a member function asBootstrap() on a non-object
am i doing something wrong?

doesnt play nice with prefix

I have the following routes:

    Route::group(array( 'prefix' => Localization::setLocale(), 'before' => 'auth'), function()
    {
        Route::get('/', ['as' => 'home', 'uses' => 'indexController@index']);
        Route::get('lorem/', ['as' => 'business-index', 'uses' => 'LoremController@index']);
        Route::get('lorem/{slug}-{id}', ['as' => 'lorem-page', 'uses' => 'LoremController@pageView'])
            ->where(['slug' => '[a-z0-9-]+', 'id' => '[0-9]+']);
        Route::get('ipsum', ['as' => 'ipsum-index', 'uses' => 'IpsumController@index']);


    });

and the menu:

    Menu::make('loremipsum', function($menu){
        $menu->add('index', ['route' => 'home']);
        $menu->add('lorem', ['route' => 'lorem-index']);
        $menu->add('ipsum, ['route' => 'ipsum-index']);
        Event::fire('menuTop', $menu);
    });

The prefix comes from the laravel-localization package. (can prefix en, nl, de et cetera)

Now when visiting mysite.dev/en/lorem, the index link also lights up! What am I doing wrong?

ErrorException in Builder.php line 577 : Array to string conversion

Hi,
I'm trying to create a menu, according to the documentation, but always obtain the following error : "Array to string conversion" when i'm trying to print it on the view.
I tried to access the menu with the asUl method {!! Menu::get('MyNavBar')->asUl() !!} and with the custom menu template in a separate view but always got this issue.
Laravel version is 5.1.
A little help would be appreciated :)
Thank in advance.

Unique title overriding

$menu->add('Companies', 'companies');
$menu->companies->add('New', 'companies/new');
$menu->companies->add('All', 'companies/all');

$menu->add('Users', 'users');
$menu->users->add('New', 'users/new');
$menu->users->add('All', 'users/all');

$menu->all->link->['foo' => 'bar']);

The title property is used to define the item object.

In the above use case, the item's link attribute is setting for both companies->all and users->all.

Is this intended, a bug, or am I blatantly missing something?

Why does add use push?

Why does builder->add use collection->push?

http://laravel.com/api/source-class-Illuminate.Support.Collection.html#_push

This means each time you add an item, it's being prepended to the menu.

$menu->add(...1...)
$menu->add(...2...)

Logically if you add two items, you'd expect the first item to be first. But, using push means the first item is last, and the second item is first.

It seems like the add function should simply call $this->items[] = $item; so that they're added in order.

Perhaps you were looking for a function to add an item to the collection's items array and only saw push? Since it implements ArrayAccess, you can simply use $col[] = ...; to append an item to the array, rather than pushing/unshifting it.

get all menu instances

Is there a way i can get all menu's i made with Menu::make these are the Builder.php Classes i think..?

How to create a link to #

Is there a way that I'm missing to create a link to # as in literally <a href="#"> as currently using 'url' => '#' ends up as http://mydomain.com/# because $link->url() calls $this->url->to($prefix . '/' . $url, array(), $secure) which then /# becomes the current site. This can cause different behavior than simply #.

Custom "active"

Currently it will use the class "active" if an element is active as defined in

Menu/Item.php on line 354:

$this->attributes['class'] = Builder::formatGroupClass(array('class' => 'active'), $this->attributes);

However many template use a different class, for example nav-active

Could you please add a config value to customize it?

Thank you very much.

cleaner way to check if logged in?

Hey because you are superman.. =)
Is there a cleaner better way to achieve this:

Menu::make('loginNav', function($menu){

    $menu->add(trans('login.login'), array('route'  => 'login'))
            ->meta('loggedIn', false);
    $menu->add(trans('login.register'), array('route'  => 'register'))
            ->meta('loggedIn', false);
    $menu->add(trans('login.logout'), 'todo')
            ->meta('loggedIn', true);
    $menu->add('Name', 'services')
            ->meta('loggedIn', true);

})->filter(function($item) {
        if($item->meta('loggedIn')) {
            return Sentry::check();
        }
        return !Sentry::check();
    });

PS: i like opening issues for you =) haha

Option for render Bootstrap menu only with ul li classes

I know i could publish the views and overwrite them, but anyway an option would be nice.

Reason: you're menu is like default rendered on the left side in the navbar, but what if i want to add other stuff like login, or second menu in the right side of the navbar..

L5 compatibility

Are there any plans to make this package compatible with L5? (Assuming it's not already)

Specify the id of the item manually

Would be interesting to set the id of the item manually, it would facilitate the creation of submenus using the method find, so for example, I could set the id of the item by the same id of a record in a database table.

For example:

$menu->add("Item title", "Item ID", $options);

In this case the id of the item could be optional, if not passed, would be set automatically as currently occurs.

addition of ->neverActive()

I have a language switcher to URL/en/ and that language switcher should never be active, not should its parent for example.

Maybe an implemenation of neverActive() would be an option?

There is no asBootstrap

I may miss something, but I can't seem to find such method.
When calling it from my code there is no output, and I can't seem to understand what it's supposed to do. I'm curious because I'm using bootstrap.

Is it possible to have active items for resource items?

I have a resource 'customers'. I created a menu with a link in it that links to CustomersController@index. When I click a link in my customer.index route which links to an edit page (customer.edit) the customers menu item doesn't get highlighted.

Would it be possible to make a menu-item also active when it matches the current url even if the current url has some extra paths? For example in this case:

  • menu item = /customers
  • current url = /customers/2

Or maybe add an option to a new menu item to match by regex of match by controller?

Incorrect Syntax in doccumentaion page

hey, i think you miss write the docs page at prefixing

so here is the code

add('Home', array('route' => 'home.page', 'class' => 'navbar navbar-home', 'id' => 'home')); $menu->add('About', array('url' => 'about', 'class' => 'navbar navbar-about dropdown')); // URL: /about $menu->group(array('prefix' => 'about') function($m){ ``` $about->add('Who we are?', 'who-we-are'); // URL: about/who-we-are $about->add('What we do?', 'what-we-do'); // URL: about/what-we-do ``` } $menu->add('Contact', 'contact'); }); ?>

and it should be

add('Home', array('route' => 'home.page', 'class' => 'navbar navbar-home', 'id' => 'home')); $menu->add('about', array('url' => 'about', 'class' => 'navbar navbar-about dropdown')); // URL: /about $menu->group(array('prefix' => 'about'), function($m){ ``` $m->about->add('Who we are?', 'who-we-are'); // URL: about/who-we-are $m->about->add('What we do?', 'what-we-do'); // URL: about/what-we-do ``` }); $menu->add('Contact', 'contact'); }); ?>

thank you

Sub Menu - CSS Class

I need a set class for sub menu ul

<ul>
    <li  class="navbar navbar-home" id="home"><a href="/">Home</a></li>
    <li  data-role="navigation" class="navbar navbar-about dropdown"><a href="#">About</a>

     // Need to set <ul class='submenu'>
    <ul> 
           <li><a href="#>Who we are?</a></li>
           <li><a href="#">What we do?</a></li>
     </ul>
    </li>

    <li><a href="services">Services</a></li>
    <li><a href="contact">Contact</a></li>
</ul>

What changes are needed to work with Laravel 4.2?

Hi

I'm running Laravel 4.2.3 and after adding the lavary\laravel-menu package and registering the alias and service provider I get the following error with simply adding this to my route.php

Menu::make('MyNavBar', function($menu){
    $menu->add('Home', '');
});

Argument 3 passed to Lavary\Menu\Menu::__construct() must be an instance of Illuminate\View\Environment, instance of Illuminate\View\Factory given, called in /webroot/vendor/lavary/laravel-menu/src/Lavary/Menu/ServiceProvider.php on line 32 and defined

When I try it on Laravel 4.1.30 it works just fine without any errors. Is there anything I can try to resolve this work on 4.2?

Thanks

Roy

Set active state on specific url

Can I set somehow active state for specific url? I mean, if I click on "articles" in menu, then I choose an article, which url is like "/this-is-the-title/56" , so by default laravel-menu won't find the active menu with the help of current url, but the article contained by the articles menu item, so that should be active in my case.
Thanks

Method names

Why do you choose to use certain methods in camelCase and others in snake_case? Is there a particular reason?

public function get_title()
{
    return $this->link->get_text();
}

public function hasChilderen()
{
    return (count($this->ref->whereParent($this->id))) ? true : false;
}

Load Menu from Other Package

Hi,

Here's my scenario, in my package i've created file menus.php

<?php

Menu::make('adminMenu', function($menu) {

    $menu->add('Dashboard', route('admin.home'))->prepend('<i class="fa fa-dashboard fa-fw"></i> ');
    $menu->add('Settings', 'settings')->prepend('<i class="fa fa-cogs fa-fw"></i> ');

    // sample dropdown
    $menu->add('Dropdown', '#')
        ->prepend('<i class="fa fa-sitemap fa-fw"></i>')
        ->append(' <span class="fa arrow"></span>');

        $dropdown   = $menu->get('dropdown');

        $dropdown->add('Second Level Item', '#');
        $dropdown->add('Second Level Item', '#');
        $dropdown->add('Third Level', '#')->append(' <span class="fa arrow"></span>');

            $thirdLevel = $menu->get('thirdLevel');

            $thirdLevel->add('Third Level Item', '#');
            $thirdLevel->add('Third Level Item', '#');
            $thirdLevel->add('Third Level Item', '#');

});

and load them from my ServiceProvider inside boot() method by calling

// include package menus.php
include __DIR__ . '/../../menus.php';

Then call it as usually from my view, but then i get this error

image

Seems like in boot() method, Menu package not loaded yet.. so my question how to make this work? do you have idea where to save menu builder instead using menus.php like i did?

Here's my files stucture

image

Thanks

Very custom menu

Hi. I create a custom template, but still cant reproduce needed markup:

<ul class="sidebar-menu">
        <li class="active">
            <a href="index.html">
                <i class="fa fa-dashboard"></i> <span>Dashboard</span>
            </a>
        </li>
        <li>
            <a href="pages/widgets.html">
                <i class="fa fa-th"></i> <span>Widgets</span> <small class="badge pull-right bg-green">new</small>
            </a>
        </li>
        <li class="treeview">
            <a href="#">
                <i class="fa fa-bar-chart-o"></i>
                <span>Charts</span>
                <i class="fa fa-angle-left pull-right"></i>
            </a>
            <ul class="treeview-menu">
                <li><a href="pages/charts/morris.html"><i class="fa fa-angle-double-right"></i> Morris</a></li>
                <li><a href="pages/charts/flot.html"><i class="fa fa-angle-double-right"></i> Flot</a></li>
                <li><a href="pages/charts/inline.html"><i class="fa fa-angle-double-right"></i> Inline charts</a></li>
            </ul>
        </li>
        <li class="treeview">
            <a href="#">
                <i class="fa fa-laptop"></i>
                <span>UI Elements</span>
                <i class="fa fa-angle-left pull-right"></i>
            </a>
            <ul class="treeview-menu">
                <li><a href="pages/UI/general.html"><i class="fa fa-angle-double-right"></i> General</a></li>
                <li><a href="pages/UI/icons.html"><i class="fa fa-angle-double-right"></i> Icons</a></li>
                <li><a href="pages/UI/buttons.html"><i class="fa fa-angle-double-right"></i> Buttons</a></li>
                <li><a href="pages/UI/sliders.html"><i class="fa fa-angle-double-right"></i> Sliders</a></li>
                <li><a href="pages/UI/timeline.html"><i class="fa fa-angle-double-right"></i> Timeline</a></li>
            </ul>
        </li>
    </ul>

How send icon title for "i" element before item "title"?

My custom template:

@foreach($items as $item)
    <li@lm-attrs($item) @if($item->hasChildren()) class="treeview" @endif @lm-endattrs>
        <a href="{{ $item->url() }}">
            <span>{{ $item->title }}</span>
            @if($item->hasChildren()) <i class="fa fa-angle-left pull-right"></i> @endif
        </a>
        @if($item->hasChildren())
            <ul class="treeview-menu">
                @include('admin.blocks.sidebar-menu-items', array('items' => $item->children()))
            </ul>
        @endif
    </li>
@endforeach

Error add menu with action

I tried to add menu with action but always return Exception with message "Route [HomeController@getIndex] not defined." though its already defined.

This problem occurs when I put menu definition outside routes.php, I put it in app/menus.php which actually loaded by start/global.php

Permissions issue in sub-links

Menu::make('LandingPageMenu', function ($menu) {

$menu->add('Services', ['url' =>'services', 'class' => 'dropdown']);
$menu->services->add('link1')->link->href('#');
$menu->services->add('link2')->link->href('#');
$menu->services->data('permission', 'services_permission');

})->filter(function ($item) {
$permissions = ['services_permission'];

if (in_array($item->data('permission'), $permissions)) {
    return true;
}

return false;

});

Maybe it is a silly question i need some directions here.
I added to Services link a permission but sub-links aren't displayed in the menu.
Is there a way to inherit the permissions from the main link or do i need to set up for each sub-link?

Array to string conversion Error

Hi, when I'm trying to add menu to view I'm getting this error:
Array to string conversion in /vendor/lavary/laravel-menu/src/Lavary/Menu/Builder.php line 557

here is my code:

Menu::make('menu_front', function ($menu) {

    $menu->add('Login', 'login');
    if(Auth::check() && Auth::user()->isAdmin()) $menu->add('Admin', 'admin');

});

and

{!! Menu::get('menu_front')->asUl() !!}

or

{!! $menu_front->asUl() !!}

I use latest version 1.5.1 and laravel 5.1.10

PS: Laravel from v5.1 moved to spaces indents, I think it's good to change coding style here as well.

Location of setting up your menu

Hey,

Nice work on this package! One thing is a bit odd though. The readme advices people to include the setting up of your menu in your routes.php or in global.php , but to me that sounds like overkill. It seems to me this would be slightly better:

If you have some master template, put in the top

<?php require app_path() . '/views/menubuilder.php'; ?> 

There you build your menu. This way you won't unneccesarily pollute API calls with your menu building which you'll never use in those calls.

Build breadcrumbs based on active path

Hello, and thanks you for such great packaged!

But I can't find any easy way to build breadcrumbs based on active path.

Let's say we have following menu structure:

$menu->add('Dashboard', ['action' => 'DashboardController@showDashboardPage']);

$menu->add('Push Notifications');
$menu->pushNotifications->add('Overview', ['action' => 'Controller@ActionA']);
$menu->pushNotifications->add('Create Campaign', ['action' => 'Controller@Action B']);

So I want to build breadcrubms like so:

  • When on dashboard page: Home > Dashboard.
  • When on overview page: Home > Push Notifications > Overview.
  • When on create campaign page: Home > Push Notifications > Create Campaign.

I'm pretty sure I've read all documentation, but I can't find how to do this right now. Is this possible?

Don't work on windows

on my mac it work's totaly fine but on my pc the menu dont include.

also the bootstrap menu Settings

Config::get('laravel-menu::views.bootstrap-items')
is null an
when i include the view without the Settings:

@include('laravel-menu::bootstrap-navbar-items', array('items' => $MainMenuRight->roots()))

it also don't work and in both cases it don't create's an new view Cache in storage.

current or active route..

didn't search for it, but anyway a built in solution for current or active menu-item would be great...

An really this one is the best menu Builder for Laravel out there !! Thanks for making it =)

Url Not rendering in view when implementing advanced usage

All the other properties are applied except the url property and it is set in the routes.php file.
This is what i am talking about:

  <li @if($item->hasChildren()) class="treeview" @endif>
      <a href="{{ $item->url }}">
          {{ $item->prepend }}
          {{ $item->title }} 
          {{ $item->append }}
      </a>

Add "Active" function from pattern in the 1.4.x branch

I Lavary,
I noticed that you have extended the "active" function with the pattern recognition but you've done that only for the master branch which is only compatible with Laravel 5.
Could you please add this possibility to the 1.4 branch for the Laravel 4 users ?
Thanks for your great job and sorry for my broken english.

REST url parameters in routes.php

Hi,

How to get the current parameters in routes.php?
for example. I'm in project/1 and I want a sidemenu where the link project/1/locations is defined.

I've got this:

Route::resource('project', 'ProjectsController', array('only' => array('show')));
Route::resource('project.locations', 'LocationsController');

Menu::make('sidemenu-project', function($menu) {
$menu->add('Locaties', array('route' => 'project.locations.index','{project?}'))->data('id',1)->prepend(' ');
});

Config file issues

After hitting issues with the facade (https://github.com/lavary/laravel-menu/issues?q=is%3Aopen+is%3Aissue) I tried calling the the code below and received an error. I've seen similar errors in the past which has been down to Laravel having issues with multiple config files (on certain systems at random). The package/service provider/facade are all installed as per the docs, I'm running windows/WAMP.

My code:

    $menu = new Menu;
    $menu->make('MyNavBar', function($menu){
      $menu->add('Home',     array('route'  => 'home.page'));
    });

The error:

Undefined index: default 
here: …\vendor\lavary\laravel-menu\src\Lavary\Menu\Menu.php 63

It seems as though the config is not loading, I've tried dumping the $options array from a line 56 in Menu.php and this returns:

 array (size=0)
    empty

Custom menu items view

Hi,

I must say that this is an awesome library for laravel. I wanted to create a menu that will result to:

<div class="panel panel-default list-group">
    <a class="list-group-item collapsed" data-toggle="collapse" data-parent="#accordion-nav" href="#project"><span
            class="glyphicon glyphicon-tag"></span>Projects<span
            class="glyphicon glyphicon-chevron-updown pull-right"></span></a>

    <div id="project" class="panel-collapse collapse">
        <a href="#" class="list-group-item">Submenu 1</a>
        <a href="#" class="list-group-item">Submenu 2</a>
        <a href="#" class="list-group-item">Submenu 3</a>
        <a href="#" class="list-group-item">Submenu 4</a>
    </div>
</div>
<div class="panel panel-default list-group">
    <a class="list-group-item collapsed" data-toggle="collapse" data-parent="#accordion-nav" href="#account"><span
            class="glyphicon glyphicon-folder-open"></span>My Account<span
            class="glyphicon glyphicon-chevron-updown pull-right"></span></a>

    <div id="account" class="panel-collapse collapse">
        <a href="#" class="list-group-item">Submenu 1</a>
        <a href="#" class="list-group-item">Submenu 2</a>
        <a href="#" class="list-group-item">Submenu 3</a>
        <a href="#" class="list-group-item">Submenu 4</a>
    </div>
</div>

as you can see, we are using bootstrap's accordion and collapse.
so far, here's what I have:

@foreach($items as $item)
    <div@lm-attrs($item) @if($item->hasChildren()) class="panel panel-default list-group" @endif data-test="test" @lm-endattrs>
    <a href="{{ $item->url }}">{{ $item->title }} </a>
    @if($item->hasChildren())
        <div  id="account" class="panel-collapse collapse">
            @include('custom-menu-items', array('items' => $item->children()))
        </div>
    @endif
    </div>
@endforeach

It seems so far from my desired output. Please help!

Thanks!

Facade not working

I've installed the latest package (using composer), and have added the service provider and facade as per the docs (including to my local config file. When I run the following code, I get the error listed below:

    Menu::make('MyNavBar', function($menu){
      $menu->add('Home',     array('route'  => 'home.page'));
    });

error:

  Non-static method Lavary\Menu\Menu::make() should not be called statically, assuming $this from incompatible context 

The error links to the first line of the code above.

It seems that the facade isn't working quite like it should, I've had a look but I'm not familiar enough with creating facades to see an issue.

I'm working on windows/WAMP if that makes any difference?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.