Reduce the pain of navigating apps on Yii2
Good day! We write most of the projects in Yii2 because it is cool and we love it.
However, there is always something to improve (the benefit of this is not prevented by the Yii architecture). I want to share a solution that simplifies prescribing navigation in applications on Yii2.
When we add a page to the application, we need to prescribe the following things for it (after creating the controller and view):
- Page Title (
$this->title = ...);
- Bread crumbs (
$this->params['breadcrumbs'] = ...);
- Rights for action in the controller (
visiblewith access control in all menus where there is a link to this page;
- Add a rule in
\yii\web\UrlManager::rulesfor a nice reference;
- Add page to sitemap.xml.
Isn't it bold for "one more page"? The worst part is that all these points must be kept in mind and not forgotten. And if the navigation in the project starts to change, then it becomes even easier to break something, most often you forget about bread crumbs and they just become not working.
We assumed that any page of the application should be included in the general site map. So, if you create such a site map (in the form of a multi-level tree) with comprehensive information about the page (see paragraphs from the "Problem" section), then adding a page will be reduced to describing it in the site map, in only one place! We can register there headings, and the rights and rules of the link, and having a site map it is easy to get bread crumbs and sitemap.xml.
Thus, the component [MegaMenu] () was obtained, which I represent to the habrasociety.
The component is installed via Composer:
$ composer require ExtPoint/yii2-megamenu
Next, we need to add the component to the application configuration:
As an application component:
'components' => [ 'megaMenu'=> [ 'class' => '\extpoint\megamenu\MegaMenu', 'items' => [ // You sitemap [ 'label' => 'Главная', 'url' => ['/site/index'], 'urlRule' => '/', ], ... ], ], ... ],
And load it before the application starts (to add rules to
... 'bootstrap' => ['log', 'megamenu'], ...
The API component was created as close as possible to Yii2, often repeating it 1 in 1.
Page description format (parameter
Each item in most corresponds to the format for the navigation task
\yii\bootstrap\Nav::items, where each item has attributes
linkOptions. Each item is defined as an array, from which an instance of the class is then created
Below we list the newly introduced parameters that are not in
urlRule(string, array, or instance
\yii\rest\UrlRule). The format follows the rule from
roles(string or array of strings). The format is identical
\yii\filters\AccessRule::roles. Values are supported
"@"and the role is indicated as a string.
order(number) Each menu level is sorted according to this parameter. The default value is 0.
setItems(array $items)Adds menu items to the end of the list;
addItems()Adds menu items;
getItems()Returns menu items;
getActiveItem()Returns the current route, similarly
\Yii::$app->requestedRoute, but with parsed parameters;
getMenu(array $item, $custom)Finds a nested menu item (
null= root) and returns a nested menu with children. In the custom parameter, you can override the menu configuration if you set it as an array. If set by a number, then this will indicate the returned menu nesting. For example, it
\Yii::$app->megaMenu->getMenu(null, 2)will return a two-level menu, even if the menu itself has a greater number of nesting.
getTitle($url = null)Finds the item for the specified
url(default is the current page) and returns its title
getFullTitle($url = null, $separator = ' — ')Similar to the previous one, but also adds all parent item names
getBreadcrumbs($url = null)Returns breadcrumbs for a widget
getItem($item, &$parents = )Finds item by url / route, in parents adds items of all parents for the found item
getItemUrl($item)Finds item and returns its url
Item search logic
The logic of comparing two item is implemented in the method
\extpoint\megamenu\MegaMenu::isUrlEquals. Compare links by comparing two strings.
Routes are compared a little more complicated: first they are normalized (getting a full route, indicating the module, controller and action), then only the routes are compared. If the routes match, then the parameters are compared.
If the parameter is different from null, then both its key and value are compared. If the value is specified as null, this means that there can be any value, only the presence of keys is compared.
- isUrlEquals (' http://ya.ru ', ' http://ya.ru ') // true
- isUrlEquals (['qq / ww / ee'], ['aa / bb / cc']) // false
- isUrlEquals (['aa / bb / cc', 'foo' => null], ['aa / bb / cc']) // false
- isUrlEquals (['aa / bb / cc', 'foo' => null], ['aa / bb / cc', 'foo' => null]) // true
- isUrlEquals (['aa / bb / cc', 'foo' => 'qwe'], ['aa / bb / cc', 'foo' => null]) // true
- isUrlEquals (['aa / bb / cc', 'foo' => 'qwe'], ['aa / bb / cc', 'foo' => '555']) // false
An example of a small web application with MegaMenu installed can be found in the test folder:
C'mon, this will not work in real projects!
However, there will be. MegaMenu is already successfully used in several large projects. In our projects, we always break the functionality into modules and MegaMenu does not resist this.
An example of such a breakdown and a more realistic example can be seen in our boilerplate . A menu of pieces is assembled from modules or controllers .
The component is still developing, here are some features that should be expected in the near future:
- Access control for the controller (behaviors analyzing the site map for access control);
- Getting a sitemap for sitemap.xml;
- UI for customizing a site map while saving changes to the database.
Thanks to everyone who read / leafed to the end. Any suggestions and wishes write to email@example.com
Put stars on github - ExtPoint / yii2-megamenu
Have a nice day everyone!