qoraiche/laravel-menu-enhanced

在 Laravel 5 中快速创建菜单的方法

v1.0.1 2023-02-18 16:48 UTC

This package is auto-updated.

Last update: 2024-09-18 20:09:40 UTC


README

Latest Stable Version Latest Unstable Version Total Downloads License

在 Laravel 6, 7 和 8 中快速简单地创建菜单的途径

对于 Laravel 4.x,请检查 版本 1.5.0

文档

安装

composer require qoraiche/laravel-menu-enhanced

如果您使用的是 Laravel 5.5,您不需要编辑您的 config/app.php,如果您使用的是 Laravel 的早期版本,请执行以下操作

将 Laravel Menu 服务提供者添加到 providers 数组中的 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,

        ...

],

config/app.php 的末尾添加 'Menu' => Lavary\Menu\Facade::class$aliases 数组

'aliases' => [

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

],

这将在 Laravel 中注册该包并创建一个名为 Menu 的别名。

要使用自己的设置,请发布配置。

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

入门

您可以在 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()来确保在给定名称的菜单尚不存在时才调用make回调。如果您在多个位置有条件地创建相同的菜单,且不确定是否其他条件已导致菜单被创建,这可能会很有用。

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>

这就是全部内容了!

路由

URL

您可以通过将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');    

引用项目

您可以使用以下方法在代码中访问定义的项目。

通过标题获取项目

使用camel case在$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 集合

获取第一个项目

$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等);您可能会遇到需要添加一些HTML属性到 <a> 标签的情况。

每个 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 类添加到锚点元素(divli)上,而不是包装元素

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

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

Laravel Menu 会根据您注册项目时的当前 URI 自动为您完成此操作。

您还可以在位于包配置目录中的 settings.php 文件中设置要激活的元素(项目或链接)

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

RESTful URL

只要在 config/settings.php 文件中将 restful 选项设置为 true,就支持 RESTful URL。例如,具有 resource URL 的菜单项将通过 resource/slugresource/slug/edit 激活。

你可能遇到应用程序位于子目录而不是根目录或资源具有共同前缀的情况;在这种情况下,您需要将 rest_base 选项设置为适当的前缀以获得更好的 RESTful 激活支持。rest_base 可以接受简单字符串、字符串数组或函数调用的值。

URL 通配符

laravel-menu 允许您为特定项定义一个模式,如果自动激活不起作用。

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

因此,this-is-another-urlthis-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>
 *
 */

附加和预附加

您可以在定义之后将 HTML 或纯文本 appendprepend 到每个项的标题中

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>

您还可以在集合上调用 prependappend

之前和之后

允许您添加任意 HTML 块而不是下拉列表。还有许多其他可能性。与 appendprepend 不同,beforeafter 在标签 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>&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 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()

$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>
 */

菜单组

有时您可能需要在多个项之间共享属性。而不是为每个项指定属性和选项,您可以使用菜单组功能。

注意:此功能与 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');

});

属性 styledata-role 将应用于 AboutServices 项。

<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 的菜单项进行前缀处理,但不是 routeaction

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');

});

这将生成

<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风格的菜单项,您可以将这些菜单项包含到基于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/settings.php中的data-toggle-attribute选项从data-toggle更改为data-bs-toggle

给子项添加类属性

与为菜单的ulol添加类一样,子菜单也可以添加类。向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代码,如navuldiv标签,它们包围着菜单项。
  • 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标签具有类dropdowndata-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):'';

配置

您可以在config/settings.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 [
    'default' => [
        'auto_activate'    => true,
        'activate_parents' => true,
        'active_class'     => 'active',
        'active_element'   => 'item',    // item|link
        'restful'          => true,
    ],
    'yourmenuname' => [
        'auto_activate'    => false
    ],
];

或者,您可以使用以下方法覆盖默认设置。或者,您可以为菜单添加新的自定义设置。

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',
    ],
];

示例 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',
]
*/

如果您需要帮助

请使用 GitHub 的问题提交所有问题和疑问,我会尽力帮助您。

贡献

如果您能改进或添加任何功能,请随时提交拉取请求。

我们目前正在使用 PSR-2+Symfony 格式化。这很容易实现并通过PHP Coding Standards Fixer进行检查。

安装 php-cs-fixer 并将其添加到您的路径后,在提交之前,在 laravel-menu 文件夹中简单运行以下命令。

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

虽然大家对格式化有不同的看法,但这个工具将帮助提供方便的一致性。

致谢

许可协议

Laravel-Menu 是在 MIT 许可证下免费软件。