Зменшуємо біль у навігації програми на Yii2


Доброго часу доби! Більшу частину проектів ми пишемо на Yii2, тому що він кльовий і ми його любимо.
Однак, завжди є що поліпшити (благо цього не перешкоджає архітектура Yii). Хочу поділитися рішенням, яке спрощує прописування навігації в додатках на Yii2.
Проблема
Коли ми додаємо в додаток сторінку, нам потрібно прописати для неї наступні речі (після створення контролера і в'юшки):
  • Заголовок сторінки (
    $this->title = ...
    );
  • Хлібні крихти (
    $this->params['breadcrumbs'][] = ...
    );
  • Права для дії в контролері (
    \yii\base\ActionFilter
    на
    behaviors
    контролера);
  • Параметр
    visible
    з перевіркою доступу у всіх меню, де є посилання на цю сторінку;
  • Додати правило
    \yii\web\UrlManager::rules
    для красивої посилання;
  • Додати сторінку в sitemap.xml.

Не жирновато для "ще однієї сторінки"? Найгірше в цьому те, що всі ці пункти потрібно тримати в голові і не забувати. А якщо навігація в проекті починає змінюватися, щось зламати стає ще простіше, найчастіше забуваєш про хлібні крихти і вони стають просто не робітниками.
Рішення
Ми припустили, що будь-яка сторінка програми повинна входити у загальну карту сайту. А значить, якщо створити таку карту сайту (у вигляді багаторівневого дерева) з вичерпною інформацією про сторінку (див. пункти з розділу "Проблема"), то додавання сторінки зведеться до опису її в карті сайті, всього лише в одному місці! Ми можемо прописати там і заголовки, і права та правила посилання, а маючи карту сайту легко отримати хлібні крихти і sitemap.xml.
Таким чином вийшов компонент [MegaMenu](), який і уявляю хабрасообществу.
Установка
Встановлюється компонент через Composer:
$ composer require ExtPoint/yii2-megamenu

Далі нам потрібно додати компонент у конфігурацію програми:
Як компонент програми:
'components' => [
'megaMenu'=> [
'class' => '\extpoint\megamenu\MegaMenu',
'items' => [
// You sitemap
[
'label' => 'Головна',
'url' => ['/site/index'],
'urlRule' => '/',
],
...
],
],
...
],

І довантажувати його до запуску програми (для додавання правил
UrlManager
):
...
'bootstrap' => ['log', 'megamenu'],
...

API
АПІ компонента створювалося максимально наближеним до Yii2, часто повторюючи його 1 в 1.
Формат опису сторінки (параметр
\extpoint\megamenu\MegaMenu::items
)
Кожен item у більшості відповідає формату завдання навігації для
\yii\bootstrap\Nav::items
, де кожен item має атрибути
label
,
url
,
visible
,
активний
,
encode
,
items
,
options
,
linkOptions
. Кожен item задається у вигляді масиву, з якого потім створюється екземпляр класу
\extpoint\megamenu\MegaMenuItem
.
Нижче перерахуємо нові параметри, яких немає в
\yii\bootstrap\Nav::items
:
  • urlRule
    (рядок, масив або примірник
    \yii\rest\UrlRule
    ). Формат відповідає правилу
    \yii\web\UrlManager::rules
    ;
  • roles
    (рядок або масив рядків). Формат ідентичний
    \yii\filters\AccessRule::roles
    . Підтримуються значення
    "?"
    ,
    "@"
    і вказівку ролі в вигляді рядка.
  • order
    (число) Кожен рівень меню сортується відповідно до цього параметру. Значення за замовчуванням — 0.
Методи компонента
\extpoint\megamenu\MegaMenu

  • 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)
    Повертає хлібні крихти для віджета
    \yii\widgets\Breadcrumbs::links
  • getItem($item, &$parents = [])
    Знаходить item url/роуту, в parents додає item'и всіх батьків для знайденого item'а
  • getItemUrl($item)
    Знаходить item та повертає його url
Логіка пошуку item'а
Логіка порівняння двох item реалізована в методі
\extpoint\megamenu\MegaMenu::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 можна знайти в папці тестів:
Да ладно, це в реальних проектах не буде працювати!

Однак, буде. MegaMenu вже успішно використовується в декількох великих проектах. В наших проектах ми завжди розбиваємо функціонал на модулі і MegaMenu цього не пручається.
Приклад такої розбивки й більш реальний приклад можна побачити в нашому бойлерплейте. Меню по шматочках збирається з модулей або контроллеров.
TODO
Компонент ще розвивається, ось деякі фічі, які варто чекати в найближчому майбутньому:
  • Перевірка доступу для контролера (behaviors, аналізує карту сайту для перевірки доступу);
  • Отримання карти сайту для sitemap.xml;
  • UI для кастомізації карти сайту із збереженням змін в БД.
End
Спасибі всім, хто дочитав/перегорнув до кінця :)
Будь-які пропозиції і побажання пишіть на affka@affka.ru
Ставте зірки на гітхабі — ExtPoint/yii2-megamenu
Всім вдалого дня!=)
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.