Фикс add-to родителя, сдвиг сортировки при переносе, branch-alias, дока Orchid подставляет аргументы query() по именам, поэтому параметр в MenuItemAddToParent::query() всегда получал дефолт 0 и новый пункт сохранялся в корень меню. Имя аргумента приведено к {parent} из маршрута. Menu::resortCurrParent() менял местами sort перемещаемого пункта и пункта с sort = newIndex, из-за чего перенос вниз больше чем на одну позицию давал неверный порядок. Теперь сдвигается диапазон между старой и новой позициями. resort() получил фильтр по type, как у соседних методов. testChangeSort закреплял старое поведение - ожидания переписаны, добавлен testChangeSortNeighbour. readme: в примере маршрутов имена параметров приведены в соответствие с сигнатурами экранов, добавлено предупреждение про сверку имён, описана сортировка перетаскиванием и запуск тестов пакета. В composer.json добавлен branch-alias dev-master -> 14.1.x-dev для установки из path-репозитория.
2 недели назад История
readme.md

Редактор меню сайта для Orchid Laravel

Сделать этот пакет навеяло создание (копирование из предыдущих проектов) в 100500 раз одного и того-же модуля админки - редактор меню сайта.

Установка

Для Orchid версии 14: В composer.json в секцию: require добавить: "mavsan/orchid-site-menu": "^14", или выполнить команду: composer require mavsan/orchid-site-menu:^14.

Для Orchid версии 10: В composer.json в секцию: require добавить: "mavsan/orchid-site-menu": "^10", или выполнить команду: composer require mavsan/orchid-site-menu:^10.

Для Orchid версии 9: В composer.json в секцию: require добавить: "mavsan/orchid-site-menu": "^9", или выполнить команду: composer require mavsan/orchid-site-menu:^9. Инструкция изменена, переключитесь на последнюю метку тега 9, чтобы изучить актуальную инструкцию.

Для Orchid версии 7: выполнить команду composer require mavsan/orchid-site-menu:^7 (инструкция немного отличается, переключайтесь на последний тег, там инструкция по этой версии).

Публикация конфигурации:

php artisan vendor:publish --tag=site-menu-config

Публикация языковых файлов:

php artisan vendor:publish --tag=site-menu-translations

Публикация шаблонов (views)

php artisan vendor:publish --tag=site-menu-views

Публикация ассетов (обязательный шаг после установки)

php artisan site-menu:publish

Команда копирует js/sitemenu.js и css/sitemenu.css в каталог public/vendor/mavsan/orchid-site-menu, чтобы веб-сервер отдавал их напрямую, без PHP-запроса в админку.

Запускать её нужно:

  • один раз сразу после установки пакета;
  • после каждого обновления пакета (новая версия может содержать новые js/css).

Чтобы не забывать про обновления, добавьте команду в composer.json вашего проекта в секцию scripts:

"scripts": {
    "post-autoload-dump": [
        "@php artisan package:discover --ansi",
        "@php artisan site-menu:publish --ansi"
    ]
}

Тогда composer install/composer update будет сам обновлять ассеты. Эквивалент команды: php artisan vendor:publish --tag=site-menu-assets.

Если команду не выполнять, пакет продолжит отдавать ассеты сам через маршрут site-menu-resources/{path} (файлы будут идти через PHP), но в public ничего не появится.

Ассеты подключаются только на экранах редактора меню: пакет кладёт ссылки через @push('stylesheets')/@push('scripts'), которые выводит layout Orchid. Гостю, в том числе на странице входа, они не нужны и не запрашиваются.

Регистрация в меню Orchid (app\Orchid\PlatformProvider.php):

public function registerMainMenu(): array
{
    return [
        ...
        Menu::make(__('sitemenu::main.name'))
                ->icon('bs.list')
                ->route('platform.site-menu.main'),
        ...
    ];
}

Создать таблицу для хранения меню:

php artisan migrate

