GLOBALES MENÜ AUS
DATENSÄTZEN GENERIERT

Globales Menü aus Datensätzen generiert

Für eine Produktübersicht ergab sich die Notwendigkeit, ein immer vorhandenes Menü für die Website zu generieren.

Der erste Gedanke war, hier ein Plugin zu bauen, welches die Datensätze holt, aufbereitet und ausliefert, damit im Fluid Template das Menü generiert werden kann.

Folgende Szenarien wären damit möglich gewesen:

Ein separates PlugIn auf jeder Seite einbinden

Warum nicht?

Man muss sich damit immer eine eigene Spalte im TYPO3 frei halten. Und das Plugin muss auf jeder Seite eingebunden werden.

Das PlugIn mit eigenem Content-Objekt einbinden

Das ist dann schon besser, man kann das Objekt dann gut über TypoScript abholen. Der Nachteil besteht aber auch hier immer noch darin, ein komplettes Plugin nur für ein Menü zu bauen und alles Extbase mitzuziehen. Außerdem kann ich mein Menü nicht im Template manipulieren.

Das Ziel ist der DatabaseQueryProcessor

Zunächst betrachtete ich also die Option, Daten mit dem DatabaseQueryProcessor zu holen.

Das hat gut funktioniert, ich hatte schnell ein Ergebnis:

10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
   table = tx_sudhaus7product_domain_model_product
   pidInList = 29
   as = prod
}

Diesen Prozessor im page-Objekt unter dataProcessing eingebunden hat mir meine Elemente schon mal sehr schön in mein Seitentemplate geladen. Im Fluid war die Handhabe hier auch recht einfach:

<f:for each="{prod}" as="product">
    <f:link.action
        arguments="{product: '{product.data.uid}'}"
        extensionName="sudhaus7product"
        controller="Product"
        action="show"
    />
</f:for>

Spannend wurde es, auch noch die Kategorie am Produkt mit auszugeben. Auch hier half mir der Prozessor wieder, ich kann ja jedem Processor neue dataProcessings zuweisen:

10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
   table = tx_sudhaus7product_domain_model_product
   pidInList = 29
   as = prod
   dataProcessing {
      10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
      10 {
         if.isTrue.field = category
         pidInList = 29
         table = tx_sudhaus7product_domain_model_category
         selectFields = tx_sudhaus7product_domain_model_product.*
         where.data = field:uid
         where.wrap = tx_sudhaus7product_domain_model_product_category_mm.uid_local=|
         join (
                     tx_sudhaus7product_domain_model_product_category_mm on tx_sudhaus7product_domain_model_product_category_mm.uid_foreign=
                    tx_sudhaus7product_domain_model_category.uid
         )
         orderBy = tx_sudhaus7product_domain_model_product_category_mm.sorting
         as = category
      }
   }
}

Somit habe ich bei jedem Durchlauf eines Produktes die jeweils zugehörigen Kategorien noch mit dabei.

Man kann, wenn man es benötigt, natürlich erst die Kategorien und dann die Produkte durchlaufen.

Einzig eine Sache fehlt jetzt noch für ein richtiges Menü: Der Aktivzustand.

Hier bediene ich mich der Tatsache, dass jede Anfrage an ein Produkt und/oder eine Kategorie in TYPO3 nur ein GET-Aufruf ist.

Mit diesem Wissen und der Tatsache, dass der DatabaseQueryProcessor analog zu CONTENT.select in TypoScript arbeitet, ergibt sich daraus folgende Änderung im TypoScript, um zu jedem Element das active-flag zu haben:

10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
10 {
   table = tx_sudhaus7product_domain_model_product
   pidInList = 29
   as = prod
   selectFields.stdWrap.cObject = COA
   selectFields.stdWrap.cObject {
      stdWrap.htmlSpecialChars = 1
      10 = TEXT
      10.value = tx_sudhaus7product_domain_model_product.*
      10.wrap = |,
      20 = TEXT
      20.value = 0
      20.override.data = gp:tx_sudhaus7product|product
      20.wrap = wrap = IF(tx_sudhaus7product_domain_model_category.uid=|, 1, 0) as active
   }
   dataProcessing {
      10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
      10 {
         if.isTrue.field = category
         pidInList = 29
         table = tx_sudhaus7product_domain_model_category
         selectFields.stdWrap.cObject = COA
         selectFields.stdWrap.cObject {
            stdWrap.htmlSpecialChars = 1
            10 = TEXT
            10.value = tx_sudhaus7product_domain_model_product.*
            10.wrap = |,
            20 = TEXT
            20.value = 0
            20.override.data = gp:tx_sudhaus7product|category
            20.wrap = IF(tx_sudhaus7product_domain_model_category.uid=|, 1, 0) as active
         }
         where.data = field:uid
         where.wrap = tx_sudhaus7product_domain_model_product_category_mm.uid_local=|
         join (
                     tx_sudhaus7product_domain_model_product_category_mm on tx_sudhaus7product_domain_model_product_category_mm.uid_foreign=
                    tx_sudhaus7product_domain_model_category.uid
         )
         orderBy = tx_sudhaus7product_domain_model_product_category_mm.sorting
         as = category
      }
   }

Durch das Definieren des selectFields als COA und einer Überprüfung auf gesetzte GET-Parameter bekomme ich so zu jeder Kategorie und zu jedem Produkt noch das active-Flag dazu. HtmlSpecialChars sorgt dafür, dass kein XSS über die URL eingebaut werden kann.

selectFields.stdWrap.cObject = COA
selectFields.stdWrap.cObject {
   stdWrap.htmlSpecialChars = 1
   10 = TEXT
   10.value = tx_sudhaus7product_domain_model_product.*
   10.wrap = |,
   20 = TEXT
   20.value = 0
   20.override.data = gp:tx_sudhaus7product|product
   20.wrap = wrap = IF(tx_sudhaus7product_domain_model_category.uid=|, 1, 0) as active
}

Insgesamt finde ich das Menü so eine gelungene Lösung. Der Overhead durch ein Extbase-Plugin wird komplett vermieden, die Daten werden aus der Datenbank direkt geholt. Das Menü lässt sich ohne Probleme und Tricks in das Template der Seite einbinden und muss nicht im Plugin gepflegt werden.

Dank der verschachtelbaren dataProcessings kann man hier zum Beispiel auch die Produkt- oder Kategoriebilder noch mit holen und ein Kachelmenü gestalten. Somit ist auch noch eine gute Erweiterbarkeit gegeben.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.