Detailseiten in der Contao-Navigation anzeigen

In dem FE-Modul der Contao-Navigation werden nur die Seiten als Navigationspunkt ausgegeben, die sich auch im Seitenbaum befinden z. B. /produkte/uebersicht. Möchte man zusätzlich zu den Contao-Seiten auch Detailseiten wie zum Beispiel /produkt/detail/artikelnummer/1364 anzeigen, obwohl es nur die Seite /produkt/detail im Seitenbaum gibt, kann man verschiedene Wege dafür gehen.

Eigene Seiten im Seitenbaum mit Alias für Weiterleitung

Wenn es die Seite /produkt/detail als Contao Seite gibt, bei der über die Slugparameter artikelnummer/1364 der gewünschte Artikel angezeigt wird, kann man eine neue Seite im Seitenbaum an der gewünschten Stelle für die Navigation einbauen z. B. mit dem Titel „Artikel 1364“. Der Alias der Seite wird aber manuell auf den Alias der Detailansicht gesetzt /produkt/detail/artikelnummer/1364. Damit beim Aufruf des Navigationslinks für „Artikel 1364“ auch der gewünschte Inhalt angezeigt wird, muss die Seite /produkt/detail eine höhere Routenpriorität (10) als die Seite artikelnummer/1364 (0) bekommen.

Weitere Tipps zur Routenpriorität.

ParseTemplateListener zum Anpassen der Navigation

Mit dem ParseTemplateListener kann das Template nav_default (oder eigenen Varianten davon) vor der „Auslieferung“ noch manipuliert werden. Damit ist es möglich, an gewünschter Position eigene Navigationslinks (siehe getSublinks()) einzubauen. Folgend ein Beispielcode:

  1<?php
  2// src/EventListener/ParseTemplateListener.php
  3
  4namespace App\EventListener;
  5
  6use Contao\CoreBundle\DependencyInjection\Attribute\AsHook;
  7use Contao\Template;
  8use MetaModels\Filter\Setting\IFilterSettingFactory;
  9use MetaModels\IFactory;
 10use MetaModels\IMetaModel;
 11use MetaModels\Render\Setting\IRenderSettingFactory;
 12use Symfony\Component\HttpFoundation\RequestStack;
 13
 14use function sprintf;
 15use function str_replace;
 16use function trim;
 17
 18#[AsHook('parseTemplate')]
 19class ParseTemplateListener
 20{
 21    public function __construct(
 22        private readonly IFactory $factory,
 23        private readonly IFilterSettingFactory $filterFactory,
 24        private readonly IRenderSettingFactory $renderFactory,
 25        private readonly RequestStack $requestStack,
 26    ) {
 27    }
 28
 29    public function __invoke(Template $template): void
 30    {
 31        if ('nav_default' === $template->getName()) {
 32            $levelData = $template->getData();
 33
 34            // Check only level 1.
 35            if ('level_1' !== $levelData['level']) {
 36                return;
 37            }
 38
 39            $items = $levelData['items'];
 40            foreach ($items as &$item) {
 41                // Check only page id 7.
 42                if (7 !== ($item['id'] ?? null)) {
 43                    continue;
 44                }
 45
 46                // Add subitems as level 2 at page id 7 and mark parent as trail.
 47                if ([] !== ($subLinks = $this->getSublinks())) {
 48                    $item['subitems'] = $subLinks['subitems'];
 49                    $item['class']    =
 50                        trim(
 51                            'submenu ' . ($subLinks['trail'] ? str_replace('sibling', 'trail', $item['class']) : '')
 52                        );
 53                }
 54            }
 55            unset($item);
 56            $levelData['items'] = $items;
 57
 58            $template->setData($levelData);
 59        }
 60    }
 61
 62    private function getSublinks(): array
 63    {
 64        // Begin configuration.
 65        $modelName = 'mm_employees';
 66        $renderId  = 4;
 67        $filterId  = 3;
 68        // End configuration.
 69
 70        if (!(($model = $this->factory->getMetaModel($modelName)) instanceof IMetaModel)) {
 71            return [];
 72        }
 73
 74        $filter           = $model->getEmptyFilter();
 75        $filterCollection = $this->filterFactory->createCollection($filterId);
 76        $filterCollection->addRules($filter, []);
 77        $items = $model->findByFilter($filter);
 78
 79        if (!$items->getCount()) {
 80            return [];
 81        }
 82
 83        $parsed = $items->parseAll('text', $this->renderFactory->createCollection($model, $renderId));
 84        unset($items, $filterCollection, $filter, $model);
 85
 86        $request = $this->requestStack->getCurrentRequest();
 87        if (null === $request) {
 88            return [];
 89        }
 90        $path        = $request->getRequestUri();
 91        $isTrail     = false;
 92        $subLinkList = '<ul class="level_2">';
 93        foreach ($parsed as $item) {
 94            $href = $item['actions']['jumpTo']['href'];
 95            // Possibly clean up the path+href from GET parameters or anchor links.
 96            if ($path !== $href) {
 97                $subLinkList .= sprintf(
 98                    '<li><a href="%1$s" title="%2$s">%2$s</a></li>',
 99                    $href,
100                    $item['text']['name']
101                );
102            } else {
103                $isTrail     = true;
104                $subLinkList .= sprintf(
105                    '<li class="active"><strong class="active" aria-current="page">%s</strong></li>',
106                    $item['text']['name']
107                );
108            }
109        }
110        $subLinkList .= '</ul>';
111
112        return ['trail' => $isTrail, 'subitems' => $subLinkList];
113    }
114}

Mehr zum Registrieren von Services in dem verlinkten Artikel.

Erweiterung „hofff/contao-navigation“ und „TreeEvent“

Die Erweiterung „Contao-Navigation“ stellt ein eigenes FE-Modul für die Navigation zur Verfügung. Zudem hat die Erweiterung verschiedene Events, bei dem die Manipulation der Ausgabe gegenüber dem ParseTemplateListener eleganter durchzuführen ist. Folgend ein Beispielcode zum Anfügen eines zusätzlichen Links in der Navigation - das kann aber auch zur Ausgabe von MM-Detailseiten entsprechend angepasst werden.

 1<?php
 2// src/EventListener/NavigationMenuListener.php
 3
 4namespace App\EventListener;
 5
 6use Hofff\Contao\Navigation\Event\TreeEvent;
 7use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
 8
 9use function array_keys;
10
11#[AsEventListener('Hofff\Contao\Navigation\Event\TreeEvent')]
12class NavigationMenuListener
13{
14    public function __invoke(TreeEvent $treeEvent): void
15    {
16        $moduleId  = $treeEvent->moduleModel()->id; // Module id for checking if it's the correct module.
17        $pageId    = $treeEvent->items()->currentPage->id; // Page id for checking if it's the correct page.
18        $pageItems = $treeEvent->items(); // Get the page items for the navigation tree.
19        $rootIds   = $pageItems->roots;
20
21        // Add a new item to the first root as the last one.
22        $pageItems->subItems[array_keys($rootIds)[0]][] = 9999;
23
24        // Item data.
25        $pageItems->items[9999] = [
26            'class'     => 'mm-page',
27            'isInTrail' => false,
28            'isActive'  => false,
29            'pageTitle' => 'MetaModels',
30            'accesskey' => '',
31            'target'    => 'target="_blank"',
32            'link'      => 'MetaModels',
33            'href'      => 'https://now.metamodel.me',
34        ];
35    }
36}

Mehr zum Registrieren von Services in dem verlinkten Artikel.