Loading...
Navigation überspringen
Bitte beachte, dass sich diese Dokumentation auf die neuste Version dieser Erweiterung bezieht. Wenn eine ältere Version eingesetzt wird, kann diese abweichen. Die jeweils passende Dokumentation befindet sich im Dokumentation-Verzeichnis der Erweiterung.

Wie baue ich ein Glossar mit alphabetischer Gruppierung?

Du möchtest eine A–Z-Glossar-Navigation über Kategorien erstellen, dabei sprechende URLs nach /buchstabe/begriff und eine korrekte sitemap.xml erzeugen, obwohl Dein Glossar die Kategorie per N:M (MM-Tabelle) speichert? Dann bist Du hier genau richtig!

Als Ergebnis möchten wir ein Glossar wie auf unserer Website erhalten:

Die URLs in der Sitemap.xml sollen wie folgt aussehen:

Voraussetzungen

  • TYPO3 in Version 11–13
  • EXT:seo muss für die sprechenden URLs installiert sein
  • EXT:glossaries für Extbase-Plugin der Liste + Details

Achtung:

Wenn ein Glossar-Eintrag mehrere Kategorien hat, ist /buchstabe/begriff mehrdeutig. Für A–Z solltest du genau 1 Kategorie je Eintrag erzwingen (TCA/Editor-UX), sonst wird Sitemap/Linkbuilding inkonsistent.

Alphabet-Reiter: Einträge sauber Buchstaben zuordnen

Vorgehen

  • Lege 26 Kategorien AZ an (Titel + slug = az)
  • Jeder Glossar-Eintrag bekommt genau eine dieser Kategorien (sein Anfangsbuchstabe)

TCA: „Genau eine Kategorie“ erzwingen (empfohlen)

Setze in deinem Site-Package in der Datei Configuration/TCA/Overrides/tx_glossaries_domain_model_glossary.php

// Reduce assigned categories to exactly one!
$GLOBALS['TCA']['tx_glossaries_domain_model_glossary']['columns']['categories']['config']['maxitems'] = 1;

Achtung:

Nach der Anpassung nicht vergessen den System-Cache zu leeren!

URL-Aufbau via RouteEnhancer

Ziel

Ziel ist es eine URL wie /category_slug/glossary_slug für die Detailseite zu haben.

  • Liste: .../glossary
  • Detail: .../glossary/r/rte

Beispiel: Site-Config RouteEnhancer

config/sites/<siteIdentifier>/config.yaml

routeEnhancers:
    GlossariesPlugin:
        type: Extbase
        limitToPages:
            - 123
        extension: Glossaries
        plugin: Glossary
        routes:
            -
                routePath: '/{category_slug}'
                _controller: 'Glossary::list'
                _arguments:
                    category_slug: selectedCategory
            -
                routePath: '/{category_slug}/{glossary_slug}'
                _controller: 'Glossary::show'
                _arguments:
                    category_slug: selectedCategory
                    glossary_slug: glossary
        defaultController: 'Glossary::list'
        aspects:
            category_slug:
                type: PersistedAliasMapper
                tableName: tx_glossaries_domain_model_glossarycategory
                routeFieldName: slug
                routeValuePrefix: /
            glossary_slug:
                type: PersistedAliasMapper
                tableName: tx_glossaries_domain_model_glossary
                routeFieldName: slug
                routeValuePrefix: /

Hinweis:

Das funktioniert nur stabil, wenn selectedCategorywirklich die UID der zugeordneten Kategorie enthält (oder per Aspect sauber auflösbar ist). Genau da hakt die Standard-Sitemap bei MM-Feldern oft.

Warum zeigt die sitemap.xml immer a als Kategorie in der URL?

Der Standard-Provider TYPO3\CMS\Seo\XmlSitemap\RecordsXmlSitemapDataProvider befüllt fieldToParameterMap bei N:M-Feldern nicht zuverlässig, weil im Datensatzfeld (categories) nicht automatisch die konkrete, sortierte Relation aus der MM-Tabelle aufgelöst wird.

Effekt: In fieldToParameterMap wird die Anzahl der zugewiesenen Relationen der N:M-Relation verwendet, welcher in unserem Fall 1 ist, weil wir ja nur genau eine Kategorie zulassen. Die Kategorie mit der UID 1 ist in unserem Beispiel zufällig Kategorie A – daher generiert die Sitemap immer URLs mit /a/....

Lösung: Custom SitemapDataProvider für die „erste“ Kategorie aus der MM-Tabelle

