Навигация
Основы
Pimcore предоставляет стандартную реализацию навигации в виде расширения Twig. Эта реализация создает контейнер навигации на основе существующей структуры документов. Процесс рендеринга делится на два шага:
- Собрать навигацию:
{% set nav = pimcore_build_nav({active: activeDocument, root: navigationRootDocument}) %} - Отобразить навигацию:
pimcore_render_nav(nav)orpimcore_nav_renderer('menu').render(nav)
Этап сборки навигации не обязательно должен выполняться в скрипте представления. На самом деле помощник представления просто перенаправляет вызов
build()в сервисPimcore\Navigation\Builder. Вы также можете создать навигацию в своем контроллере или сервисе, а затем передать объект навигации в представление.
В эту структуру включаются только документы, папки игнорируются, независимо от их свойств навигации.
{# получить корневой элемент, если документ не определен (для страниц, которые маршрутизируются напрямую через статический маршрут) #}
{% if not document is defined or not document %}
{% set document = pimcore_document(1) %}
{% endif %}
{# получить документ, который должен использоваться для начала навигации | по умолчанию главная страница #}
{% set navStartNode = document.getProperty('navigationRoot') %}
{% if not navStartNode is instanceof('\\Pimcore\\Model\\Document\\Page') %}
{% set navStartNode = pimcore_document(1) %}
{% endif %}
{% set mainNavigation = pimcore_build_nav({
active: document,
root: navStartNode
}) %}
{# позже вы можете отобразить навигацию #}
{{ pimcore_render_nav(mainNavigation) }}
Настроив контейнер навигации, как показано выше, вы можете легко использовать его для отображения дерева навигации, меню или хлебных крошек.
Мета навигация - только 1 уровень
{{ pimcore_nav_renderer('menu').renderMenu(mainNavigation,{
maxDepth: 1,
ulClass: 'nav navbar-nav'
}) | raw }}
{# или вы можете использовать функцию рендеринга для определения рендерера и метода рендеринга #}
<div class="my-menu">
{# метод menu() недоступен в twig #}
{{ pimcore_render_nav(mainNavigation, 'menu', 'renderMenu', {
maxDepth: 1,
ulClass: 'nav navbar-nav'
}) }}
</div>
Мета навигация - многоуровневая
<div class="my-menu">
{# вы можете использовать массив для ulClass, чтобы задать классы для разных уровней глубины #}
{{ pimcore_render_nav(mainNavigation, 'menu', 'renderMenu', {
maxDepth: 2,
ulClass: {
0: 'nav navbar-nav',
1: 'nav navbar-nav-second',
2: 'nav navbar-nav-third'
}
}) }}
{# или вы можете использовать ключ 'default' для применения класса на всех уровнях глубины #}
{{ pimcore_render_nav(mainNavigation, 'menu', 'renderMenu', {
maxDepth: 2,
ulClass: {
default: 'nav navbar-nav'
}
}) }}
</div>
Хлебные крошки
<div class="my-breadcrumbs">
<a href="/">{{ 'Home'|trans }}</a> >
{{ pimcore_render_nav(mainNavigation, 'breadcrumbs') }}
</div>
Навигация в боковой панели
<div class="my-sidebar-menu">
{{ pimcore_nav_renderer('menu').renderMenu(mainNavigation) }}
</div>
Навигация в боковой панели с другим HTML префиксом
<div class="my-sidebar-menu">
{% set sideNav = pimcore_build_nav({active: document, root: navStartNode, htmlMenuPrefix: 'my-nav-'}) %}
{{ pimcore_render_nav(sideNav, 'menu', 'renderMenu', {
ulClass: 'nav my-sidenav',
expandSiblingNodesOfActiveBranch: true
}) }}
</div>
Метод renderMenu() отображает меню до самого глубокого доступного уровня.
Деревья уровней, которые не находятся внутри активного дерева, и уровни под последней активной страницей должны скрываться с помощью CSS.
Пример CSS ниже показывает, как это сделать (включает 3 уровня):
#navigation ul li ul {
display:none;
}
#navigation ul li.active ul {
display:block;
}
#navigation ul li.active ul li ul {
display:none;
}
#navigation ul li.active ul li.active ul {
display:block;
}
#navigation ul li.active ul li.active ul li ul {
display:none;
}
#navigation ul li.active ul li.active ul li.active ul{
display:block;
}
Настройка свойств навигации документа

Страницы и ссылки имеют настройки навигации в своих системных свойствах, как показано на экране выше. Эти настройки навигации включают следующие свойства:
- Name: Имя документа, используемое в навигации (ярлык).
- Title: Заголовок документа, используемый в навигации - HTML атрибут заголовка.
- Target: Цель ссылки (
_blank,_self,_top,_parent) - Exclude from Navigation: Свойство для быстрого исключения страницы из навигации.
В вашем шаблоне представления вы можете использовать:
{% do document.getProperty('navigation_exclude') %}
- Class: HTML класс элемента навигации
- Anchor: Якорь, добавляемый к URL документа
- Parameters: Параметры, добавляемые к URL документа
- Relation: Доступно только в пользовательском скрипте навигации. Предполагаемое значение HTML атрибута rel для открытия ссылки в виде Lightbox / Clearbox
- Accesskey: Доступно только в пользовательском скрипте навигации
- Tab-Index: Доступно только в пользовательском скрипте навигации
Индивидуальный (частичный) скрипт навигации
Если стандартный HTML вывод метода render() не подходит для проекта, существует возможность предоставить пользовательский скрипт для HTML меню.
Например, внутри вашего представления:
{# \Pimcore\Navigation\Renderer\Menu #}
{% set menuRenderer = pimcore_nav_renderer('menu') %}
{# используйте метод renderPartial, чтобы использовать частичный скрипт один раз #}
{{ menuRenderer.renderPartial(mainNavigation, 'includes/navigation.html.twig') | raw }}
{# или установите частичный скрипт на рендерере (будет действителен для всех последующих вызовов рендеринга) #}
{% do menuRenderer.setPartial('includes/navigation.html.twig') %}
{{ menuRenderer.render(mainNavigation) | raw }}
templates/includes/navigation.html.twig
{% for page in pages %}
<div class="my-menu-element">
{{ pimcore_nav_renderer('menu').htmlify(page) | raw }}
</div>
{% endfor %}
Использование хелпера навигации с сайтами (Sites).
Например:
{% set navStartNode = document.getProperty('navigation_root') %}
{% if not navStartNode is instanceof('\\Pimcore\\Model\\Document\\Page') %}
{% if pimcore_site_is_request() %}
{% set site = pimcore_site_current() %}
{% set navStartNode = site.getRootDocument() %}
{% else %}
{% set navStartNode = pimcore_document(1) %}
{% endif %}
{% endif %}
{% set navigation = pimcore_build_nav({active: document, root: navStartNode}) %}
{{ pimcore_render_nav(navigation, 'menu', 'renderMenu', {
maxDepth: 1,
ulClass: {
'default': 'nav navbar-nav'
}
}) }}
Использование частичных скриптов для генерации кастомизированной навигации
Например, создание навигации в стиле bootstrap 4.0:
{% set navStartNode = document.getProperty('navigation_root') %}
{% if not navStartNode is instanceof('\\Pimcore\\Model\\Document\\Page') %}
{% if pimcore_site_is_request() %}
{% set site = pimcore_site_current() %}
{% set navStartNode = site.getRootDocument() %}
{% else %}
{% set navStartNode = pimcore_document(1) %}
{% endif %}
{% endif %}
{% set mainNavigation = pimcore_build_nav({active: document, root: navStartNode}) %}
{% set menuRenderer = pimcore_nav_renderer('menu') %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
{% for page in mainNavigation %}
{# here need to manually check for ACL conditions #}
{% if page.isVisible() and menuRenderer.accept(page) %}
{% set hasChildren = page.hasPages() %}
{% if not hasChildren %}
<li class="nav-item">
<a class="nav-link" href="{{ page.getHref() }}">{{ page.getLabel() }}</a>
</li>
{% else %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="{{ page.getHref() }}" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ page.getLabel() }}</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{% for child in page.getPages() %}
{% if child.isVisible() and menuRenderer.accept(child) %}
<a class="dropdown-item" href="{{ child.getHref() }}">{{ child.getLabel() }}</a>
{% endif %}
{% endfor %}
</div>
</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</div>
</nav>
Добавление пользовательских элементов в навигацию
В следующем примере мы добавляем
- новые элементы (объекты) в существующую навигацию с атрибутом "pageCallback"
- элементы категории (объекты) к корневой навигации с атрибутом "rootCallback" с использованием расширения Twig.
<?php
namespace App\Twig\Extension;
use App\Website\LinkGenerator\NewsLinkGenerator;
use App\Website\LinkGenerator\CategoryLinkGenerator;
use Pimcore\Model\Document;
use Pimcore\Navigation\Container;
use Pimcore\Twig\Extension\Templating\Navigation;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class NavigationExtension extends AbstractExtension
{
protected Navigation $navigationHelper;
protected NewsLinkGenerator $newsLinkGenerator;
protected CategoryLinkGenerator $categoryLinkGenerator;
public function __construct(Navigation $navigationHelper, NewsLinkGenerator $newsLinkGenerator, CategoryLinkGenerator $categoryLinkGenerator)
{
$this->navigationHelper = $navigationHelper;
$this->newsLinkGenerator = $newsLinkGenerator;
$this->categoryLinkGenerator = $categoryLinkGenerator;
}
/**
* @return TwigFunction[]
*/
public function getFunctions(): array
{
return [
new TwigFunction('app_navigation_links', [$this, 'getNavigationLinks'])
];
}
/**
* @throws \Exception
*/
public function getNavigationLinks(Document $document, Document $startNode): Container
{
$navigation = $this->navigationHelper->build([
'active' => $document,
'root' => $startNode,
'pageCallback' => function($page, $document) {
/** @var \Pimcore\Model\Document $document */
/** @var \Pimcore\Navigation\Page\Document $page */
if($document->getProperty("templateType") == "news") {
$list = new \Pimcore\Model\DataObject\News\Listing;
$list->load();
foreach($list as $news) {
$detailLink = $this->newsLinkGenerator->generate($news, ['document' => $document]);
$uri = new \Pimcore\Navigation\Page\Document([
"label" => $news->getTitle(),
"id" => "object-" . $news->getId(),
"uri" => $detailLink,
]);
$page->addPage($uri);
}
}
},
'rootCallback' => function(Container $navigation) {
$list = new \Pimcore\Model\DataObject\Category\Listing;
$list->load();
foreach($list as $category) {
$detailLink = $this->categoryLinkGenerator->generate($category);
$categoryDocument = new \Pimcore\Navigation\Page\Document([
"label" => $category->getName(),
"id" => "object-" . $category->getId(),
"uri" => $detailLink,
]);
$navigation->addPage($categoryDocument);
}
}
]);
return $navigation;
}
}
{% set navigation = app_navigation_news_links(document, navStartNode) %}
<div class="my-navigation">
{{ pimcore_render_nav(navigation, 'menu', 'renderMenu', {
expandSiblingNodesOfActiveBranch: true,
ulClass: {
default: 'nav my-sidenav'
}
}) }}
</div>
Кэширование / Высокопроизводительная навигация
Дерево навигации / контейнер (\Pimcore\Navigation\Container) автоматически кэшируется pimcore и значительно улучшает производительность навигации.
Чтобы воспользоваться кэшем, абсолютно необходимо не использовать объекты Pimcore\Model\Document непосредственно в шаблонах навигации / частичных скриптах, так как это приведет к повторной загрузке всех документов в навигации и обойдет механизм кэширования контейнера навигации.
Но иногда необходимо получить некоторые свойства или другие данные из документов в навигации, чтобы построить навигацию так, как должно быть. Для этого мы ввели новый параметр для расширения навигации, который действует как обратный вызов и позволяет сопоставить пользовательские данные с элементом навигации.
<?php
namespace App\Twig\Extension;
use Pimcore\Model\Document;
use Pimcore\Navigation\Container;
use Pimcore\Twig\Extension\Templating\Navigation;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class NavigationExtension extends AbstractExtension
{
protected Navigation $navigationHelper;
public function __construct(Navigation $navigationHelper)
{
$this->navigationHelper = $navigationHelper;
}
/**
* @return TwigFunction[]
*/
public function getFunctions(): array
{
return [
new TwigFunction('app_navigation_custom', [$this, 'getCustomNavigation'])
];
}
/**
* @throws \Exception
*/
public function getCustomNavigation(Document $document, Document $startNode): Container
{
$navigation = $this->navigationHelper->build([
'active' => $document,
'root' => $startNode,
'pageCallback' => function ($page, $document) {
$page->setCustomSetting("myCustomProperty", $document->getProperty("myCustomProperty"));
$page->setCustomSetting("subListClass", $document->getProperty("subListClass"));
$page->setCustomSetting("title", $document->getTitle());
$page->setCustomSetting("headline", $document->getEditable("headline")->getData());
}]
);
return $navigation;
}
}
{% set mainNavigation = app_navigation_custom(document, navStartNode) %}
{% set menuRenderer = pimcore_nav_renderer('menu') %}
{% do menuRenderer.setPartial("navigation/partials/navigation.html.twig") %}
{{ menuRenderer.render(mainNavigation) | raw }}
Позже в шаблоне навигации (navigation/partials/navigation.html.twig) вы можете использовать сопоставленные данные непосредственно на объекте элемента страницы.
{% for page in pages %}
{% if page.isVisible() %}
{% set activeClass = page.getActive(true) ? 'active' : '' %}
<li class="{{ activeClass }}">
<a href="{{ page.getUri() }}" target="{{ page.getTarget() }}">{{ page.getLabel() }}</a>
<ul class="{{ page.getCustomSetting("subListClass") }}" role="menu">
{% include 'navigation/partials/partials/main.html.twig' with {pages: page.getPages()} %}
</ul>
</li>
{% endif %}
{% endfor %}
Использование этого метода значительно улучшит производительность вашей навигации.
Динамический ключ для кэша навигации
Иногда необходимо вручную установить ключ для кэша навигации.
{% pimcore_build_nav({active: document, root: navStartNode, cache: 'yourindividualkey'}) %}
Отключение кэша навигации
Вы можете отключить кэш навигации, установив аргумент cache в false.
{% pimcore_build_nav({active: document, root: navStartNode, cache: false}) %}
Часто задаваемые вопросы
Документ не отображается в навигации. Почему?
Пожалуйста, убедитесь, что документы и их родительские документы опубликованы и что сам документ, а также все его родители имеют установленное имя навигации.
Ни сам документ, ни один из его родительских документов не должны иметь активированное свойство Exclude From Navigation. (Document properties -> System properties)
Почему навигация не отображается?
Смотрите предыдущий вопрос. Если ни один из документов не имеет установленного заголовка навигации, функция рендеринга просто не вернёт ничего.
Почему главная страница не отображается в навигации?
Главная страница по умолчанию не будет отображаться в навигации. Вы можете вручную добавить главную страницу (и любую другую страницу):
{% do navigation.addPage({
order: -1,
uri: '/',
label: 'Home'|trans,
title: 'Home'|trans,
}) %}
Если вы получите документ главной страницы (который всегда имеет ID 1), вы также сможете получить его свойства навигации, чтобы ими можно было управлять через интерфейс администратора Pimcore так же, как и всеми другими документами.
{% set home = pimcore_document(1) %}
{#
order: put it in front of all the others
uri: path to homepage
label: visible label
title: tooltip text
active: active state (boolean)
#}
{% do navigation.addPage({
order: -1,
uri: '/',
label: home.getProperty('navigation_name'),
title: home.getProperty('navigation_title'),
active: document.id == home.id
}) %}
Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.