rinvex/laravel-menus

Rinvex Menus 是一个简单的 Laravel 菜单构建器包,支持使用展示器实现全灵活性的分层结构、排序和样式,以便轻松进行样式设计和自定义菜单渲染结构。

v7.1.2 2023-07-03 13:28 UTC

README

Rinvex Menus 是一个简单的 Laravel 菜单构建器包,支持使用展示器实现全灵活性的分层结构、排序和样式,以便轻松进行样式设计和自定义菜单渲染结构。

Packagist Scrutinizer Code Quality Travis StyleCI License

鸣谢声明

本包是基于nWidart/laravel-menus的重新编写版,而后者本身是基于pingpong-labs/menus的分支,原始的鸣谢归功于这两者。经过广泛的重写以消除技术债务和移除旧代码,因此请注意,API 已不同并且与原始包不兼容。此分支的主要目标是

  • 简化菜单注册
  • 清理代码并提高可读性
  • 默认启用排序顺序功能
  • 移除旧代码,消除技术债务
  • 允许以最小或无核心更改进行扩展
  • 强制一致性并使 API 直观
  • 新增侧边栏菜单功能,以不同方式处理下拉菜单
  • 与 Laravel 的认证授权功能集成,以便根据权限简化隐藏/显示菜单

安装

  1. 通过 composer 安装包

    composer require rinvex/laravel-menus
  2. 可选,您可以通过运行以下命令发布视图文件

    php artisan vendor:publish --tag="rinvex-menus-views"
  3. 完成!

用法

创建新菜单

要注册新菜单,只需调用 Menu::register() 方法。它接受两个参数,第一个是菜单标题,第二个是用于定义菜单项的回调函数。请参见以下示例

use Rinvex\Menus\Models\MenuItem;
use Rinvex\Menus\Models\MenuGenerator;

Menu::register('frontend.sidebar', function(MenuGenerator $menu) {
    // Add menu header
    $menu->header('Header Title');

    // Add url menu item
    $menu->url('url/path', 'Menu Title #1');

    // Add route menu item
    $menu->route(['route.name'], 'Menu Title #2');

    // Add menu divider
    $menu->divider();

    // Add menu dropdown (it can have childs too)
    $menu->dropdown(function(MenuItem $dropdown) {
        $dropdown->header('Child Header Title');
        $dropdown->url('url/path', 'Child Menu Title #1');
        $dropdown->route(['route.name'], 'Child Menu Title #2');
        $dropdown->divider();
    }, 'Dropdown Title', 50, 'fa fa-arrows', ['data-attribute' => 'something']);
});

所有 urlrouteheaderdropdown 方法都有标准的 API,如:$menu->method('data', 'title', 'order', 'icon', 'linkAttributes', 'itemAttributes'),直观且易于理解。只有第一个参数是必需的,并且对每种方法而言都是不同的,但其余的都是相同的且为可选。其中 header 接受字符串标题,url: 字符串链接,route: 包含字符串路由名称和可选路由参数的数组,dropdown: 用于定义子项的回调函数,其他参数都是可选的。

注意

  • 菜单项默认按升序排序。如果您不需要排序,只需在定义菜单时忽略 order 参数即可,因为它是可选的。这样菜单项将按添加的顺序显示。
  • icon 参数接受一个 CSS 类名,例如 fa fa-user 用于 fontawesome,而 linkAttributes 参数接受一个数组,其中包含您想要添加到菜单项的任何其他 HTML 属性。
  • 您可以在父下拉菜单内创建子下拉菜单来创建多级菜单项,并且没有限制,因此您可以创建所需的深度结构。
  • 您可以使用Menu::register()方法创建多个具有不同名称的菜单,并在不同的位置调用它们。例如,如果您想创建一个顶部菜单和一个侧边栏菜单等。

修改现有菜单

要修改已在代码中其他地方添加的现有菜单项,您可以使用相同的注册方法。

Menu::register('frontend.sidebar', function(MenuGenerator $menu) {
    // Add url menu item above the dropdown we created before
    $menu->url('different/path', 'Menu Title #3', 40);
});

如您所见,我们刚刚修改了frontend.sidebar菜单,并在分隔符下方、下拉菜单上方添加了一个新的URL菜单项。看,就这么简单!

或者,您可以获取要修改的菜单的句柄,然后按您喜欢的任何方式使用它,如下所示

$sidebar = Menu::instance('frontend.sidebar');
$sidebar->url('new/url', 'Menu Title #4', 40);
$sidebar->route('some.new.route', 'Menu Title #5', 60);

有条件地隐藏菜单

要简单地隐藏任何菜单项,您可以使用以下任何一种方法

$sidebar->url('one/more/url', 'One more new item')->hideWhen(function () {
    return true; // Any expression
});

如您所见,hideWhen方法接受一个返回true或false的闭包。如果返回true,则菜单项将被隐藏,否则将显示,因此您可以将任何逻辑放在这里进行评估。

