braunstetter / menu-bundle
在您的 symfony 项目中实现复杂菜单系统的简单方法。
Requires
- php: ^8.1
- braunstetter/helper: ^0.2.5
- symfony/framework-bundle: ^6.0|^7.0
- symfony/string: ^6.0|^7.0
- symfony/twig-bundle: ^6.0|^7.0
- symfony/yaml: ^6.0|^7.0
- webmozart/assert: ^1.11
Requires (Dev)
- matthiasnoback/symfony-dependency-injection-test: ^4.2
- nyholm/symfony-bundle-test: 1.8.1
- phpstan/phpstan: ^1.10
- psalm/plugin-phpunit: ^0.18.4
- symfony/event-dispatcher: ^5.3
- symfony/property-access: ^6.3
- symfony/test-pack: ^1.0
- symplify/easy-coding-standard: ^11.4
- vimeo/psalm: ^5.12
README
概述
braunstetter/menu-bundle
是一个强大的工具,旨在简化在 Symfony 项目中创建菜单的过程。它提供了一个简单直观的界面来创建和配置各种类型的菜单。
使用此组件的好处包括
- 无匹配复杂性:您可以使用 selectedSubnavItem 函数轻松激活菜单项,无需它是直接子项。
- 无需渲染系统:您可以使用此组件提供的渲染块,或获取原始数据并根据需要自定义它。
- 可重用性:您可以在应用程序的不同上下文中(菜单、面包屑)重用您的菜单类。
- 可扩展性:通过允许他人使用 MenuEvent 类扩展您完成的菜单,来扩展您的生态系统。
安装
要安装 MenuBundle,请运行以下命令
composer require braunstetter/menu-bundle
Symfony flex 会为您完成其余工作。
使用方法
安装后,您可以通过创建一个实现 MenuInterface
的类来创建菜单。以下是一个定义菜单的示例
<?php namespace App\Menu; use Braunstetter\MenuBundle\Contracts\MenuInterface; use Braunstetter\MenuBundle\Events\MenuEvent; use Braunstetter\MenuBundle\Factory\MenuItem; use Traversable; class MainMenu implements MenuInterface { public function define(): Traversable { yield MenuItem::linkToRoute('System', 'route_to_my_system', [], 'images/svg/system.svg')->setChildren(function () { yield MenuItem::linkToUrl('Section', 'https://my-site.com', MenuItem::TARGET_BLANK, 'images/svg/thunder.svg')->setChildren(function () { yield MenuItem::linkToRoute('Site', 'site', [], 'images/svg/align_justify.svg'); yield MenuItem::linkToRoute('Dashboard', 'cp_dashboard'); }); }); } }
图标
图像的基础路径是您的应用程序的 templates
文件夹。但由于此值将仅传递给 Twig 源函数,因此您还可以使用别名,例如 @my_bundle/images/svg/thunder.svg
。
Twig 辅助函数
在您的 twig 模板中,您可以使用 menu()
函数并传递 snake_cased 类名来打印菜单。
{{ menu('main_menu') }}
格式化后的结果
注意:此组件不包含 CSS。但如您所见,一旦使用 menu() 函数,就会打印出可以立即使用的样式化 HTML 标记。
菜单项类型
MenuItem::linkToRoute
MenuItem::linkToUrl
还有额外的
MenuItem::system
和MenuItem::section
。这些只是方便的静态方法,用于生成具有attr.class
设置为 system/section 的MenuItem::linkToRoute
项。MenuItem::system
和MenuItem::section
都可以具有空路由
yield MenuItem::system('System', null, [], 'images/svg/system.svg')
链接到路由
yield MenuItem::linkToRoute('Label', 'route-name', [], 'images/svg/align_justify.svg');
参数
- 显示的标签 - 您可以在这里或您的自定义模板中将其翻译。
- 路由名称。
- 路由参数。
- 图标(可选)
链接到 URL
yield MenuItem::linkToUrl('Some extern resource', 'https://my-site.com', MenuItem::TARGET_BLANK, 'images/svg/thunder.svg');
参数
- 标签
- 绝对 URL
- 目标(可选)
- 图标(可选)
除了将目标作为第三个参数传递之外,您还可以使用
$item->setTarget(MenuItem::TARGET_BLANK)
方法来更改/设置目标。有预定义的目标可供选择,这些目标在
'Braunstetter\MenuBundle\Items\MenuItem'
类中作为常量设置。
设置目标
如上所示 - linkToUrl
项提供了通过将目标作为静态方法的第三个参数传递来设置目标属性的可能性。这很有用,因为外部/绝对链接通常应作为 target="_blank"
链接打开。但如果您想更改任何其他链接的目标,您可以通过使用任何实现 MenuItemInterface
的 MenuItem
的 setTarget
方法来完成此操作。
以下是一个完整的示例 - 如您所见
- 仪表板 使用
setTarget
- 商店 使用
linkToRoute
方法的第三个参数设置目标
yield MenuItem::system('System', 'test', [], 'images/svg/system.svg') ->setChildren(function () { yield MenuItem::linkToRoute('Dashboard', 'dashboard')->setTarget(Item::TARGET_BLANK); yield MenuItem::linkToUrl('Shop', 'https://my-online-shop.com', Item::TARGET_BLANK, 'images/svg/thunder.svg'); });
自定义菜单项
您不仅限于使用内置菜单类型。您可以直接创建一个包含几个静态方法的类,以创建自己的菜单项工厂。或者直接传递一个新的Braunstetter\MenuBundle\Items\Item
。
yield (new Item('My label', 'images/svg/system.svg', ['linkAttr' => ['target' => Item::TARGET_BLANK]]));
参数
- 标签
- 图标(可选)
- 选项(可选)
如果您使用的是默认渲染块,那么$options['attr']
、$options['linkAttr']
和$options['target']
对渲染结果有任何影响。
将目标作为选项传递实际上等同于将其直接传递给
$options['linkAttr']
。它只是一个快捷方式。
如果您需要传递更多选项,那么现在是创建一个自定义的Braunstetter\MenuBundle\Items\Item
扩展类的好时机。这样,您就能在类中创建额外的属性,并在构造函数中使用$options
来填充它们。
面包屑
上一章中定义的相同菜单可以通过使用breadcrumbs()
twig函数来作为面包屑菜单使用。
{{ breadcrumbs('main_menu') }}
一个准备好被样式的标记将被渲染 - 由caret.svg
分隔。
breadcrumbs()
和menu()
函数之间的主要区别是,breadcrumbs()
仅在包含某些活动路由时输出一个菜单树行。然后迭代停止,并打印这个活动树叶。
自行渲染菜单。
有时您希望对菜单的渲染拥有完全的控制权,并确保所有必要的信息都已到位。这就是为什么您可以像上面描述的那样使用menu_result()
和breadcrumbs_result()
twig函数。唯一的区别是,现在您拥有原始数据而不是标记。
{% set items = menu_result('main_menu') %} {% for item in items %} {# do whatever you want with the data #} {% endfor %}
如果您决定走这条路,您可能会发现使用在
menu_blocks.html.twig
和breadcrumb_menu_blocks.html.twig
允许他人通过MenuEvents扩展您的菜单
如果您构建了一个生态系统,那么您可能也想给其他用户和/或扩展包提供扩展或更改您菜单的选项。
使用菜单事件来做这件事非常简单直接。在您将Symfony\Contracts\EventDispatcher\EventDispatcherInterface
注入到菜单类的构造函数后,您就可以派发事件了
$siteLinksEvent = new MenuEvent( yield MenuItem::linkToRoute('Site', 'site', [], 'images/svg/align_justify.svg'); yield MenuItem::linkToRoute('Dashboard', 'cp_dashboard'); ); $this->eventDispatcher->dispatch($siteLinksEvent, 'app.main_menu'); yield from $siteLinksEvent->items;
一旦您将菜单保存到一个变量中(在这种情况下为$siteLinks
),您就可以创建一个new MenuEvent($siteLinks)
。现在您可以派发事件(例如,'app.main_menu')
Braunstetter\MenuBundle\Events\MenuEvent
包含菜单项和
可以前置/后置菜单项。您可以创建一个事件订阅者
<?php namespace App\EventSubscriber; use Braunstetter\MenuBundle\Events\MenuEvent; use Braunstetter\MenuBundle\Factory\MenuItem; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Generator; class MenuSubscriber implements EventSubscriberInterface { /** * @param MenuEvent $event */ public function onAppMainMenu(MenuEvent $event) { $event->prepend(function () { yield MenuItem::linkToRoute('Prepended', 'other'); }); $event->append(function () { yield MenuItem::linkToRoute('Appended', 'other'); }); } public static function getSubscribedEvents(): array { return [ 'app.main_menu' => 'onAppMainMenu', ]; } }
这样,您可以非常容易地为您的软件生态系统构建一个可扩展的菜单系统。
激活非直接子项的菜单项
有时并不是每个路由都是直接位于父项的叶子节点,我们只能依赖于这些活动路径。然后您需要告诉您的菜单系统“某种方式”激活一个看似无关的菜单项(并激活其活动路径)。
而不是对自定义匹配器疯狂操作,您可以直接这样做
{% set selectedSubnavItem = 'snake_cased_item_label' %}
// you can also pass an array of items to activate multiple items at once
// selectedSubnavItem = ['snake_cased_item_label', 'another_snake_cased_item_label']
{{ menu('main_menu') }}
匹配此路由的菜单项将是活动的,并且所有父项都将位于活动路径中。
注意:
selectedSubnavItem
必须位于全局twig作用域内 - 因此在您的块之间定义它,或从控制器内部传递它作为变量。
selectedSubnavItem
的值必须等于MenuItem的句柄(蛇形标签)。