Prinzip

  • Vor dem URL-Build:

    • erste Kategorie-UID aus der MM-Tabelle holen (nach sorting_foreign)
    • $record['categories'] = <uid> setzen
  • Dann den Standard-Mechanismus weiter nutzen, damit dein fieldToParameterMap + RouteEnhancer greifen

Beispiel-Implementierung (erweiterter Records-Provider)

Dieser XmlSitemap-DataProvider wird auch in der EXT:glossaries in Classes/XmlSitemap/RecordsXmlSitemapDataProvider.php mitgeliefert.

<?php
declare(strict_types=1);
namespace CodingMs\Glossaries\XmlSitemap;
use Doctrine\DBAL\ParameterType;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Seo\XmlSitemap\RecordsXmlSitemapDataProvider as ParentProvider;
final class RecordsXmlSitemapDataProvider extends ParentProvider
{
    /** @var array<int,int> Cache: glossaryUid => firstCategoryUid */
    private array $firstCategoryUidCache = [];
    protected function defineUrl(array $data): array
    {
        $record = $data['data'] ?? null;
        if (!is_array($record) || empty($record['uid'])) {
            return parent::defineUrl($data);
        }
        $glossaryUid = (int)$record['uid'];
        // Only fix if the DB field is not already a usable UID
        // (with MM "count" behaviour this is usually 1 for all records, which is wrong)
        $firstCategoryUid = $this->resolveFirstCategoryUid($glossaryUid);
        if ($firstCategoryUid > 0) {
            // Make fieldToParameterMap pick the correct category UID
            $record['categories'] = $firstCategoryUid;
            $data['data'] = $record;
        }
        return parent::defineUrl($data);
    }
    private function resolveFirstCategoryUid(int $glossaryUid): int
    {
        if (isset($this->firstCategoryUidCache[$glossaryUid])) {
            return $this->firstCategoryUidCache[$glossaryUid];
        }
        $queryBuilder = $this->getQueryBuilder('tx_glossaries_glossary_glossarycategory_mm');
        $row = $queryBuilder
            ->select('uid_foreign')
            ->from('tx_glossaries_glossary_glossarycategory_mm')
            ->where(
                $queryBuilder->expr()->eq(
                    'uid_local',
                    $queryBuilder->createNamedParameter($glossaryUid, ParameterType::INTEGER)
                )
            )
            ->orderBy('sorting_foreign', 'ASC')
            ->addOrderBy('uid_foreign', 'ASC')
            ->setMaxResults(1)
            ->executeQuery()
            ->fetchAssociative();
        $firstUid = (int)($row['uid_foreign'] ?? 0);
        return $this->firstCategoryUidCache[$glossaryUid] = $firstUid;
    }
    private function getQueryBuilder(string $table): QueryBuilder
    {
        return GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
    }
}

TypoScript: SEO-Sitemap auf deinen Provider umstellen

Du ersetzt nur den Provider auf CodingMs\Glossaries\XmlSitemap\RecordsXmlSitemapDataProvider – der Rest kann bleiben.

Configuration/TypoScript/setup.typoscript

plugin.tx_seo.config.xmlSitemap.sitemaps.glossary glossaries {
    provider = CodingMs\Glossaries\XmlSitemap\RecordsXmlSitemapDataProvider
    config {
        table = tx_glossaries_domain_model_glossary
        sortField = title
        lastModifiedField = tstamp
        recursive = 1
        # Speicherort der Records
        pid = 1200
        url {
            # PageID der Detailseite
            pageId = 2038
            fieldToParameterMap {
                uid = tx_glossaries_glossary[glossary]
                categories = tx_glossaries_glossary[selectedCategory]
            }
            additionalGetParameters {
                tx_glossaries_glossary.controller = Glossary
                tx_glossaries_glossary.action = show
            }
        }
    }
}

Hinweis:

Die additionalGetParameters müssen zu deinem Plugin-Namespace passen (tx_<extkey>_<plugin>). Wenn dein Plugin anders heißt, musst du das anpassen.

Dokumentation
TYPO3 Glossaries Erweiterung

TYPO3 Glossaries

Die Glossaries Erweiterung für TYPO3 ermöglicht es Dir ein Website-Glossar zu erstellen und zu verwalten.

Menü

Kontaktanfrage

Du kannst uns jederzeit kontaktieren

Stop! Playing in the meantime?
Stop! Playing in the meantime?
Stop! Playing in the meantime?

Stop! Playing in the meantime?

Break the highscore

Press Start
Contact request
Screenreader label
Security question
YUN_________5IU______
7_S____5______R___MH5
F9W___FGM___TBJ______
__D____Q______F___HW7
4LK_________BHC______