mertasan / laravel-menu
快速创建 Laravel 菜单
Requires
- php: ^7.4.0|^8.0
- blade-ui-kit/blade-icons: ^0.5.1
- laravel/framework: ^7.0|^8.0
README
文档
- 需求
- 安装
- 入门
- 路由
- 子项
- 手动设置项的 ID
- 手动设置项的昵称
- 引用项
- 引用菜单对象
- HTML 属性
- 操作链接
- 活动项
- 插入分隔符
- 追加和前缀
- 项授权
- 图标
- Svg 图标
- 前后
- 原始项
- 菜单组
- URL 前缀
- 嵌套组
- 元数据
- 过滤项
- 排序项
- 渲染方法
- 子集菜单
- 高级用法
- 配置
- 如果您需要帮助
- 贡献
- 许可
需求
- PHP 7.3 或更高版本
- Laravel 7.14 或更高版本
安装
composer require mertasan/laravel-menu
这将在 Laravel 中注册包并创建一个名为 Menu
的别名。
要使用自己的设置,请发布配置。
php artisan vendor:publish --provider="Mertasan\Menu\MenuServiceProvider"
入门
您可以在 laravel 中间件 中定义菜单定义。结果,每当请求击中您的应用程序时,菜单对象都将可用于所有视图。
php artisan make:middleware GenerateMenus
请确保还将中间件添加到 app\Http\Kernel.php
protected $middlewareGroups = [ 'web' => [ //... \App\Http\Middleware\GenerateMenus::class, ], //... ];
打开您刚刚创建的中间件 app\Http\Middleware\GenerateMenus.php
然后添加基本的菜单声明。例如
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); }
最后,打开一个视图并添加
{!! $MyNavBar->asUl() !!}
您的菜单将创建并显示在页面上。
注意: $MyNavBar
是这些示例中使用的假设名称;您可以为您的菜单命名任何您想要的。
在上面的示例中,Menu::make()
创建了一个名为 MyNavBar
的菜单,将菜单实例添加到 Menu::collection
中,并最终使 $myNavBar
对象在所有应用程序视图中可用。
此方法接受一个可调用的函数,您可以在其中定义菜单项。add
方法定义一个新项。它接受两个参数,第一个是项标题,第二个是选项。
第二个参数 options
可以是一个表示 URL 的简单字符串或一个关联数组,其中包含我们将稍后讨论的选项和 HTML 属性。
您可以使用 Menu::exists()
检查菜单是否已存在。
Menu::exists('primary'); // returns false Menu::make('primary', function(){}); Menu::exists('primary'); // returns true
您可以使用 Menu::makeOnce()
来确保只有当给定名称的菜单不存在时,才调用创建回调。如果您在多个地方有条件地创建相同的菜单,并且不确定是否其他条件已导致菜单被创建,这将非常有用。
Menu::makeOnce('primary', function(){}); // Creates primary, and executes callback. Menu::makeOnce('primary', function(){}); // No operation.
要在视图中渲染菜单
Laravel-menu
默认提供三种渲染方法。但是,您可以使用正确的方法和属性创建自己的渲染方法。
如前所述,laravel-menu
默认提供三种渲染格式:asUl()
、asOl()
和 asDiv()
。您可以在这里了解详细信息:这里。
{!! $MyNavBar->asUl() !!}
您也可以通过菜单集合访问菜单对象
{!! Menu::get('MyNavBar')->asUl() !!}
这将按照以下方式渲染您的菜单
<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::make('MyNavBar', function (\Mertasan\Menu\Builder $menu) { ... });
路由
URLs
您可以通过将 URL 作为 add
方法的第二个参数传递来简单地给菜单项分配一个 URL
$menu->add('About Us', 'about-us');
命名路由
laravel-menu
也支持命名路由
这次,我们不是将简单字符串传递给 add()
,而是传递一个包含键 route
和命名路由作为值的关联数组
// 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']); });
控制器操作
Laravel Menu 也支持控制器操作。
您只需将选项数组的 action
键设置为控制器操作即可。
假设我们在 routes/web.php
或较旧的 app/Http/routes.php
文件中定义了以下路由
Route::get('services', 'ServiceController@index');
然后,要引用此路由,我们可以将操作传递到选项数组中。
$menu->add('services', ['action' => 'ServicesController@index']);
另外:如果您需要将一些参数作为查询字符串发送到路由、URL 或控制器操作,您可以简单地将其包含在一个数组中,并与路由、操作或 URL 值一起传递。
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
默认情况下,HTTP 与 HTTPS 将由 Laravel 的 UrlGenerator 确定,它匹配页面的当前模式。
如果您需要覆盖此行为,请在项目的 link
属性上调用 secure()
以强制使用 https。或者,将 secure
键添加到选项数组中,并将其设置为 true
。
$menu->add('Members', 'members')->link->secure(); // or alternatively use the following method $menu->add('Members', ['url' => 'members', 'secure' => true]);
输出为 <ul>
的结果将是
<ul> ... <li><a href="https://yourdomain.com/members">Members</a></li> ... </ul>
子项
项目也可以有子项目
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'); //... });
您也可以链式定义项目并无限深入
$menu->add('About', ['route' => 'page.about']) ->add('Level2', 'link address') ->add('level3', 'Link address') ->add('level4', 'Link address');
您可以使用 parent
属性直接添加子项目。
$menu->add('About', ['route' => 'page.about']); $menu->add('Level2', ['url' => 'Link address', 'parent' => $menu->about->id]);
手动设置项的 ID
当您添加新项目时,会自动分配一个唯一的 ID。然而,有时您从数据库加载菜单项时必须手动设置 ID。要处理这种情况,您可以针对项目对象调用 id()
方法并传递您想要的 ID。
$menu->add('About', ['route' => 'page.about']) ->id('74398247329487')
或者,您可以在添加菜单项时将 ID 作为选项数组的元素传递。
$menu->add('About', ['route' => 'page.about', 'id' => 74398247329487]);
手动设置项目的昵称
当您添加新项目时,会自动分配一个昵称以供进一步引用。这个昵称是项目标题的驼峰式形式。例如,标题为 About Us
的项目将具有昵称:aboutUs
。但是,有时您必须明确定义菜单项,因为您使用的是特殊的字符集。为此,您可以使用项目的对象调用 nickname()
方法并传递您想要的昵称。
$menu->add('About', ['route' => 'page.about']) ->nickname('about_menu_nickname'); // And use it like you normally would $menu->item('about_menu_nickname');
或者,您可以将昵称作为选项数组的元素传递。
$menu->add('About', ['route' => 'page.about', 'nickname' => 'about_menu_nickname']); // And use it like you normally would $menu->item('about_menu_nickname');
引用项
您可以使用以下方法在您的代码中访问定义的项目。
通过标题获取项
使用 $menu
后跟项目标题的 驼峰式 写法
$menu->itemTitleInCamelCase // or $menu->get('itemTitleInCamelCase') // or $menu->item('itemTitleInCamelCase')
例如,让我们在我们定义 About us
项目后在其后插入一个分隔符
$menu->add('About us', 'about-us') $menu->aboutUs->divide(); // or $menu->get('aboutUs')->divide(); // or $menu->item('aboutUs')->divide();
如果您不熟悉上述方法,可以将项目对象的引用存储在变量中以供进一步引用。
$about = $menu->add('About', 'about'); $about->add('Who We Are', 'who-we-are'); $about->add('What We Do', 'what-we-do');
通过 ID 获取项目
如果需要,您也可以通过 ID 获取项目。
$menu->add('About', ['url' => 'about', 'id' => 12]); $about = $menu->find(12)
获取所有项
$menu->all(); // or outside of the builder context Menu::get('MyNavBar')->all();
all()
方法返回一个 Laravel Collection。
获取第一个项
$menu->first(); // or outside of the builder context Menu::get('MyNavBar')->first();
获取最后一个项
$menu->last(); // or outside of the builder context Menu::get('MyNavBar')->last();
获取活动项
$menu->active() // or outside of the builder content Menu::get('MyNavBar')->active();
获取项目的子项目
首先,您需要使用上述方法获取项目,然后在它上面调用 children()
。
获取 About
项目的子项目
$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()
返回一个 Laravel 集合。
要检查一个项目是否有子项目,您可以使用 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();
要获取一个项目的所有后代,您可以使用 all
$aboutSubs = $menu->about->all();
获取项的父项
首先使用上述方法之一获取项目,然后在其上调用 parent()
。
获取 About
项目的父项目
$aboutParent = $menu->about->parent(); // or outside of the builder context $aboutParent = Menu::get('MyNavBar')->about->parent(); // Or $aboutParent = Menu::get('MyNavBar')->item('about')->parent();
要检查一个项目是否有父项目,您可以使用 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();
魔法 Where 方法
您也可以通过魔法位置方法搜索项目集合。这些方法由一个 where
连接到属性(对象属性甚至元数据)组成
例如,要获取父项目等于 12 的项目,您可以这样做
$subs = $menu->whereParent(12);
或者获取具有特定元数据的项目
$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');
此方法返回一个 Laravel 集合。
如果您需要获取匹配项的后代,只需将第二个参数设置为 true。
$reds = $menu->whereColor('red', true);
这将给出所有颜色为红色及其后代的项。
引用菜单实例
您可能会遇到需要在不处于构建器上下文的情况下引用菜单实例的情况。
通过名称获取特定菜单
$menu = Menu::get('MyNavBar');
或者获取所有菜单实例
$menus = Menu::all();
您也可以调用 getCollection()
来获取相同的结果
$menus = Menu::getCollection();
这两种方法都返回一个 Laravel 集合
HTML 属性
由于所有菜单项都会被渲染为 HTML 实体,如列表项或 div,因此您可以定义每个项目所需的所有 HTML 属性
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'); });
如果我们选择 HTML 列表作为我们的渲染格式,如 ul
,结果将类似于以下内容
<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>
您还可以使用 attr()
方法在项目定义后设置或获取 HTML 属性。
如果您用单个参数调用 attr()
,它将为您返回属性值。如果您用两个参数调用它,它将考虑第一个和第二个参数作为键/值对并设置属性。您也可以传递一个关联数组,如果您需要一次添加一组 HTML 属性;最后,如果您不带任何参数调用它,它将返回一个包含所有属性的数组。
//... $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 ) */ //...
您可以在集合上使用 attr
,如果需要针对一组项目
$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');
操作链接
所有 HTML 属性都将应用于包装标签(li,div 等);您可能会遇到需要向 <a>
标签添加一些 HTML 属性的情况。
Item
每个实例都有一个属性,该属性存储一个 Link
对象的实例。此对象为您提供了操作 <a>
标签的方法。
就像每个项目一样,Link
也有一个 attr()
方法,其功能与项目完全相同
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']); });
链接的 Href 属性
如果您不想使用 laravel-menu
的路由功能,或者您不希望构建器将任何内容(例如您的宿主地址)作为前缀添加到您的 URL 上,您可以显式设置您的链接的 href 属性
$menu->add('About')->link->href('#');
活动项
您可以使用 active()
在该项目上将其标记为激活
$menu->add('Home', '#')->active(); /* Output * * <li class="active"><a href="#">#</a></li> * */
您还可以将 active
类添加到锚点元素而不是包装元素(div
或 li
)
$menu->add('Home', '#')->link->active(); /* Output * * <li><a class="active" href="#">#</a></li> * */
Laravel Menu 会根据您注册项目时的当前 URI 自动为您执行此操作。
您还可以从包的配置设置中选择要启用的项目(项目或链接)
'active_element' => 'item', // item|link
RESTful URLs
只要在 config/laravel-menu.php
文件中将 restful
选项设置为 true
(config('laravel-menu.menus.[default or your menu name].restful')
),就支持 RESTful URL,例如具有 URL 的菜单项 resource
将由 resource/slug
或 resource/slug/edit
激活。
您可能会遇到应用程序位于子目录而非根目录或资源具有公共前缀的情况;在这种情况下,您需要将 rest_base
选项设置为适当的前缀以获得更好的 RESTful 激活支持。rest_base
可以接受一个简单字符串、字符串数组或函数调用的值。
URL 通配符
laravel-menu
允许您定义一个模式,用于特定项目,如果自动激活不起作用
$menu->add('Articles', 'articles')->active('this-is-another-url/*');
因此,this-is-another-url
和 this-is-another-url/and-another
都将激活 Articles
项目。
禁用激活
有时您可能需要禁用单个项目的自动激活。您可以在选项中传递 disableActivationByURL 如此
$menu->add('Anchor', ['disableActivationByURL' => true, 'url' => '#']);
这通过匹配 URL 来防止自动激活。但是,具有活动子项目的项目的激活仍然有效。
插入分隔符
您可以使用 divide()
方法在每个项目后插入分隔符
//... $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()
还接受一个关联数组作为属性
//... $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
或 prepend
方法将 HTML 或纯文本追加到每个项目的标题中
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> '); // ... });
上面的代码将产生
<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>
您也可以在集合上调用 prepend
和 append
。
前后
允许您添加一个任意 HTML 块而不是下拉列表。还有许多其他可能性。与 append
和 prepend
不同,before
和 after
在 li 标签的根处添加任意 HTML。
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', ]); // ... });
视图资源,模式: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> Exit </a> <form id="logout-form" action="<?php echo route('logout'); ?>" method="POST" style="display: none;"> <?php echo csrf_field(); ?> </form> </div> </div>
上面的代码将产生
<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> 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()
$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> */
菜单组
有时您可能需要在多个项目之间共享属性。您无需为每个项目指定属性和选项,而是可以使用菜单分组功能
PS:此功能的工作方式与 Laravel 路由分组完全相同。
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'); });
属性 style
和 data-role
将应用于 About
和 Services
项目
<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 前缀
就像 Laravel 路由前缀功能一样,您可以使用传递给分组的数组中的 prefix
选项对一组菜单项目进行前缀
注意:前缀仅适用于用 url
标记的菜单项目,但不适用于 route
或 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($about){ $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'); });
这将生成
<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>
嵌套组
Laravel Menu 还支持嵌套分组功能。一个菜单组将其自己的属性与其父分组合并,然后将其共享给其包装的项目
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'); }); }); });
如果我们将其渲染为 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>
元数据
您可能会遇到需要将一些元数据附加到每个项目的情况;这些数据可以是项目放置顺序到访问项目所需的权限等任何内容;您可以通过使用 data()
方法来完成此操作。
data()
方法与 attr()
方法的工作方式完全相同
如果您用单个参数调用 data()
,它将为您返回数据值。如果您用两个参数调用它,它将考虑第一个和第二个参数作为键/值对并设置数据。如果您需要一次添加一组键/值对,也可以传递一个数据关联数组;最后,如果您不带任何参数调用它,它将返回所有数据作为数组。
Menu::make('MyNavBar', function($menu){ $menu->add('Users', ['route' => 'admin.users']) ->data('permission', 'manage_users'); });
您也可以像访问属性一样访问数据
//... $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 //... ?>
元数据不会对项目执行任何操作,也不会在 HTML 中渲染。开发者将决定如何使用它们。
如果需要针对一组项目,您可以在集合上使用 data
$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');
过滤项
我们可以使用 filter()
方法过滤菜单项。Filter()
接收一个您定义的闭包。然后它遍历项目并在每个项目上运行您的闭包。
对于您想要排除的项目,必须返回 false,而对于您想要保留的项目,必须返回 true。
让我们用一个真实场景来继续
我假设您的 User
模型可以检查用户是否有特定的权限
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; });
如您所注意到的,我们使用 data()
附加了每个项目所需的权限。
因此,拥有 manage_users
权限的用户将可以看到 Users
项目。
排序项
laravel-menu
可以根据用户定义的函数或键对项目进行排序,这些键可以是项目属性,如 id、parent 等,或者是存储在每个项目中的元数据。
要根据属性和/或元数据对项目进行排序
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()
还接收第二个参数,该参数指定了排序方向:升序(asc
)和降序(dsc
)。
默认值是 asc
。
按降序对 Id
进行排序的项目
Menu::make('main', function($m){ $m->add('About'); $m->add('Home'); $m->add('Services'); $m->add('Contact'); $m->add('Portfolio'); })->sortBy('id', 'desc');
通过传递闭包来对项目进行排序
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... });
闭包将项目集合作为参数。
渲染方法
默认提供多种渲染格式。
菜单作为无序列表
{!! $MenuName->asUl() !!}
asUl()
将菜单渲染为无序列表。它还可以接收一个可选参数来定义 <ul>
标签本身的属性
{!! $MenuName->asUl( ['class' => 'awesome-ul'] ) !!}
结果
<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>
菜单作为有序列表
{!! $MenuName->asOl() !!}
asOl()
方法将菜单渲染为有序列表。它也可以接收一个可选参数来定义 <ol>
标签本身的属性
{!! $MenuName->asOl( ['class' => 'awesome-ol'] ) !!}
结果
<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>
菜单作为 Div
{!! $MenuName->asDiv() !!}
asDiv()
方法将菜单渲染为嵌套的 HTML div。它也可以接收一个可选参数来定义父 <div>
标签本身的属性
{!! $MenuName->asDiv( ['class' => 'awesome-div'] ) !!}
结果
<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>
菜单作为 Bootstrap 3 Navbar
Laravel Menu 默认提供了一个部分视图,用于生成响应式样式的菜单项,您可以将它包含在基于 Bootstrap 的导航栏中。
您可以通过 config('laravel-menu.views.bootstrap-items')
访问此部分视图。
您只需要包含此部分视图,并将根级项传递给它
... @include(config('laravel-menu.views.bootstrap-items'), ['items' => $mainNav->roots()]) ...
这将使您的 Bootstrap 代码看起来像这样
<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>
如果您正在使用 Bootstrap 5(目前处于测试版),您需要在 config/laravel-menu.php
中将 data_toggle_attribute
选项从 data-toggle
更改为 data-bs-toggle
。
return [ ... 'menus' => [ 'default' => [ 'data_toggle_attribute' => 'data-toggle', ], ], ... ];
向子项添加类属性
除了添加到菜单 ul
和 ol
类,还可以将类添加到子菜单中。传递给 asUl
的三个参数都是数组,如下所示
- 第一个数组是列表的属性:例如,
ul
- 第二个是子列表的属性,例如,
ul>li>ul
- 第三个数组是添加到
li
元素属性的属性
有了这个,您可以像这样给子菜单(子菜单)添加一个类
{!! $menu->asUl( ['class' => 'first-level-ul'], ['class' => 'second-level-ul'] ) !!}
子集菜单
构建好菜单后,您可以调用我们的任何子菜单函数来获取一个新的 Builder
以快速生成其他菜单。
顶部菜单
这生成顶级项的 Builder
,即没有父项的项。
{!! Menu::get('primary')->topMenu()->asUl() !!}
子菜单
这生成活动项的直接子项的 Builder
。
{!! Menu::get('primary')->subMenu()->asUl() !!}
同级菜单
这生成活动项的兄弟项的 Builder
。
{!! Menu::get('primary')->siblingMenu()->asUl() !!}
面包屑菜单
这通过递归获取活动项的所有父项(包括活动项)来生成 Builder
。
{!! Menu::get('primary')->crumbMenu()->asUl() !!}
高级用法
如前所述,您可以创建自己的渲染格式。
基本示例
如果您想根据您的设计渲染菜单(s),您应该创建两个视图。
View-1
该视图包含所有 HTML 代码,如nav
或ul
或div
标签,它们包裹着菜单项。View-2
实际上是一个部分视图,用于渲染菜单项(它将被包含在View-1
中)。
我们在这里使用两个视图文件的原因是 View-2
会递归调用自身,以便渲染多层菜单所需的最深级别的项。
让我们用一个例子来简化这个过程
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'); });
在这个例子中,我们将 View-1 命名为 custom-menu.blade.php
,将 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
让我们描述一下上面做了什么,在 custom-menus.blade.php
中,我们根据我们的设计放置了任何 HTML 模板代码,然后我们包含了 custom-menu-items.blade.php
并将 根级别 的菜单项传递给了 custom-menu-items.blade.php
... @include('custom-menu-items', ['items' => $menu->roots()]) ...
在 custom-menu-items.blade.php
中,我们运行了一个 foreach
循环,并在当前项有子项的情况下递归调用文件。
要将渲染后的菜单放入您的应用程序模板中,您只需在主布局中包含 custom-menu
视图。
Blade 控制结构
Laravel 菜单扩展 Blade 以处理特殊布局。
@lm_attrs
您可能会遇到某些 HTML 属性被显式地写在了您的视图中,而不是在添加项时动态定义的情况;但是您需要将这些静态属性与您的项属性合并。
@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
在上面的片段中,li
标签具有 dropdown
类和 data-test
属性在视图中显式定义;Laravel 菜单提供了一个控制结构来处理这个问题。
假设该项在添加时也具有几个动态定义的属性
$menu->add('Dropdown', ['class' => 'item item-1', 'id' => 'my-item']);
视图
@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
此控制结构会自动将静态 HTML 属性与动态定义的属性合并。
这是结果
...
<li class="item item-1 dropdown" id="my-item" data-test="test">...</li>
...
项的属性和回调函数
在打印列表时,您可以:为列表元素设置属性;设置回调函数,为每个链接添加前缀或条件("?id={$id}"),等等。
- 将菜单转换为移动端下拉列表的示例
控制器
$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 ]);
视图: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']); ?>
- 打印录制管理菜单的示例
控制器
$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 ]);
视图:layouts.table.view(用于不同 ID 的循环中)
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):'';
图标
settings.php
return [ ... 'config' => [ 'icon_family' => 'fa', // class="fa [other classes]" [ or null ] 'icon_attributes' => [ 'class' => 'icon-xl', ] ] ... ];
$menu->add('Home')->icon('fa-home'); $menu->add('Users')->icon('fa-user', true); // isAppend = true // or attributes: $menu->add('Users')->icon('fa-user', ['data-icon' => 'hello']); $menu->add('Settings')->appendIcon('fa-cog'); // or appendIcon() $menu->add('Products')->icon('fa-arrow-right', ['data-icon' => 'value', 'class' => 'icon-red']); $menu->add('Categories')->icon('fa-arrow-right', ['data-icon' => 'value', 'class' => 'icon-red'], true); $menu->add('Others')->icon('fa-archive')->appendIcon('fa-caret-right');
输出
<ul> ... <li><a href="..."><i class="fa fa-home icon-xl"></i> Home</a></li> <li><a href="...">Users <i class="fa fa-user icon-xl"></i></a></li> <li><a href="...">Settings <i class="fa fa-cog icon-xl"></i></a></li> <li><a href="..."><i class="fa fa-arrow-right icon-red icon-xl" data-icon="value"></i> Products</a></li> <li><a href="...">Categories <i class="fa fa-arrow-right icon-red icon-xl" data-icon="value"></i></a></li> <li><a href="..."><i class="fa fa-archive icon-xl"></i> Others <i class="fa fa-caret-right icon-xl"></i></a></li> ... </ul>
活动/非活动图标类
return [ ... 'config' => [ 'icon_family' => 'fa', ] 'menus' => [ 'default' => [ // or your menu name 'active_icon_class' => 'icon-active', 'inactive_icon_class' => 'icon-inactive', ], ], ... ];
$menu->add('Home')->icon('fa-home'); $menu->add('Users')->icon('fa-user')->active();
输出
<ul> ... <li><a href="..."><i class="fa fa-home icon-inactive"></i> Home</a></li> <li><a href="..."><i class="fa fa-user icon-active"></i> Users</a></li> ... </ul>
Svg 图标
settings.php
return [ ... 'config' => [ 'svg_path' => 'svg', // project/resources/svg 'svg_attributes' => [ 'class' => 'svg', 'fill' => 'none' ] ] ... ];
文件
project
├──app
└──resources
└── svg
├─home.svg
└──sub
└──user.svg
$menu->add('Home')->svg('home'); // resources/svg/home.svg $menu->add('Users')->svg('sub.user'); // resources/svg/sub/user.svg // $menu->add('Users')->svg('first.second.file'); // resources/svg/first/second/file.svg // custom classes $menu->add('Settings')->svg('icon_name', 'custom svg-xl'); // merge custom attributes $menu->add('Settings')->svg('icon_name', ['width' => '10']); // new attributes $menu->add('Settings')->svg('icon_name', ['width' => '10'], false); // or new attributes with class: // $menu->add('Settings')->svg('icon_name', 'custom-class', ['width' => '10'], false); // prepend and append icons $menu->add('Home')->svg('home')->appendSvg('caret');
注意:以上示例也可以与 $menu->appendSvg(...)
方法一起使用。
输出
<ul> ... <li><a href="..."><svg class="svg" fill="none">......</svg> Home</a></li> <li><a href="..."><svg class="svg" fill="none">......</svg> Users</a></li> <-- custom classes --> <li><a href="..."><svg class="custom svg-xl" fill="none">......</svg> Settings</a></li> <-- merge custom attributes --> <li><a href="..."><svg class="svg" fill="none" width="10">......</svg> Settings</a></li> <-- new attributes --> <li><a href="..."><svg width="10">......</svg> Settings</a></li> <-- prepend and append icons--> <li> <a href="..."> <svg class="svg" fill="none">...home icon...</svg> Home <svg class="svg" fill="none">...caret icon...</svg> </a> </li> ... </ul>
活动/非活动 svg 类
return [ ... 'config' => [ 'svg_attributes' => [ 'class' => 'svg', ] ] 'menus' => [ 'default' => [ // or your menu name 'active_svg_class' => 'svg-active', 'inactive_svg_class' => 'svg-inactive', ], ], ... ];
Menu::make('myNavbar', function(Builder $menu){ $menu->add('Home')->svg('home'); $menu->add('Users')->svg('sub.user')->active(); });
输出
<ul> ... <li><a href="..."><svg class="svg svg-inactive">......</svg> Home</a></li> <li><a href="..."><svg class="svg svg-active">......</svg> Users</a></li> ... </ul>
以下是一个示例
这将特别有助于与 tailwindcss 的兼容性。
'config' => [ 'svg_attributes' => [ 'class' => 'w-5 h-5 fill-current', ] ] 'menus' => [ 'default' => [ 'active_class' => 'text-red', 'active_element' => 'item', 'active_svg_class' => 'bg-green-400 dark:bg-green-600 text-white', 'inactive_svg_class' => 'bg-gray-400 dark:bg-gray-600 text-white', ], 'mynavbar' => [ 'active_class' => 'text-blue', 'active_svg_class' => 'bg-green-600 dark:bg-green-400 text-yellow', ], ],
Menu::make('myNavbar', function(Builder $menu){ $menu->add('Home')->svg('home'); $menu->add('Users')->svg('sub.user')->active(); });
输出
<ul> ... <li> <a href="..."> <svg class="w-5 h-5 fill-current bg-gray-400 dark:bg-gray-600 text-white">......</svg> Home </a> </li> <li class="text-blue"> <a href="..."> <svg class="w-5 h-5 fill-current bg-green-600 dark:bg-green-400 text-yellow">......</svg> Users </a> </li> ... </ul>
授权
Laravel 身份验证 用于基本的授权控制(用户/访客)。并且 Laravel Jetstream 用于其他授权控制。
仅限访客的菜单项
$menu->add('Login')->onlyGuests();
仅限登录用户菜单项
$menu->add('Logout')->onlyUsers();
仅限管理员菜单项
$menu->add('Settings')->onlyAdmins();
自定义授权控制
$menu->add('Settings')->permission(function($user) { return $user->hasTeamPermission($user->currentTeam, 'management:settings'); }); $menu->add('General Settings')->permission(function($user) { return $user->id < 2; });
条件事务
$menu->add('Models'); $menu->add('Settings')->onlyAdmins(); if($menu->settings->isAllowed()){ $menu->models->add('Products'); } if($menu->settings->isAllowed(\App\User::find(10))){ $menu->models->add('Categories'); }
配置
您可以在 config/laravel-menu.php
文件中调整菜单构建器的行为。目前它提供了一些开箱即用的选项
- auto_activate 根据当前 URI 自动激活菜单项
- activate_parents 激活活动项的父项
- active_class 活动项的默认 CSS 类名
- restful 激活 RESTful URL。例如,
resource/slug
将激活具有resource
URL 的项。 - cascade_data 如果您需要项的子代从其父代继承元数据,请确保此选项已启用。
- rest_base 所有 RESTful 资源可能需要以该基本 URL 为前缀。
- active_element 您可以选择要将激活类添加到的 HTML 元素(锚点或包装元素)。
您还可以覆盖每个菜单的默认设置。要覆盖菜单设置,只需将小写的菜单名称作为键添加到设置数组中,并添加您需要覆盖的选项
return [ 'menus' => [ 'default' => [ 'auto_activate' => true, 'activate_parents' => true, 'active_class' => 'active', 'active_element' => 'item', // item|link 'restful' => true, ], 'yourmenuname' => [ 'auto_activate' => false ], ] ];
或者,您可以使用以下方法覆盖默认设置。或者您可以为菜单添加新的自定义设置。
return [ 'menus' => [ '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', ], ] ];
示例 1
覆盖默认设置
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, ] */
示例 2
为菜单添加特定的新设置。
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', ] */
示例 3
添加自定义设置,并从 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', ] */
示例 4
覆盖所有设置(包括默认设置)并添加新的设置。
Menu::make('MyNavbar', function ($menu) { $menu->options([ 'active_class' => 'active', ], null); $menu->add('Home'); $menu->add('About', 'about'); }); /** * Results: [ 'active_class' => 'active' ] */
示例 5
或者你可以这样使用它
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', ] */
未来
待办事项
- 创建菜单和共享选项时的自定义菜单选项。
- 菜单项图标
- 菜单项授权
- 针对 tailwindcss 的特殊配置和方法
- 使用 Alpine.js 的下拉菜单项
- 使用 Livewire 的实时徽章(例如:通知徽章)
- 以及您的建议。
待办事项列表将继续更新。
如果您需要帮助
请使用 GitHub 的问题功能提交所有问题和疑问,我会尽力帮助您。
贡献
如果您可以改进或添加任何功能,请随时提交拉取请求。
致谢
许可
GPL-3.0 许可证(GNU 通用公共许可证 3.0)
有关更多信息,请参阅 许可文件。
这是对 lavary/laravel-menu 的重写。
感谢
表单生成器
您需要一个简单而有用的表单生成器吗?试试 tanthammar/tall-forms。
Laravel Livewire (TALL-stack) 表单生成器,具有实时验证、文件上传、数组字段、Blade 表单输入组件等。