В поставке есть только локализация ru, поэтому, чтобы не лицезреть в админстративной панели надписи вида sitemenu::main.bottom убедитесь, что в файле конфигурации app.php в параметре locale установлено значение ru.

Маршруты и хлебные крошки

В файл маршрутов Orchid (routes/platform.php) необходимо добавить следующие маршруты (пути можно изменить):

// Platform -> Site Menu
Route::screen('site-menu/{menuType}/item/add-to/{parent}', OrchidSiteMenu\Screens\MenuItemAddToParent::class)
    ->name('platform.site-menu.item.add-to-parent')
    ->breadcrumbs(function (Trail $trail, $menuType, $parent) {
        return $trail
            ->parent('platform.site-menu.list', $menuType)
            ->push(__('Create'));
    });
// Platform -> Site Menu -> Site Menu Type
Route::screen('site-menu/{menuType}/item/{id}', OrchidSiteMenu\Screens\MenuItemScreen::class)
    ->name('platform.site-menu.item')
    ->breadcrumbs(function (Trail $trail, $menuType, $id) {
        return $trail
            ->parent('platform.site-menu.list', $menuType)
            ->push($id !== 'add' ? __('Edit') : __('Create'));
    });
// Platform -> Site Menu -> Site Menu Type -> Site Menu Item
Route::screen('site-menu/{menuType}/list', OrchidSiteMenu\Screens\MenuListScreen::class)
    ->name('platform.site-menu.list')
    ->breadcrumbs(function (Trail $trail, $menuType) {
        $label = __('sitemenu::main.'.$menuType) === 'sitemenu::main.'.$menuType
            ? config('sitemenu.menus.' . $menuType)
            : __('sitemenu::main.'.$menuType);

        return $trail
            ->parent('platform.site-menu.main')
            ->push($label, route('platform.site-menu.list', ['menuType' => $menuType]));
    });
// Platform -> Site Menu -> Site Menu Type -> Site Menu Item add to parent
Route::screen('site-menu', OrchidSiteMenu\Screens\MenuMainScreen::class)
    ->name('platform.site-menu.main')
    ->breadcrumbs(function (Trail $trail) {
        return $trail
            ->parent('platform.index')
            ->push(__('sitemenu::main.name'), route('platform.site-menu.main'));
    });

Имена параметров в маршрутах обязаны совпадать с именами аргументов экранов ({parent} → query($menuType, $parent), {id} → query($menuType, $id)) и с именами аргументов в closure хлебных крошек. Orchid вызывает методы экрана через App::call(), который подставляет значения по именам; при несовпадении берётся значение по умолчанию — без ошибки и без записи в лог. Последствия: в .../item/add-to/{parent} родитель нового пункта молча станет верхним уровнем, а на экране создания в крошках будет «Редактировать».

Если вы переименовали параметр в URI (например, {parent} → {id}), не забудьте поправить ссылки в OrchidSiteMenu\Screens\MenuItemScreen::save(), которые строятся с именованным параметром:

redirect()->route('platform.site-menu.item.add-to-parent', [
    'menuType' => $menuType,
    'parent'   => $menu->id,
]);

Конфигурация

locales - где искать используемые на сайте локали. По-умолчанию config/app.php, ключ locales, должно быть массивом. Если ключа в конфигурации нет, то будет использован app.locale. Это означает, что если по-умолчанию сейчас app.locale = en, вы заполните меню, а потом решите сделать app.locale = ru, то надо заново пересоздать все меню, т.к. в базе сохранится именно как en.

menus - список нужных меню (верхнее, нижнее, слева, справа и т.д.), массив - ключ => выводимое в админке название (или ключ в языковом файле). По ключу этого массива потом будете запрашивать меню у модели.

systemIDs - массив, список ID пунктов меню, которые являются служебными. Например, структура меню такая: “О нас”, “Доставка”, “Пользовательское соглашение”, “Каталог”. Каталог - генерируется автоматически. Чтобы не вышло ситуации, что “Каталог” пропал - надо внести его ID сюда, и тогда его нельзя будет удалить в административной панели.

