Listenansicht und Detailansicht mit typoscript

Kommunikation eines fiktiven Projektes:

Kunde: “ ... und dann möchten wir hier auf der Seite noch ein paar Produkte als Liste darstellen und wenn man drauf klickt, soll eine Detailansicht kommen ...”

Entwickler: “Ach so, kein Problem. Da legen wir eine extra Tabelle an, erstellen ein Repository mit Models und einen Controller mit listaction und detailaction. Dann erstellen wir eine Seite mit der Listenansicht und eine mit der Detailansicht”.

Kunde: “Okay. Ich verstehe kein Wort - ich vertraue dir - mach weiter”.

Etwas später ...

Redakteur: “Ich verstehe nicht, warum ich einen extra sys_folder anlegen muss, in dem meine Produkte liegen. Und warum ist da so ein komisches Plugin auf der Seite mit der Listenansicht? Warum brauche ich eine extra Seite für die Details?"

Solche oder ähnliche Diskussionen hat wahrscheinlich jeder schon einmal geführt und hoffentlich daraus gelernt.

Im Folgenden zeige ich, wie man diese Probleme mit einfachen Mitteln lösen kann. Ich bin mir bewusst, dass eine extbase-Lösung viel mächtiger ist. Dennoch finde ich die Einfachheit dieser Lösung bestechend und sie zeigt einmal mehr die Stärken von typoscript.

Was soll genau passieren?

Das Ziel ist eine Seite mit einfachen Inhaltselementen für Produkte. Dazu erstellen wir ein einfaches tt_content Element mit einem Feld für den Teaser, einem Feld für die Detailinformationen und einem Bild. Zusätzlich soll das Element ein Slug-Feld haben, um eine lesbare Detail-URL zu erzeugen.

Das Ganze wird auf einer normalen Seite platziert.

Klickt man im Frontend auf ein Element, wird die Detailansicht angezeigt.

Zusätzlich sollte ein Backlink vorhanden sein.

Zuerst das Backend

Da ich es einfacher und übersichtlicher finde, verwende ich mst_yaml2tca, um das Inhaltselement im Backend zu erstellen.

Ich hoffe, der Code spricht für sich.

Da ich zwei neue Spalten in tt_content erstellt habe, muss die Datenbank angepasst werden:

ddev typo3 database:updateschema

columns:
  tt_content:
    slug:
      label: 'Slug'
      config:
        type: slug
        generatorOptions:
          fields:
            - header
          fieldSeparator: '/'
          prefixParentPageSlug: true


    description:
      label: 'Description'
      config:
        type: text
        enableRichtext: true


contentElements:
  new_mst_site_basic_elements:
    title: "New Basic elements yaml"
    elements:
      product:
        title: "Product"
        description: "Product"
        icon: "content-extension"

        config:
          columnsOverrides:
            bodytext:
              config:
                type: text
                enableRichtext: true

          showitem:
            - 
              title: "LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general"
              fields:
              - "--palette--;;general"
              - "--palette--;;header"
              - "slug"
              - "description"
              - "bodytext"
              - "media"
            -
              title: "LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance"
              fields:
                - "--palette--;;frames"
            -
              title: "LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access"
              fields:
                - "--palette--;;hidden"
                - "--palette--;;access"



        

Das sieht dann im Backen in etwa so aus

typoscript, HTML & route enhancer

Damit im frontend etwas erscheint benötigen wir ein paar Zeilen typoscript und ein Fluid-Template. Außerdem noch einen Route enhancer der die URL schön macht.

Da ich kein Freund von viel Text bin - hier ist Code:

tt_content {
    product =< lib.contentElement
    product {
        layoutRootPaths.20 =   EXT:mst_site/Resources/Private/Components/Layouts/
        partialRootPaths.20 =  EXT:mst_site/Resources/Private/Components/Partials/
        templateRootPaths.20 = EXT:mst_site/Resources/Private/Components/Templates/

        templateName = Product
        dataProcessing {
            10 = TYPO3\CMS\Frontend\DataProcessing\FilesProcessor
            10 {
                references.fieldName = media
                as = media
            }
        }

        variables {
            display = TEXT
            display{
                value = teaser

                override.cObject = TEXT
                override.cObject {
                    value = false
                    if.isTrue.data = GP:product|detail
                    
                    override.cObject = TEXT
                    override.cObject {
                        value = detail
                        if.equals.data = GP:product|detail
                        if.value.field = uid
                    }
                }
            }
        }
    }
}
<html xlmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:switch expression="{display}">
    <f:case value="detail">
        <div class="mstitb_col mstitb_col--100">
            <f:link.page>zurück</f:link.page>
            <div class="mstitb_product">
                <h4>{data.header}</h4>
                <f:if condition="{media.0}">
                    <f:image image="{media.0}" alt="{media.0.alternative}" width="800px" />
                </f:if>
                <f:format.html>{data.bodytext}</f:format.html>
            </div>
        </div>
    </f:case>
    <f:case value="teaser">
        <div class="mstitb_col mstitb_col--33">
            <div class="mstitb_product">
                <f:link.typolink parameter="{data.pid}" additionalParams="&product[detail]={data.uid}">
                    <h4>{data.header}</h4>
                    <f:if condition="{media.0}">
                        <f:image image="{media.0}" alt="{media.0.alternative}" width="300px" />
                    </f:if>
                    <f:format.html>{data.description}</f:format.html>
                </f:link.typolink>
            </div>
        </div>
    </f:case>
</f:switch>
</html>
routeEnhancers:
  Products:
    type: Plugin
    namespace: 'product'
    routePath: '/{product_detail}'
    _arguments:
      product_detail: 'detail'

    aspects:
      product_detail:
        type: PersistedAliasMapper
        tableName: tt_content
        routeFieldName: slug

Zusammenfassung

Man könnte das Ganze natürlich auch ohne zusätzliche Zeile machen, indem man die Detailansicht im Seitenbaum ablegt und auf der Listenansicht ein Menüelement mit Unterseiten platziert.
Aber vielleicht ist das in einem gewachsenen Projekt nicht möglich, weil es die gesamte Navigation ändern würde.
Ich bezweifle allerdings, dass dieser Vorschlag jemals produktiv umgesetzt wird. Falls doch, würde ich mich über einen entsprechenden Hinweis freuen.

Comments

No Comments

Write comment

* These fields are required