作为语法糖,还有一些使生活更简单的更多方法!请参阅ifUserifGuestifCan方法

// Only display if logged condition is true
$sidebar->url('one/more/url', 'One more new item')->if(true);

// Only display if logged in user (authenticated)
$sidebar->url('one/more/url', 'One more new item')->ifUser();

// Only display if guest not yet authenticated
$sidebar->url('one/more/url', 'One more new item')->ifGuest();

// Only display if logged in user has required ability (authorization)
$sidebar->url('one/more/url', 'One more new item')->ifCan('do-some-ability');

当然,如您所期望的,所有这些方法都运行顺畅,并且与Laravel的默认认证授权功能完全集成。

要轻松控制菜单隐藏状态,您可以无限级联所有隐藏方法,所有隐藏回调将按顺序堆叠并执行。它将在第一个正条件结果时停止执行。示例

// Only display if logged in user has required ability (authorization)
$sidebar->url('one/more/url', 'One more new item')->ifUser()->ifCan('do-some-ability')->hideWhen(function () {
    return true; // Any expression
});

此示例表示,只有具有do-some-ability权限的用户,并且当hideWhen回调表达式返回true时,菜单才会显示。

有条件地激活菜单

要根据路由名称有条件地激活菜单,您可以设置路由前缀以匹配。如果当前路由名称包含该前缀,则自动激活菜单项。这样,我们可以通过访问子页面来激活父菜单项。示例

$menu->route(['route.name.example'], 'Menu Title #2')->activateOnRoute('route.name');

现在,当我们访问以route.name前缀的路由时,我们的具有route.name.example路由的菜单将自动激活。

或者,您可以通过在解析为布尔值的回调中添加自己的逻辑来完全控制该菜单项何时激活,如下所示

$menu->route(['route.name.example'], 'Menu Title #2')->activateWhen(function () {
    return true; // Any expression
});

搜索现有菜单项

您还可以使用findBy搜索特定的菜单项,例如下拉菜单,并直接向其添加子项。findBy方法可以通过任何属性在您的菜单中进行搜索,并接受两个必需参数,要搜索的属性名称和值,您还可以传递第三个参数作为回调来定义子项(一种一次性修改下拉菜单的方法)。

Menu::register('frontend.sidebar', function(MenuGenerator $menu) {
    $menu->findBy('title', 'Dropdown Title', function (MenuItem $dropdown) { // Seach items by title
        $dropdown->route(['the.newest.route'], 'Yet another menu item', 15, 'fa fa-building-o');
    });
});

如果您需要更新特定的菜单项,或者例如您需要更改URL或重命名标题,这也完全可以通过使用上述相同的方法并做一个小调整来实现