show - отображаемые элементы в редакторе пункта меню

Использование

При построении меню для фронтэнда необходимо получить все пункты интересующего типа меню верхнего уровня, на выходе будет коллекция из экземпляров модели \OrchidSiteMenu\Models\MenuItem, содержащий дерево всего меню данного типа.

// По-умолчанию не отображаются пункты меню, отмеченные как скрытые для текущего языка
// получить все пункты меню для ТЕКУЩЕГО языка одним списком, т.е. строить дерево надо самому
$menuItems = resolve(OrchidSiteMenu\Models\Menu::class)->getTreeAll('needMenuType', $parent = 0, $level = 0, $showHidden = false);
// аналогично инструкции выше
$menuItems = OrchidSiteMenu\Models\Menu::getAllCurrLng('needMenuType', $parent = 0, $level = 0, $showHidden = false);
// получить все пункты меню для ТЕКУЩЕГО языка в виде дерево родитель-потомок-...-потомок
$tree = resolve(OrchidSiteMenu\Models\Menu::class)->getTreeAll('needMenuType', $parent = 0, $showHidden = false);
// аналогично инструкции выше
$tree = OrchidSiteMenu\Models\Menu::getTreeAllCurrLng('needMenuType', $parent = 0, $showHidden = false);

, где: needMenuType - одно из значений, указанных в файле конфигурации в menus (т.е. какое меню вам надо - верхнее, нижнее и т.д.)

При необходимости получить дерево для конкретного пункта меню, необходимо вызвать метод getTreeChildren($level = 0, $showHidden = false), где level - номер следующего за текущим уровень меню (просто номер, если он нужен зачем-то), $showHidden - нужны ли скрытые пункты меню. Например, верхний уровень - 0, следующий - 1 и т.д. Именно так эти уровни формируются при вызове метода getTreeAll('needMenuType'). На выходе будет массив, содержащий информацию о потомках пункта меню, для которого был вызван метод getTreeChildren. При этом - информация о текущем пункте меню не будет включена в результат работы метода getTreeChildren.

Если есть необходимость кеширования меню, то надо создать слушателя событий (см. доку о Laravel) и подписаться на события модели OrchidSiteMenu\Models\Menu. При формировании меню надо кешировать данные с каким-то ключом, например: topmenu-ru или topmenu-en. В слушателе событий, при возникновении событий saved и deleted - удалять кеш с этими ключами (если сайт мультиязычный, то удалять надо для всех языков сразу).

Сортировка пунктов перетаскиванием

Порядок пунктов хранится в поле sort и пересобирается в плотную последовательность 0…n-1 отдельно для каждой пары «родитель + тип меню». Перенос пункта выполняет Menu::updateSort($parentId, $newIndex, $oldIndex):

  • пункт переехал в пределах родителя — сдвигается диапазон между старой и новой позициями, поэтому перенос на любое расстояние вверх и вниз даёт ожидаемый порядок ([A, B, C] + перенос A на позицию 2 = [B, C, A]);
  • пункт переехал к другому родителю — у нового родителя вставляется на newIndex (остальные сдвигаются вниз), у старого порядок пересобирается.

Индексы приходят из resources/js/sortable.js и соответствуют позициям в ul.sm-items на экране списка.

Тесты пакета

Тесты лежат в tests/Unit и запускаются PHPUnit:

vendor/bin/phpunit -c packages/orchid-site-menu/phpunit.xml

Они рассчитаны на запуск внутри проекта с Orchid: bootstrap указывает на vendor/autoload.php проекта, а tests/Unit/MenuTest.php наследует Tests\TestCase. Отдельного Testbench у пакета нет, поэтому экраны и маршруты пакета (они регистрируются в проекте, см. раздел выше) тестируются в сюите проекта.

Описание
Конвейеры
0 успешных
0 с ошибкой
Разработчики