konekt / 菜单
Laravel 10 - 11 中的菜单
Requires
- php: ^8.1
- illuminate/support: ^10.0|^11.0
- illuminate/view: ^10.0|^11.0
Requires (Dev)
- ext-sqlite3: *
- mockery/mockery: ^1.0
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0
README
这是 Lavary Menu 的重写版本 Lavary Menu
在 Laravel v5.4 - v10 中快速轻松地创建菜单
Laravel 兼容性
PHP 兼容性
文档
安装
composer require konekt/menu
入门
您可以在服务提供者的 boot 方法中定义菜单,这样任何请求击中您的应用程序时,菜单对象都将可用。
创建菜单
$sidebar = Menu::create('sidebar'); $sidebar->addItem('Home', '/'); $sidebar->addItem('About', 'about');
您可以在稍后将其引用为 Menu::get('sidebar')
。
在视图中访问菜单
如果您想使菜单对象在所有应用程序视图中都可用,请将 'share' 选项传递给 create()
Menu::create('sidebar', null, ['share' => true]); // will be $sidebar in views Menu::create('main', null, ['share' => 'mainMenu']); // will be $mainMenu in views
在 blade 视图中
{{-- Render with the built in 'ul' renderer --}} {!! $mainMenu->render('ul') !!} {{--Or render items manually--}} <nav> @foreach($mainMenu->items as $item) <div class="nav-link><a href="{{ $item->url }}">{{ $item->title }}</a></div> @endforeach </nav>
添加项目
$navbar = Menu::create('navbar'); // Simple link; to '/' via the URL helper $navbar->addItem('home', 'Home', '/'); // Named route $navbar->addItem('clients', 'Clients', ['route' => 'client.index']); // Named route with parameter $navbar->addItem('my-profile', 'My Profile', ['route' => ['user.show', 'id' => Auth::user()->id]]); // Refer to an action $navbar->addItem('projects', 'Projects', ['action' => 'ProjectController@index']); // Action with parameter $navbar->addItem('issue7', 'Issue 7', ['action' => ['IssueController@edit', 'id' => 7]]);
addItem()
方法接受 3 个参数
- 项目的名称
- 项目的标题
- 和选项
选项 可以是一个表示 URL 的简单字符串,也可以是一个包含选项和 HTML 属性的关联数组,具体描述如下。
移除项目
$menu = Menu::create('main'); $menu->addItem('home', 'Home', '/'); $menu->addItem('about', 'About', '/about'); $menu->getItem('about')->addSubItem('about-us', 'About Us', ['url' => '/about/us']); // This will remove both about and about-us $menu->removeItem('about'); // To keep children, set the second parameter `$removeChildren` to false: $menu->removeItem('about', false); // about-us will remain
渲染菜单
该组件提供了 3 种内置渲染方法,即 ul
、ol
和 div
。您可以在 此处 了解详细信息。
{!! $myMenu->render('ul') !!}
您也可以通过 Menu
门面访问菜单对象
{!! Menu::get('navbar')->render('ul') !!}
这将按照以下方式渲染您的菜单
<ul> <li class="active"><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 = Menu::create('uberGigaMenu') $menu->addItem('about', 'About', ['route' => 'page.about']); // these items will go under Item 'About' // refer to about as a property of $menu object then call `addItem()` on it $menu->about->addSubItem('who-we-are', 'Who We are', '/who-we-are'); // or $menu->getItem('about')->addSubItem('what-we-do', 'What We Do', '/what-we-do'); // or $menu->addItem('our-goals', 'Our Goals',[ 'parent' => 'about', 'url' => '/our-goals' ]);
您还可以链式定义项目并深入到您想要的任何程度
$menu->addItem('about', 'About', '/about') ->addSubItem('level2', 'Level 2', '/about/level2') ->addSubItem('level3', 'Level 3', '/about/level2/level3') ->addSubItem('level4', 'Level 4', '/about/level2/level4');
您可以直接使用 parent
属性添加子项目
$menu->addItem('about', 'About'); // You can either set the item object directly as parent: $menu->addItem('team', 'The Team', ['url' => '/about-the-team', 'parent' => $menu->about]); // Or just simply the parent item's name: $menu->addItem('board', 'The Board', ['url' => '/about-the-board', 'parent' => 'about']);
引用项目
您可以使用下面描述的方法在您的代码中访问定义的项目。
按名称获取项目
$menu = \Menu::create('menu'); $menu->addItem('contact', 'Contact', '/contact'); // via the getItem method: $menu->getItem('contact'); // or via magic property accessor: $menu->contact;
您还可以将项目变量存储起来以供进一步引用
$about = $menu->addItem('about', 'About', '/about'); $about->addSubItem('who-we-are', 'Who We Are', '/about/who-we-are'); $about->addSubItem('what-we-do', 'What We Do', '/about/what-we-do');
获取所有项目
菜单有一个 items
属性,它是一个菜单 Item
对象的集合。
$menu->items; // ItemCollection // or: \Menu::get('MyNavBar')->items;
ItemCollection
是一个略微扩展的 Laravel Collection。
获取项目子项目
使用上述方法获取项目,然后在上面调用 children()
。
要获取 About
项目的子项目
$aboutSubs = $menu->about->children(); // or outside of the builder context $aboutSubs = Menu::get('MyNavBar')->about->children(); // Or $aboutSubs = Menu::get('MyNavBar')->getItem('about')->children();
children()
也返回一个 ItemCollection
。
要检查一个项目是否有子项目,您可以使用 hasChildren()
if( $menu->about->hasChildren() ) { // Do something } // or outside of the builder context Menu::get('MyNavBar')->about->hasChildren(); // Or Menu::get('MyNavBar')->getItem('about')->hasChildren();
魔法 Where 方法
您还可以通过魔法 where 方法搜索项目集合。这些方法由一个 where
与属性(对象属性甚至元数据)连接组成
例如,获取具有特定元数据的项目
$menu->addItem('Home', '#')->data('color', 'red'); $menu->addItem('About', '#')->data('color', 'blue'); $menu->addItem('Services', '#')->data('color', 'red'); $menu->addItem('Contact', '#')->data('color', 'green'); // Fetch all the items with color set to red: $reds = $menu->whereColor('red');
此方法返回一个 ItemCollection
。
引用菜单实例
您可能会遇到需要在不处于构建器上下文中引用菜单实例的情况。
通过名称获取特定菜单
$menu = Menu::get('MyNavBar');
或获取所有菜单实例
$menus = Menu::all();
它返回一个 Laravel Collection
HTML 属性
由于所有菜单项都将作为HTML实体(如列表项或div)渲染,因此您可以定义每个项所需的任何HTML属性
$menu = Menu::create('MyNavBar'); // As you see, you need to pass the second parameter as an associative array: $menu->addItem('home', 'Home', ['route' => 'home.page', 'class' => 'navbar navbar-home', 'id' => 'home']); $menu->addItem('about', 'About', ['route' => 'page.about', 'class' => 'navbar navbar-about dropdown']); $menu->addItem('services', 'Services', ['action' => 'ServicesController@index']); $menu->addItem('contact', 'Contact', 'contact');
如果您使用 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->addItem('about', '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: another-class $menu->about->attr(['class' => 'yet-another', 'id' => 'about']); echo $menu->about->attr('class'); // output: yet-another echo $menu->about->attr('id'); // output: about print_r($menu->about->attr()); /* Output Array ( [class] => yet-another [id] => about ) */
如果您需要针对一组项,可以在ItemCollection上使用 attr
。
$menu->addItem('About', 'about'); $menu->about->addSubItem('whoweare', 'Who we are', 'about/whoweare'); $menu->about->addSubItem('whatwedo', 'What we do', 'about/whatwedo'); // add a class to children of About $menu->about->children()->attr('class', 'about-item');
操作链接
所有HTML属性都将传递到包装标签(li、div等);您可能会遇到需要将一些HTML属性添加到 <a>
标签的情况。
每个 Item
实例都有一个属性,该属性存储一个 Link
对象的实例。此对象为您提供操作 <a>
标签的能力。
就像每个项一样,Link
也有一个 attr()
方法,其功能与项的完全相同
$menu = Menu::create('MyNavBar'); $about = $menu->addItem('About', ['route' => 'page.about', 'class' => 'navbar navbar-about dropdown']); $about->link->attr('data-toggle', 'dropdown');
链接的 Href 属性
如果您不想使用路由功能或不想构建器在您的URL前添加任何前缀(例如,您的宿主地址),您可以显式设置链接的href属性
$menu->addItem('about', 'About')->link->href('#');
活动项
您可以使用该项上的 activate()
将项标记为激活
$menu->addItem('home', 'Home', '/')->activate(); /* Output <li class="active"><a href="/">Home</a></li> */
您也可以将 active
类添加到锚点元素而不是包装元素(div
或 li
)上
$menu->addItem('home', 'Home', '/')->link->active(); /* Output <li><a class="active" href="/">Home</a></li> */
菜单组件会根据注册项时的当前 URI 自动激活相应的项。
您可以禁用自动激活或选择要激活的元素(项或链接)
// To prevent from auto activation Menu::create('nav', [ 'auto_activate' => false ]); // To set the active element: Menu::create('nav', [ 'active_element' => 'link' // item|link - item by default ]);
URL 通配符
Konekt菜单组件使您能够为特定项定义一个模式,如果自动激活不起作用
$menu->addItem('articles', 'Articles', '/articles')->activateOnUrls('articles/*');
因此,articles
、articles/random-news-title
都会激活 Articles
项。
检查活动子项
您可以通过调用以下内容来检查菜单项是否有激活的子项
$item->hasActiveChild(); // (bool) false
您可以通过在它们上应用 actives()
过滤器来从项目集合中获取活动项
$menu->roots()->actives(); // Konekt\Menu\ItemCollection // or: $item->children()->actives(); // Konekt\Menu\ItemCollection
追加和前置
您可以在定义后向每个项的标题添加HTML或纯文本
<?php $menu = Menu::create('MyNavBar'); $about = $menu->addItem('about', '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
,以便它影响所有项。
元数据
您可能会遇到需要将某些元数据附加到每个项的情况;这些数据可以是任何东西,从项放置顺序到访问项所需的权限;您可以通过使用 data()
方法来完成此操作。
data()
方法与 attr()
方法功能完全相同
如果您使用一个参数调用 data()
,它将为您返回数据值。如果您使用两个参数调用它,它将考虑第一个和第二个参数作为键/值对并设置数据。如果您需要一次添加一组键/值对,也可以传递一个数据关联数组;最后,如果您不带任何参数调用它,它将返回所有数据作为数组。
$menu->addItem('users', 'Users', ['route' => 'admin.users']) ->data('permission', 'manage_users');
您也可以将数据作为属性访问
$menu->addItem('users', 'Users', '/users')->data('placement', 12); echo $menu->users->placement; // Output : 12
元数据不会对项目产生影响,也不会在HTML中渲染。开发者将决定如何使用它们。
如果您需要针对一组项目,可以在集合上使用data
。
$menu->addItem('users', 'Users'); $menu->users->addSubItem('create_user', 'New User', ['route' => 'user.create']); $menu->users->addSubItem('list_users', 'Uses', ['route' => 'user.index']); // add a meta data to children of Users $menu->users->children()->data('tag', 'admin');
操作项目
菜单项目集合可以通过任何Illuminate Collection方法进行筛选、排序等操作。
渲染
内置渲染器
提供了多种默认渲染格式。
- ul
- ol
- div
以 UL 渲染
$menu = Menu::create('menu'); $menu->addItem('home', 'Home', '/'); $menu->addItem('about', 'About', '/about');
在blade模板中
{!! $menu->render('ul') !!}
结果
<ul> <li class="active"><a href="http://acme.io">Home</a></li> <li><a href="http://acme.io/about">About</a></li> </ul>
以 OL 渲染
$menu = Menu::create('menu', ['class' => 'navigation', 'auto_activate' => false]); $menu->addItem('home', 'Home', '/'); $menu->addItem('about', 'About', '/about');
模板
{!! $menu->render('ol') !!}
结果
<ol class="navigation"> <li><a href="http://acme.io">Home</a></li> <li><a href="http://acme.io/about">About</a></li> </ol>
以 Div 渲染
$menu = Menu::create('menu'); $menu->addItem('home', 'Home', '/'); $menu->addItem('about', 'About', '/about')->attr('data-woink', 'kaboom');
在Blade中
{!! $menu->render('div') !!}
结果
<div> <div class="active"><a href="http://acme.io">Home</a></div> <div data-woink="kaboom"><a href="http://acme.io/about">About</a></div> </div>
自定义渲染器
渲染设计从一开始就是可扩展的。
可以为菜单和项目定义独立的渲染器。
自定义渲染器示例(Bulma)
有关CSS的参考,请参阅Bulma菜单组件。
创建一个菜单渲染器类
use Konekt\Menu\Contracts\MenuRenderer; use Konekt\Menu\Item; use Konekt\Menu\ItemCollection; use Konekt\Menu\Menu; class BulmaMenuRenderer implements MenuRenderer { public function render(Menu $menu) { $result = sprintf("<aside%s class=\"menu\">\n", $menu->attributesAsHtml()); $result .= $this->renderLevel($menu->items->roots(), 1); $result .= "</aside>\n"; return $result; } protected function renderLevel(ItemCollection $items, $level) { $tabs = str_repeat("\t", $level); $class = $level == 1 ? ' class="menu-list"' : ''; $result = "$tabs<ul$class>\n"; foreach ($items as $item) { $result .= $this->renderItem($item, $level); } return $result . "$tabs</ul>\n"; } protected function renderItem(Item $item, $level) { if ($item->hasChildren()) { return $this->renderItemLi($item, $level, $this->renderLevel($item->children(), $level + 1) ); } return $this->renderItemLi($item, $level); } protected function renderItemLi(Item $item, $level, $extraHtml = '') { $tabs = str_repeat("\t", $level + 1); $link = sprintf('<a href="%s"%s>%s</a>', $item->link->url(), $item->link->attributesAsHtml(), $item->title ); if (empty($extraHtml)) { return sprintf("%s<li%s>%s</li>\n", $tabs, $item->attributesAsHtml(), $link); } return sprintf("%s<li%s>\n%s%s\n%s\n%s</li>\n", $tabs, $item->attributesAsHtml(), $tabs, $link, $extraHtml, $tabs ); } }
注册渲染器
app()->singleton('konekt.menu.renderer.menu.bulma', BulmaMenuRenderer::class);
创建菜单
$menu = Menu::create('bulma', [ 'active_element' => 'link', 'active_class' => 'is-active', 'share' => 'bulmaMenu' ]); $menu->addItem('dashboard', 'Dashboard', '/dashboard'); $menu->addItem('customers', 'Customers', '/customers'); $menu->addItem('team', 'Team', '#')->activate(); $menu->team->addSubItem('members', 'Members', '/team/members'); $menu->team->addSubItem('plugins', 'Plugins', '/team/plugins'); $menu->team->plugins->addSubItem('addNewPlugin', 'Add New Plugin', '/team/plugins/new');
在blade模板中渲染
{!! $bulmaMenu->render('bulma') !!}
结果
<aside class="menu"> <ul class="menu-list"> <li><a href="http://menu.test/dashboard">Dashboard</a></li> <li><a href="http://menu.test/customers">Customers</a></li> <li> <a href="#" class="is-active">Team</a> <ul> <li><a href="http://menu.test/team/members">Members</a></li> <li> <a href="http://menu.test/team/plugins">Plugins</a> <ul> <li><a href="http://menu.test/team/plugins/new">Add New Plugin</a></li> </ul> </li> </ul> </li> </ul> </aside>
授权
项目可以通过两种简单的方式授权给用户
1. 通过动作检查
基于Laravel内置的授权,您可以通过can()
方法传递将要对当前用户进行测试的动作名称(字符串)。
$menu = Menu::create('nav', []); $menu->addItem('users', __('Users'), ['route' => 'app.user.index']) ->allowIfUserCan('list users'); $menu->addItem('settings', __('Settings'), ['route' => 'app.settings.index']) ->allowIfUserCan('list settings');
2. 通过回调检查
您还可以传递回调来授权菜单项目给用户
$menu = Menu::create('nav', []); $menu->addItem('users', __('Users'), ['route' => 'app.user.index']) ->allowIf(function($user) { return $user->id > 500; // Add your arbitrary condition });
回调将接收用户作为第一个参数。
您可以在项目中添加多个
allowIf
和/或allowIfUserCan
条件。如果所有条件都满足,项目将被允许。
检查授权
默认情况下,没有任何allow*条件的项是允许的。
要检查项是否允许
$menu->users->isAllowed(); // Checks if the item users item is allowed for the current user $menu->settings->isAllowed(\App\User::find(501)); // Check if an item is available for a given user
要获取允许子项的列表
$item->childrenAllowed(); // Returns an ItemCollection of the allowed item for the current user $item->childrenAllowed(\App\User::find(123)); // Returns the allowed items for the given user
配置
您可以通过创建菜单时传递设置来调整菜单构建器的行为
- auto_activate 根据当前URI自动激活菜单项(默认为true)
- activate_parents 激活活动项的父项(默认为true)
- active_class 活动项的CSS类名(默认为"active")
- cascade_data 如果您需要项目的后代从其父项继承元数据,请确保此选项已启用(默认为true)
- active_element 要设置为活动的HTML元素 'link' (
<a>
)或 'item' (<li>
,<div>
等)(默认为item)
Menu::create('menu', [ 'auto_activate' => false, 'activate_parents' => true, 'active_class' => 'active', 'active_element' => 'item', // item|link 'restful' => true, 'share' => 'myMenu' // Will be available as `$myMenu` in blade files ]);