Menu::register('frontend.sidebar', function(MenuGenerator $menu) {
    $menu->findBy('title', 'Yet another menu item', function (MenuItem $item) {
        $item->fill(['icon' => 'fa fa-business]);
    });
});

此代码搜索标题为'另一个菜单项'的菜单项,并只更新其图标。fill方法接受一个数组,其中包含您想更新的任何属性,并将其与原始值合并,从而覆盖菜单项定义。

菜单表示器

渲染菜单是最容易的部分,但让我们首先发现这个包使用的一些有趣的概念,表示器!

演示者就像是布局驱动器,它定义了菜单的渲染方式,每个菜单可能都不同。让我们通过一个简单的例子来解释:如果你的项目中包含多个使用不同CSS框架(如vanilla bootstrap和AdminLTE)的部分,你可以为它们创建两个不同的演示者(幸运的是,这两个框架已经内置,但你也可以为任何其他框架构建自己的演示者)。它的工作方式是创建演示者,将其与包注册,然后只需使用其名称。简而言之,即使你改变了整个布局或切换到另一个CSS框架,你的菜单定义也不会改变,你只需要更改你的演示者。

演示者还用于定义菜单结构的不同布局,这样你可以使用菜单来构建导航栏、下拉菜单或甚至是选项卡。这些都由你决定,使用相同的代码。只需连接你的演示者,你就可以开始了。默认情况下,已经为你内置了一些演示者。

  • navbar \Rinvex\Menus\Presenters\NavbarPresenter
  • navbar-right \Rinvex\Menus\Presenters\NavbarRightPresenter
  • nav-pills \Rinvex\Menus\Presenters\NavPillsPresenter
  • nav-tab \Rinvex\Menus\Presenters\NavTabPresenter
  • sidebar \Rinvex\Menus\Presenters\SidebarMenuPresenter
  • navmenu \Rinvex\Menus\Presenters\NavMenuPresenter
  • adminlte \Rinvex\Menus\Presenters\AdminltePresenter

所有这些除了AdminLTE都基于Bootstrap构建,但你也可以构建自己的。你总是使用别名,而不是完整的类路径。

创建新的演示者

要构建自己的演示者,你需要

  • 创建一个新的PHP类,该类实现了\Rinvex\Menus\Contracts\PresenterContract
  • 将你的演示者与包注册:app('rinvex.menus.presenters')->put('new-presenter', \Your\New\Presenter\ClassPresenter::class)

就这样,你的新演示者就准备好了,可以通过其名称new-presenter使用。参见Rinvex\Menus\Presenters\AdminltePresenter的源代码以获取真实示例。

查看演示者

除了上述基于类的演示者之外,你还可以使用基于视图的演示者。幸运的是,也有内置的bootstrap视图可供使用,你也可以创建自己的视图。

这里没有复杂的内容需要解释,只需将视图演示者视为正常的Laravel视图,因为它们确实是,没有特别之处。唯一的区别是在渲染菜单时,你可以设置你偏好的演示者。如果提供了基于视图的演示者和基于类的演示者,基于视图的演示者具有优先级,如果没有提供任何演示者,它将回退到默认的内置基于类的演示者。默认情况下,已经为你内置了一些基于视图的演示者。

  • rinvex/menus::menu 纯菜单
  • rinvex/menus::default Bootstrap Navbar(默认)
  • rinvex/menus::navbar-left Bootstrap Navbar 左
  • rinvex/menus::navbar-right Bootstrap Navbar 右
  • rinvex/menus::nav-tabs Bootstrap Nav Tabs
  • rinvex/menus::nav-tabs-justified Bootstrap Nav Tabs Justified
  • rinvex/menus::nav-pills Bootstrap Nav Pills
  • rinvex/menus::nav-pills-stacked Bootstrap Nav Pills Stacked
  • rinvex/menus::nav-pills-justified Bootstrap Nav Pills Justified

渲染现有菜单

要渲染菜单,你可以使用以下方法的Menu::render()

Menu::render('frontend.sidebar');

正如你在方法定义中所看到的,还有三个可选参数需要解释:public function render(string $name, string $presenter = null, array $bindings = [], bool $specialSidebar = false)presenter参数指定了菜单的渲染方式,bindings是一种简单的方式来搜索和替换标题占位符(更多内容将在下面介绍),而specialSidebar是一个标志,用于以显示每个组上方的标题而不是可折叠下拉菜单的方式来处理侧边栏下拉菜单(beta功能)。

数据绑定

当你定义一个新的菜单时,你可以在标题中放置占位符,然后在渲染时可以传递绑定以在运行时进行替换。很有趣,对吧?请看以下示例

// Define new menu item with title placeholder
$sidebar = Menu::instance('frontend.sidebar');
$sidebar->url('very/new/url', 'Welcome {user}');

// Render menu and bind data on runtime
Menu::render('frontend.sidebar', null, ['user' => 'Omran']);

如您所见,我们定义了一个新的菜单项,其中包含一个 {user} 占位符,并且在渲染菜单时传递了所需的数据进行绑定。它将在运行时进行搜索/替换,因此您可以在菜单项标题中传递任何动态数据。

更改默认的展示器

您可以在菜单定义或菜单渲染步骤中更改默认的展示器,但最好在运行时渲染时这样做,以保持稳定的未更改的菜单结构,同时将布局相关的更改(如展示器)保持在前端层。

以下是两种更改展示器的方法

// Change menu presenter on definition
$sidebar = Menu::instance('frontend.sidebar');
$sidebar->setView('view-name'); // Set view-based presenter
$sidebar->setPresenter('presenter-name'); // Set class-based presenter

// Change menu presenter on rendering
Menu::render('frontend.sidebar', 'view-name'); // Set view-based presenter
Menu::render('frontend.sidebar', 'presenter-name'); // Set class-based presenter

您无需担心此包的工作原理以及它如何知道提供的展示器是基于视图还是基于类,但请记住,基于视图的展示器优先于基于类的展示器,因此此包将首先搜索带有提供名称的现有视图-展示器,如果找到,则立即使用并返回,否则将其次搜索带有提供名称的基于类的展示器。

变更日志

有关项目的完整历史记录,请参阅变更日志

支持

以下支持渠道随时可供您使用

贡献 & 协议

感谢您考虑为这个项目做出贡献!贡献指南可以在CONTRIBUTING.md中找到。

欢迎提出错误报告、功能请求和拉取请求。

安全漏洞

如果您在此项目中发现安全漏洞,请通过电子邮件发送至help@rinvex.com。所有安全漏洞都将得到及时处理。

关于Rinvex

Rinvex是一家自2016年6月成立以来位于埃及亚历山大的软件解决方案初创公司,专注于为中小企业提供集成企业解决方案。我们相信,我们的动力“价值、范围和影响”是我们与众不同的地方,并通过软件的力量释放我们哲学的无限可能性。我们喜欢称它为“生活的速度创新”。这就是我们如何为推进人类文明贡献我们的一份力量。

许可

此软件根据MIT许可(MIT)发布。

(c)2016-2022 Rinvex LLC,部分权利保留。