cyberpunkcodes / laravel-menu-helper
Laravel 菜单助手 - 一个简化创建各种类型菜单的 Laravel 扩展包。
Requires
- php: ^7.3|^8.0
- laravel/framework: ^8.0|^9.0|^10.0
README
Laravel 菜单助手可以使处理菜单变得极其简单。无论是顶部导航栏、左侧侧边栏、常见问题解答还是任何其他内容。如果是菜单,它都能帮到您:)
菜单可能会很麻烦,尤其是在您需要处理各种逻辑时,比如确定用户是否在匹配的路由上、添加一个 active
类,或者您需要动态修改数据。
使用 Laravel 菜单助手,您可以通过配置数组构建菜单,将数据视图组件分解以便更容易地管理。这些组件有它们自己的方法,允许您控制您的菜单。
由于 Laravel 可以缓存配置文件,因此您不必使用它们。可以直接将项目传递给组件,允许您从数据库加载它们、使用 RBAC 来切换是否显示项目、在数据上执行集合操作、缓存每个用户的菜单,等等,满足您的各种需求。
[目录]
简介
简单的 basic
菜单模板是单级菜单。简单的菜单最明显的例子是常见问题解答部分。您有多个部分,每个部分都有一个标题(title
)和 text
。这不必是常见问题解答部分,您可以用它来在主页上创建定价框或任何不需要多级的内容。
高级的 advanced
模板用于多级菜单。可以是顶部导航栏、侧边栏、移动菜单等。高级模板包括方法,允许您确定当前菜单项(在循环中)是否是当前路由,以便您执行不同的操作,例如应用类,或检测菜单项是否有子项(下拉菜单)。这使您能够轻松创建多级菜单。
在内部,basic
和 advanced
之间没有区别。它们都使用相同的组件,区别在于它们的使用方式。所有相同的方法都是可用的。在简单的菜单中,我们只有一个标题和文本,并且不会检查是否有子项或我们是否在特定的路由上。尽管如此,您可以使用这些方法来这样做。使用基本模板不会限制您。模板只是以不同的方式看待菜单。仅仅因为基本模板使用了 text
字段,并不意味着高级不能使用。您可以用它来传递弹出悬停消息。仅仅因为高级模板使用了图标,并不意味着您不能在基本中使用。您只需要使用视图组件中的方法来利用它们。如果您正在创建多级菜单,请从高级开始,因为它已经包含了子项视图组件和处理循环的方法。
请注意,您还可以创建自己的模板以在多个项目中重用。您可以为需要的基本 CSS 类创建自己的 Bulma 模板。将其放入其中,进行修改,并在未来的项目中用 5 分钟构建菜单。
安装
要安装此包,请运行
composer require cyberpunkcodes/laravel-menu-helper
建议您首次使用此包时先查看演示,以便更好地理解您正在处理的内容,并以此为基础进行扩展。包含两种类型的菜单占位符以供开始使用:基本
和 高级
。基本
和 高级
都提供了 Bootstrap 和 Tailwind 占位符可供选择:basic-bootstrap
、basic-tailwind
、advanced-bootstrap
、advanced-tailwind
。
演示模式
要启用演示模式,请在项目的 .env
文件中添加 Menu Helper 演示模式键。
MH_DEMOMODE=true
演示模式激活演示路由、视图和配置,以便您可以查看一些实时示例。有一个演示页面可以帮助您浏览和查看各种菜单示例。
导航到 /demo/
,您将看到两个选项,Tailwind 和 Bootstrap。每个选项都有自己的基本和高级示例。
Tailwind 和 Bootstrap 演示正在使用它们的 CDN 链接,因此它们在这个演示中可以立即使用。当您生成自己的菜单时,您需要有自己的 CSS 和 JavaScript(如有必要)。这不会处理任何 CSS 或 JavaScript 来使菜单工作。它假设您已经将它们安装到模板中,因为在这个时候,您应该已经安装了。
构建
首先,让我们发布配置文件
php artisan vendor:publish --tag=menuhelper-config
这将生成您的应用配置目录中的 menu-helper
和 menu-helper-secondary
。
menu-helper
配置文件是全局配置。目的是放置将在每个(或大多数)页面加载时加载的所有菜单。例如,像顶部导航栏或左侧边栏这样的东西。
menu-helper-secondary
配置文件用于不在每个页面上的东西。由于菜单配置数组可能相当大,我们将其拆分。例如,您的常见问题解答菜单的配置将放在这里。只有在您的常见问题解答页面上需要时才需要它,因此它不需要为每次其他页面加载而携带。
现在,您已经使用 Composer 安装了此包并查看了演示,您现在可以构建自己的菜单。您只需知道您想用作基准的占位符。如果您不想使用 Tailwind 或 Bootstrap,则可以选择任何一个并修改它以适应您的需求。
make:menu name --stub=
命令中的占位符值与演示中的菜单名称相对应。所以如果您想查看 Bootstrap 侧边栏的示例,请打开 /demo/bootstrap/advanced
URL。如果您想将其用作起始菜单模板,可以使用 php artisan make:menu left-sidebar --stub=bootstrap-advanced
生成它,其中 left-sidebar
是您要创建的菜单名称。
请注意,占位符名称与演示路由匹配。/demo/bootstrap/basic
是 bootstrap-basic
。/demo/tailwind/advanced
是 tailwind-advanced
。如果您创建了自定义占位符,则它是文件夹名称。有关更多信息,请参阅创建占位符部分。
如果您没有指定占位符,并且仅运行 php artisan make:menu my-menu
,则默认为 Tailwind 高级占位符/模板。
基本菜单配置
我们可以通过菜单配置数组传递很多内容。现在,让我们保持简单。有关显示/隐藏菜单项或子项、检测项目是否与当前路由匹配等信息,请参阅高级使用部分。
常见问题解答示例
让我们从一个基于基本占位符的常见问题解答示例开始
[ 'my-menu' => [ 'expanded' => true, // `$isExpanded()` - Optional - Default: false 'target' => 'collapseOne', // `$getTarget()` - Optional 'id' => 'headingOne', // `$getId()` - Optional - A random `mh_` prefixed id if empty 'title' => 'Accordion 1', // `$getTitle()` 'text' => "<strong>This is the first item's...", // `$getText()` ], ]
注意:my-menu
是您使用 make:menu
命令生成的菜单名称。
在这里,真正需要的是 title
和 text
。其余的选项在用 JavaScript 使用时很有帮助。
记住,所有选项对我们来说都仍然可用。您可以指定图标、自定义数据、链接, whatever 您需要。这取决于您如何在视图组件中使用这些数据(逻辑流程和方法)。由于这是一个单级菜单,没有子项,因此不需要 menu-subitem.blade.php
视图文件。这正是基本和高级之间的唯一区别。高级假设您将需要多级,因此包括 menu-subitem
blade 文件。如果您只想有一个级别的菜单,可以使用高级模板并删除子项视图文件,同时也可以在 menu-item
blade 文件中删除 if/else 检查。
导航栏示例
大多数人都会寻找导航栏菜单。所以如果您不确定,那么您很可能就是。
下面是一个使用高级模板构建的侧边栏菜单示例
[ 'my-menu' => [ // start: single item [ 'title' => 'Home', // `$getTitle()` 'icon' => '<i class="fa-solid fa-fw fa-house-chimney"></i>', // `$hasIcon()`, `$getIcon()` 'route' => 'dashboard', // named route - `route` key only supports a single route as a string //'routes' => ['dashboard'], // we could also use an array list of all routes that match 'link' => ['dashboard'], // `$getLink()`named route for the link, null = no link ], // end: single item // start: dropdown item [ 'title' => 'Posts', 'icon' => '<i class="fa-solid fa-fw fa-book"></i>', 'routes' => ['posts.*'], // route matching: array list of all routes that match 'items' => [ // first sub-item [ 'title' => 'All Posts', 'route' => ['posts.index'], 'link' => ['posts.index'], ], // second sub-item [ 'title' => 'Create Post', 'route' => ['posts.create'], 'link' => ['posts.create', ['foo' => 'bar']], ], ], ], // end: dropdown item ] ]
注意:my-menu
是您使用 make:menu
命令生成的菜单名称。
请注意,这个数组有点不同。它没有 text
,而是有一个 items
键。Items 保存每个 "子项" 的数组。
"首页项" 没有任何 "items",或 "子项",正如在视图组件中所称呼的那样。它是第一级,或者如果您不需要子项或子菜单,则是唯一级别。它也支持路由匹配。这种路由匹配使我们能够对菜单项进行不同的处理。我们可以给它一个 active
类来改变背景、字体颜色,或者我们想要的任何东西。
"个人资料项" 有 "子项",键为 items
。它使用路由匹配来匹配 posts.*
通配符。我们可以使用此来添加一个 expanded
类,以显示子项。它还有一个 icon
,可以是您需要的任何 HTML。您需要自己安装任何额外的字体或字体库。
"个人资料子项" 更简单,使用 title
、route
和 link
。同样,这里也支持路由匹配。通常它将是确切的路径,而不是通配符。
构建菜单视图
现在您有了菜单配置数组,您需要视图文件来将它们整合在一起。这就是您对演示理解的地方。或者..您也可以猜测并随机尝试。无论什么对您来说都行 :)
以下是制作菜单命令的格式
php artisan make:menu {name} {--stub=tailwind-advanced}
注意:如果您没有使用 --stub
选项指定,则使用 tailwind-advanced
。
包含的模板
- tailwind-advanced
- tailwind-basic
- bootstrap-advanced
- bootstrap-basic
您还可以创建自己的模板。有关更多信息,请参阅 创建模板 部分。
使用 bootstrap-advanced
作为样板生成名为 my-menu
的菜单的示例
php artisan make:menu my-menu --stub=bootstrap-advanced
菜单视图将生成在 resources/views/vendor/menuhelper/my-menu
。
注意:菜单名称必须与您的配置文件中使用的名称匹配。此外,此操作不会安装任何 CSS 或 JavaScript。如果您正在使用 Bootstrap 或 Tailwind、FontAwesome 等,那么您需要自己安装它们。
将菜单添加到布局中
好的,现在您已经创建了配置和视图。是时候将菜单添加到布局中了。
无论您想在何处放置菜单,请添加 Menu Helper 菜单组件
<x-menuhelper::menu scope="global" menu="my-menu" />
作用域可以是 global
或 secondary
,并且 menu
必须与您在先前步骤中使用的菜单名称匹配。建议始终传递作用域。如果您的菜单是二级菜单,并且您没有指定,它将首先在全局菜单配置中查找。然后是二级菜单,如果没有找到。我们可以节省一些资源并指定 secondary
作用域。即使它是全局的,也始终指定它也可以帮助提高代码的可读性。
现在您的菜单已创建并准备好供您自定义。
高级菜单配置
@待办
自定义
从这里开始变得有趣,您的菜单将变得生动。记住,没有包含 CSS 或 JavaScript。您必须自己完成这些。
您需要了解构成菜单的视图组件以及可帮助您快速轻松地构建菜单的辅助函数。
视图组件文件
基本示例和高级示例之间的唯一区别是,基本示例没有 menu-subitem
,并且它们的配置数组没有 items
。它只是 title
和 text
。因此,基本上是 1 级。高级示例确实有 menu-subitem
视图文件,并且它们的配置数组有 items
。这意味着它有多于 1 级,父元素可以有子元素。类似于顶部导航栏或左侧边栏。
- menu.blade.php - 菜单周围的内容包装
- menu-items.blade.php - 通常这里没有任何样式,它只是循环并创建
menu-item
- menu-item.blade.php - 实际的菜单项本身(例如:账户)
- menu-subitem.blade.php - 上级项的子项(例如:在账户项下更改密码)
再次强调,如果您的菜单是并且仅是 1 级,则可能不需要 menu-subitem
。
辅助方法
每个视图组件都有自己的辅助方法可供使用。示例展示了您在使用 CSS 和 JavaScript 配对后,可以拉出一个不错的菜单的一些最必要的辅助方法。一些方法是针对组件的,而另一些方法则在整个链中可用。
菜单组件
以下方法是菜单组件 menu.blade.php
可用的
- getConfig() - 可能不需要这个。它包含整个菜单的配置。
- getScope() -
global
或secondary
。不清楚为什么你需要这个,但它可用 :) - getMenu() - 菜单的名称,例如:
left-sidebar
- getMenuId() - 菜单的 id,用于在 HTML 中使用。如果未定义,则生成一个以
mh_
前缀的随机 id - getItems() - 获取菜单项
- showHeader() - (bool) 是否显示标题
- getHeaderText() - 标题文本。对于在菜单之前显示文本很有用,例如
Navigation
要渲染菜单组件,请将其放置在模板/布局中您希望它出现的地方
<x-menuhelper::menu scope="global" menu="left-sidebar" />
请记住,作用域必须是 global
或 secondary
,并且与配置文件匹配。菜单必须与您的配置文件中配置的菜单匹配,并且与您的 resources/views/vendor/menuhelper
目录中的菜单名称匹配,例如: resources/views/vendor/menuhelper/left-sidebar
。
菜单项组件
菜单项组件实际上非常简单。它只是简单地将菜单中的数据传递下去。它是对每个循环传递其已知内容的菜单项。
以下方法是菜单项组件 menu-items.blade.php
可用的
- getParentId() - 获取整个菜单的 id(与上面的菜单组件中的
getMenuId()
相同) - getMenu() - 菜单的名称,例如:
left-sidebar
- getItems() - 获取菜单项
要渲染菜单项组件,请从菜单组件中调用它
<x-menuhelper::menuItems :menu="$getMenu()" :items="$getItems()" :parent-id="$getMenuId()" />
菜单项组件
菜单项组件本身就是第一级项。此项目可以有子元素。如果您查看任何高级占位符示例,我们会使用 $isDropdown()
来判断该项是否是下拉项。如果是,我们会做一些不同的操作并渲染一个 menu-subitem
组件。如果不是,则直接使用项目的链接/图标/标题。如果您的菜单只有一级,则无需检查是否为下拉菜单,可以直接显示数据。
以下方法是可用的 menu-item.blade.php
菜单项组件:
- shouldRender() - (bool) 是否显示该项
- getParentId() - 主菜单的 ID(一路传递下来)
- getId() - 项目的 ID
- getMenu() - 菜单的名称
- getItem() - 项目的数据
- isDropdown() - (bool) 项是否有子项(子元素)
- hasTitle() - (bool) 项是否有标题
- getTitle() - 获取项的标题
- getText() - 获取项的文本(用于 FAQ 使用,不用于侧边栏)
- hasIcon() - (bool) 项是否有图标
- getIcon() - 获取图标(html)
- hasLink() - (bool) 项是否有链接
- getLink() - 获取项的链接
- hasSubItems() - 与
isDropdown()
相同 - getSubItems() - 获取子项
- getRoutes() - 获取项的路由(由
routeIsMatch()
使用) - routeIsMatch($returnAsString = false) - 判断当前路由是否与项的路由匹配
- classIfRoute($class = 'active', $fallback = '') - 如果路由匹配则返回一个类,否则返回另一个类
- classIfNotRoute($class = 'active') - 如果路由不匹配则返回一个类
- boolIfRoute($returnAsString = true) - 如果路由匹配则返回一个布尔值(或字符串表示形式)
- getCustom($key) - 获取传递给项目的自定义数据的值
- getLoop() - 获取 Laravel 循环对象(由
menu-items
中创建的循环传递给menu-item
) - isFirstLoop() - 是否是循环的第一个迭代(正在处理第一个项)
- isLastLoop() - 是否是循环的最后一个迭代(正在处理最后一个项)
- getTarget() - 获取项的目标(对 JS 很有用)
- isExpanded($returnAsString = false) - 项是否(或应该)展开。在配置中
'expanded' => true
- isCollapsed($returnAsString = false) - 与
isExpanded()
相反。如果项未展开,则折叠
要渲染菜单项组件,请在 foreach 循环中从菜单项组件中调用它
@foreach($getItems() as $item) <x-menuhelper::menuItem :menu="$getMenu()" :item="$item" :parent-id="$getParentId()" :loop="$loop" /> @endforeach
菜单子项
由于子项基本上是一个没有自己子元素的项,因此它有一些相同的辅助方法。
- shouldRender() - (bool) 子项是否显示
- getMenu() - 菜单的名称
- getSubItem() - 获取子项的数据
- getRoute() - 获取子项的路由
- routeIsMatch($returnAsString = false) - 判断当前路由是否与子项的路由匹配
- classIfRoute($class = 'active', $fallback = '') - 如果路由匹配则返回一个类,否则返回另一个类
- boolIfRoute($returnString = true) - 如果路由匹配则返回一个布尔值(或字符串表示形式)
- hasLink() - (bool) 子项是否有链接
- getLink($fallback = '') - 获取子项的链接
- getTitle() - 获取子项的标题
要渲染菜单子项组件,从菜单项组件中调用它
@foreach ($getSubItems() as $subItem) <x-menuhelper::menuSubItem :menu="$getMenu()" :subItem="$subItem" /> @endforeach
在您的菜单项组件中,您会希望通过使用 isDropdown()
或 hasSubItems()
方法之一来检查项是否有子项。
请参阅源代码以获得更好的理解:src/View/Components/MenuItem.php
创建占位符
@待办
这个仓库和文档尚未完善。我对代码做了一些修改,可能已经影响了文档的准确性。我需要将其整理好并使用这个菜单辅助工具在项目中,所以只需要将其推送到版本控制系统。
自定义存根可能还不能正常工作。因为存在白名单检查,所以可能需要检查存根目录,看看是否存在同名目录。
欢迎提出问题或深入代码并提交一个Pull Request。