Help to build menus easier in Laravel applications (currently support Laravel 5 only)
First, add this package to your project dependencies:
$> composer require "hieu-le/laravel-menu"
After Composer updated your vendors code, add the package service provider to your providers
array in the config/app.php
file:
HieuLe\LaravelMenu\LaravelMenuServiceProvider::class,
Now, you can access to the menu manager via app('menu.manager')
object. If you want to use static methods via alias classes, register the package facade to your aliases
array in the config/app.php
file:
'Menu' => HieuLe\LaravelMenu\Facades\LaravelMenu::class,
You can create as many menus as you want in your application. A menu comes with a unique name which can be anything. You can get the menu instance via that name by menu
method:
<?php
$menu = app('menu.manager')->menu($menuName); // the default value of $menuName is "default"
# with the registered alias, you can also do this
$menu = Menu::menu($menuName);
?>
If there is no menu with the name you specified, a new one will be initiated and returned to you. Some menu comes with an optional header (or label), which can be set by the setLabel
method on the menu instance.
<?php
$menu->setLabel('Sidebar navigation');
?>
The method return the menu instance itself to enable method chaining. Many menus can have the same label or don't have any label. Remember that you cannot retrieve a menu from menu manager via its label, you can only use its name.
I suggest two places to define your menus:
- a route middleware
- a service provider
API: $menu->addLink($text, array $url = [], $options = [])
$text
is the anchor text$url
is an associative array, which is used to generate thehref
attribute of the link. More details is explained later.$option
array is described later.
If $url
is an empty array, the href
will be an hash character (#
).
To assign an URL to the the link, pass a string to the to
element of the $url
array. Example: an item created with $url
equals to ['to' => '/foo/bar']
has the href
leading to http://your-domain.com/foo/bar
, an item created with $url
equals to ['to' => 'http://other-site.com/foo/bar']
has the href
leading to http://other-site.com/foo/bar
.
To assign an internal URL by your named route, pass the route name as string to the route
element of the $url
array. If the route has parameters, pass an array with the first element is the route name, the others are route parameters and the appropriate parameter name as key. For example:
<?php
// routes.php
Route::get('/posts/{post_id}/comments/{comment_id}', ['as' => 'posts.comments.detail'];
// your menu definitions
$menu->addLink($text, ['route' => ['posts.comments.detail', 'post_id' => 1, 'comment_id' => 99]]);
// the output link is: /posts/1/comments/99
?>
To assign an internal URL by the action name, pass the action as string to the action
element of the $url
array. If the action has parameters, pass an array with the first element is the action, the others are the action parameter. For example:
<?php
// routes.php
Route::get('/posts/{post_id}/comments/{comment_id}', ['use' => 'PostController@viewComment'];
// your menu definitions
$menu->addLink($text, ['action' => ['App\Http\Controllers\PostController@viewComment', 1, 99]]);
// the output link is: /posts/1/comments/99
?>
If your desired link contains query string, you can pass an array to the query
element of the $url
array. I use http_build_query
to create the query string and append it to the end of the URL created by the above options.
API $menu->addSubMenu($menu, $options = [])
$menu
is a menu instance. You can useMenu::menu($name)
to create a reusable sub men with a name as descripted above. There is another method to create a menu instance with no name:Menu::createMenu($label = '')
. You can create nested menu system with this package.$options
array is the same asaddLink
function. This array is discussed more in the next section.
When creating new menu item (a link or a sub menu), you can pass an optional array as the $options
parameter. The array can contain these elements:
before
: the content before the link contentafter
: the content after the link contentis_active
: the function to detect whether this link is currently active or not. If it is missing, I do it for you by using my Laravel Active package underground.id
: a local id of the item.next_to
: the local id of the item that you want the new menu item inserted right after. If the local id is not found inside the parent menu, it is ignored and the new item is append to the end of the parent menu link normal.url_def
: the URL definition, it is usage with theisActive
method of the manager to determine whether the current item is active.
I use Laravel built in view system to render the menu, so that you can easily customize the output of a menu. To get the HTML of a menu, call render
method from the menu instance.
API: $menu->render($data = [], $view = '')
$data
: the additional data to pass to the view$view
: the view name to render to menu, it can including other view as normal Laravel view. If it is empty, the default value is use:menu_manager::master_menu
.
If you use the default built in view (the $view
parameter is empty), there are 3 elements used in the $data
array, all of them are strings:
class
: the additional classes of the outermost UL element which wraps the whole menuchildClass
: the additional classes of the LI element that contains the sub menuchildUlClass
: the additional classes of the UL element wrap the child menu, this element is inside a LI element which is applied thechildClass
classes above.
In views, you usually want to add some active class to the menu items that currently selected. This package provides the isActive
method from the menu manager to do that. You give a menu item and get a boolean value which tell you whether this menu item is currently active or not.
<?php
foreach($menu->getItems() as $menuItem) {
$class = Menu::isActive($menuItem) ? "active" : "";
echo "<li class='{$class}'></li>
}
?>
The isActive
method check the is_active
element of the current menu item first, if it is callable, the method return the result from that to you.
If there is no is_active
element, the method use the url_def
element as written above. The url_def
element describe URLs that can make the current menu item active. Its value is an associative array:
route
: the route name of URL of the active menu itemsroute_param
: the route parameter of the active menu itemsroute_pattern
: the pattern of the route name of the active menu itemsaction
: the action of the active menu itemsuri
: the URI of the active menu itemsuri_pattern
: the pattern of the URI of the active menu itemsquery
: the query string variables of the active menu items
See the documentation of Laravel Active package to get more detail.
// routes.php
Route::get('/a', ['as' => 'links.a']);
Route::get('/a/new', ['as' => 'links.a.create']);
Route::get('/a/list', ['as' => 'links.a.list']);
Route::get('/a/{id}', ['as' => 'links.a.detail']);
Route::get('/b', ['as' => 'links.b']);
Route::get('/c', ['as' => 'links.c']);
// AppServiceProvider.php
public function boot() {
$subMenu = app('menu.manager')->createMenu('Multilevel link A')
->addLink('Create A', ['route' => 'links.a.create'])
->addLink('Listing A', ['route' => ['links.a.list']]);
$menu = app('menu.manager')->menu('sidebar')
->setLabel('Sidebar Navigation')
->addSubMenu($subMenu, ['id' => 'link-a', 'url_def' => ['route_pattern' => 'link.a.*']])
->addLink('Link C', ['route' => 'links.c']);
// some thing else ...
app('menu.manager')->menu('sidebar')
// we want to insert link B before link C
->addLink('Link B', ['route' => 'links.b'], 'next_to' => 'link-a']);
}
// Some where in your view
{!! app('menu.manager')->menu('sidebar')->render() !!}
If you are visiting the URI /a/new
, the HTML output will be:
<ul class="menu">
<li class="header">
Sidebar Navigation
</li>
<li class="menuitem active">
<a href="#">
Multilevel link A
</a>
<ul class="menu submenu">
<li class="menuitem item">
<a href="/a/new">Create A</a>
</li>
<li class="menuitem item">
<a href="/a/list">Listing A</a>
</li>
</ul>
</li>
<li class="menuitem item">
<a href="/b">Link B</a>
</li>
<li class="menuitem item">
<a href="/c">Link C</a>
</li>
</ul>