Уменьшаем боль в навигации приложения на Yii2

в 5:01, , рубрики: menu, navigation, open source, php, yii, yii2, печенька
Уменьшаем боль в навигации приложения на Yii2 - 1

Доброго времени суток! Большую часть проектов мы пишем на Yii2, потому что он клёвый и мы его любим.
Однако, всегда есть что улучшить (благо этого не препятствует архитектура Yii). Хочу поделиться решением, которое упрощает прописывание навигации в приложениях на Yii2.

Проблема

Когда мы добавляем в приложение страницу, нам нужно прописать для неё следующие вещи (после создания контроллера и вьюшки):

  • Заголовок страницы ($this->title = ...);
  • Хлебные крошки ($this->params['breadcrumbs'][] = ...);
  • Права для действия в контроллере (yiibaseActionFilter в behaviors контроллера);
  • Параметр visible с проверкой доступа во всех меню, где есть ссылка на эту страницу;
  • Добавить правило в yiiwebUrlManager::rules для красивой ссылки;
  • Добавить страницу в sitemap.xml.

Уменьшаем боль в навигации приложения на Yii2 - 2

Не жирновато ли для "ещё одной страницы"? Самое плохое в этом то, что все эти пункты нужно держать в голове и не забывать. А если навигация в проекте начинает меняться, то что-то сломать становится еще проще, чаще всего забываешь про хлебные крошки и они становятся просто не рабочими.

Решение

Мы предположили, что любая страница приложения должна входить в общую карту сайта. А значит, если создать такую карту сайта (в виде многоуровнего дерева) с исчерпывающей информацией о странице (см. пункты из раздела "Проблема"), то добавление страницы сведётся к описанию её в карте сайте, всего лишь в одном месте! Мы можем прописать там и заголовки, и права и правила ссылки, а имея карту сайта легко получить хлебные крошки и sitemap.xml.

Таким образом получился компонент [MegaMenu](), который и представляю хабрасообществу.

Установка

Устанавливается компонент через Composer:

$ composer require ExtPoint/yii2-megamenu

Далее нам нужно добавить компонент в конфигурацию приложения:

Как компонент приложения:

'components' => [
    'megaMenu'=> [
        'class' => 'extpointmegamenuMegaMenu',
        'items' => [
            // You sitemap
            [
                'label' => 'Главная',
                'url' => ['/site/index'],
                'urlRule' => '/',
            ],
            ...
        ],
    ],
    ...
],

И подгружать его до запуска приложения (для добавления правил в UrlManager):

...
'bootstrap' => ['log', 'megamenu'],
...

API

АПИ компонента создавалось максимально приближенным к Yii2, часто повторяя его 1 в 1.

Формат описания страницы (параметр extpointmegamenuMegaMenu::items)

Каждый item в большинстве соответствует формату задания навигации для yiibootstrapNav::items, где каждый item имеет атрибуты label, url, visible, active, encode, items, options, linkOptions. Каждый item задается в виде массива, из которого затем создается экземпляр класса extpointmegamenuMegaMenuItem.
Ниже перечислим нововведенные параметры, которых нет в yiibootstrapNav::items:

  • urlRule (строка, массив или экземпляр yiirestUrlRule). Формат соответствует правилу из yiiwebUrlManager::rules;
  • roles (строка или массив строк). Формат идентичен yiifiltersAccessRule::roles. Поддерживаются значения "?", "@" и указание роли в виде строки.
  • order (число) Каждый уровень меню сортируется согласно этому параметру. Значение по-умолчанию — 0.

Методы компонента extpointmegamenuMegaMenu

  • setItems(array $items) Добавляет элементы меню в конец списка;
  • addItems() Добавляет элементы меню;
  • getItems() Возвращает элементы меню;
  • getActiveItem() Возвращает текущий роут, аналогично Yii::$app->requestedRoute, но с распарсеными параметрами;
  • getMenu(array $item, $custom) Находит вложенный элемент меню (null = корень) и возвращает вложенное меню с дочерними элементами. В параметре custom можно переопределять конфигурацию меню, если задать его как массив. Если задать числом — то это укажет на возвращаемую вложенность меню. Например, Yii::$app->megaMenu->getMenu(null, 2) вернет двухуровневое меню, даже если само меню имеет большее число вложенности.
  • getTitle($url = null) Находит item для указанного url (по-умолчанию — текущая страница) и возвращает его заголовок
  • getFullTitle($url = null, $separator = ' — ') Аналогично предыдущему, но так же добавляет все родительские названия item'ов
  • getBreadcrumbs($url = null) Возвращает хлебные крошки для виджета yiiwidgetsBreadcrumbs::links
  • getItem($item, &$parents = []) Находит item по url/роуту, в parents добавляет item'ы всех родителей для найденного item'а
  • getItemUrl($item) Находит item и возвращает его url

Логика поиска item'а

Логика сравнения двух item реализована в методе extpointmegamenuMegaMenu::isUrlEquals. Сравнение ссылок ведется путем сравнения двух строк.
Роуты сравниваются немного сложнее: сперва они нормализуются (получение полного роута, с указанием модуля, контроллера и экшена), затем сравниваются только роуты. Если роуты совпали, то сравниваются параметры.
Если параметр отличается от null, то сравнивается как его ключ, так и значение. Если значение указано как null, это означает, что может быть любое значение, сравнивается только наличие ключей.
Примеры:

  • 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

Пример

Пример маленького веб-приложения с установленным MegaMenu можно найти в папке тестов:

Да ладно, это в реальных проектах не будет работать!

Уменьшаем боль в навигации приложения на Yii2 - 3

Однако, будет. MegaMenu уже успешно используется в нескольких крупных проектах. В наших проектах мы всегда разбиваем функционал на модули и MegaMenu этому не сопротивляется.
Пример такой разбивки и более реальный пример можно увидеть в нашем бойлерплейте. Меню по кусочкам собирается из модулей или контроллеров.

TODO

Компонент ещё развивается, вот некоторые фичи, которые стоит ждать в ближайшем будущем:

  • Проверка доступа для контроллера (behaviors, анализирующий карту сайта для проверки доступа);
  • Получение карты сайта для sitemap.xml;
  • UI для кастомизации карты сайта с сохранением изменений в БД.

End

Спасибо всем, кто дочитал/пролистал до конца. Любые предложения и пожелания пишите на affka@affka.ru

Ставьте звезды на гитхабе — ExtPoint/yii2-megamenu

Всем удачного дня!

Автор: affka

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js