elmdash/menu
Composer 安装命令:
composer require elmdash/menu
包简介
A simple laravel menu structure
README 文档
README
Menus are but a simple tree structure. You can implement them a million ways, but this is a clean and minimal menu structure that handles the basic chores of setting active states and toggling menu items depending on a user's authorization within Laravel apps.
In your provider's boot method add something like:
$m = new \ElmDash\Menu\Menu('top'); // a login route, only visible to guests $m->add('access')->guests(); // menu items for users with proper permissions $m->add('books.edit', function (Menu $books) { $books->can('edit-books'); // It's optional to edit within callbacks. // However, this will be called once the menu is rendered // (instead of immediately) }); // an adit route, active for any account routes $m->add('account.edit')->match('account.*'); // you can also nest items $c = $m->add('event.create'); // you may add route parameters that are // included when determining active states $c->add('event.create')->params(['type' => 'basic']); $c->add('event.create')->params(['type' => 'special']); // more menu items $m->add('logout'); $this->app->instance('menu-top', $m);
Then in your view:
{% set menu = app.make('menu-top') %}
<div container>
<nav class="top">
<div class="logo"></div>
<nav>
{% for item in menu.children %}
{% set activeClass = item.isActive ? 'active' : '' %}
<span>
<a href="{{ item.href }}" class="{{ activeClass }}">
{{ item.label }}
</a>
</span>
<nav>
{% for subitem in item.children %}
{% set activeClass = subitem.isActive ? 'active': '' %}
<span>
<a href="{{ subitem.href }}" class="{{ activeClass }}">
{{ subitem.label }}
</a>
</span>
{% endfor %}
</nav>
{% endfor %}
</nav>
</nav>
</div>
Labels are set in lang/menu.php:
<?php return [ 'top' => [ 'access' => 'Sign up or sign in', 'account-edit' => 'Settings', 'event-create' => 'Create an event', 'logout' => 'Sign out', ], ];
Notice that the translation keys are the menu items' route names but with dashes instead of periods. Also, notice that the top-level name of the menu is used to group the translations.
Features
Root Menus
Top-level (root) menu objects will usually not be rendered and won't have a route to reference. You can give these routes names for convenience.
$menu = new Menu('top-left');
Adding items
The menu tree is made up of only Menu objects so every item in the tree has the same methods as every other item. That is, there is not "MenuRoot", "MenuGroup", or "MenuItem" differentiation.
Adding an item can be as simple as adding the name of a route. (At this time, you can't add any routes that aren't named.)
$menu->add('my.route');
This returns the newly created Menu child. You can use this for chaining.
$menu->add('my.route')->add('my.child.route');
It may be easier for several reasons, however, to use a callback to create your tree structures. First, it's more visually appealing and second, the functions are called once needed. This is handy for delaying menu initialization code until everything else has been initialized.
$menu->add('my.route', function (Menu $m) { // runs eventually, but not right away $m->add('my.child.route'); }); // this would trigger all callbacks and load the whole tree (likely called in your view) $children = $menu->children();
There are other means of adding items:
// prepending $menu->add('my.second.child'); $child = $menu->prepend('my.first.child'); // using groups (the name is _not_ a route name) // they do not have a route associated with them. So you can't, for example, // attempt to get the href of a group. $child->group('some.group.name', function (Menu $g) { $g->add('my.grand.child'); }); // you can also prepend a group $child->groupPrepend('some.other.group', function (Menu $g) { $g->add('my.step.grand.child'); });
Routes
The route name is typically specified when adding the menu item.
$menu->add('my.first.route'); // or more manually $child = $menu->add(); $child->route('my.route');
If your route takes parameters, you should add them to the menu. They will be used to render the correct href value and for determining the active menu item.
$menu->add('my.route.default', function (Menu $m) { // if the param is optional $m->params(['type' => null]); }); $menu->add('my.route.type', function (Menu $m) { $m->params(['type' => 'fish']); }); $menu->add('my.route.type', function (Menu $m) { $m->params(['type' => 'mammals']); });
The params used to find a match are just the params defined on the route. To add finer control, you can extract these params yourself. The result is cached so it will only get called once. For example, you may need to get a param value from a different route parameter (like during an edit route).
foreach ($contexts as $ctx) { // list by context (i.e. /media/{context}) $m->add('media.index') ->params(['context' => $ctx->handle]) ->activeFor('media.*') // get the context from the media when editing that media // (i.e. /media/item/{media}/edit) ->extractParams(function ($currentParams) { $media = array_get($currentParams, $media); if ($media) { return ['context' => $media->context->handle]; } return []; }); }
Getting the URL to render in your view is simply like this:
<a href="{{ item.href }}" class="{{ item.isActive ? 'active' : '' }}"> {{ item.label }} </a>
Or if you need absolute URLs
<a href="{{ item.href(true) }}" class="{{ item.isActive ? 'active' : '' }}"> {{ item.label }} </a>
Active menu items
When a child item is active, all parents will be considered active. To find out which menu item matches the currently active URL, we compare the current route's name with each menu item's route name. For example, the "users.account" menu item will be active when the "users.account" route is active. It's just a strict name (and possibly parameters) comparison.
However, you may have more than one view per your lowest level of menu items. You'll need a way to specify other named routes that should mark a menu item as active. For that we have globbing:
// single glob $users->activeFor('users.*'); // multi glob $settings->activeFor('users.settings.*|account|account.edit.*'); // regex $account->activeFor('/^users\.account\..*$/');
Again, we're only working with named routes, so make sure your routes are all named.
As shown above, to find out if an item is active in your view just call item.isActive.
For submenus, you may also need to get the currently active item of some root menu object.
{% set menu = app.make('nav-top-right').activeChild %}
Access
Menu items can be hidden for users who don't have permission. This just uses Laravel's authorization policies.
$left->add('wiki.index')->can('edit-wiki');
Menu items that are not visible to the current user will not be returned by the menu.children function. If a parent item is not visible, then the children will also not be marked as visible. In submenus, you may need to check for visibility directly (like, if you are not looping directly over the results of menu.children.
{% set menu = app.make('nav-top-left').activeChild %}
{% if not menu %}
{% set menu = app.make('nav-top-right').activeChild %}
{% endif %}
{% if menu and menu.isVisible %}
<nav class="nav nav-pills nav-stacked">
{% for group in menu.children %}
{% if group.isVisible %}
<li class="nav-item nav-header">{{ group.label }}</li>
{% for item in group.children %}
<li class="nav-item">
<a class="nav-link {{ item.isActive ? 'active' : '' }}" href="{{ item.href }}">
{{ item.label }}
</a>
</li>
{% endfor %}
{% endif %}
{% endfor %}
</nav>
{% endif %}
By default, all menu items require authentication. To create menu items for anonymous users:
$menu->add('photos.index')->guests();
Flags
You can add flags to a menu item for inquiring about menu items later.
// during initialization $m->flags(['ajax-only', 'bold']);
// later in the view
{% if menu.is('ajax-only') %}
{# ... do something different for this menu item #}
{% endif %}
Labels
Labels are just taken from the Laravel's translation infrastructure.
By default, the translation key is just the route name provided when adding the menu item, but with the dots converted to dashes
$menu->add('my.route.name'); // key is "my-route-name"
If the top-level menu object has a name, then that will be prefixed to the translation key: i.e. 'top-left' and 'my.route.name' => 'top-left.my-route-name'.
$menu = new Menu('top-left'); $menu->add('my.route.name'); // key is "top-left.my-route-name"
Group names are also prefixed
$menu = new Menu('top-left'); $menu->group('branding', function (Menu $g) { $g->add('my.route.name'); // key is "top-left.branding.my-route-name" });
At any place up the menu tree, you may specify a translation namespace.
$menu = new Menu('top-left'); $menu->langNamespace('app'); $menu->add('my.route.name'); // key is "app::top-left.my-route-name"
You can override the language key to use:
$menu->add('my.route.name', function (Menu $m) { $m->labelKey('my-full-route-name'); });
Or you can override the label directly
$menu->add('my.route.name', function (Menu $m) { $m->label('My Route Here!'); });
elmdash/menu 适用场景与选型建议
elmdash/menu 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 3.04k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2017 年 03 月 19 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「routing」 「menu」 「Authentication」 「navigation」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 elmdash/menu 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 elmdash/menu 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 elmdash/menu 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Automatically logs-in users if they are already authenticated by a remote source. (e.g. environment variable REMOTE_USER)
A Symfony extension to get active class base on current bundle/controller/action
GraphQL authentication for your headless Craft CMS applications.
Write down your routing mapping at one place
Laravel middleware to restrict a site or specific routes using HTTP basic authentication
Flight routing is a simple, fast PHP router that is easy to get integrated with other routers.
统计信息
- 总下载量: 3.04k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 7
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2017-03-19