Beiträge der Kategorie gridelements

Abschnittsnavigation (aka menu_section) mit Gridelements

Wenn man mit Gridelements und somit verschachtelten Inhaltselementen arbeitet, stellt man vielleicht irgendwann fest, dass die Sortierung nicht funktioniert, sobald in der Abschnittsnavigation anzuzeigende Inhaltselemente in Container (2-Spalten etc.) angeordnet werden. Beispiel (in Klammern ist die Position intern):

  • Container A (1)
    • Element A.1 (1)
    • Element A.2 (2)
  • Container B (2)
    • Element B.1 (1)
    • Element B.2 (2)

Je nachdem, in welcher Reihenfolge man die Elemente erstellt hat, kann z.B. folgende Abschnittsnavigation ausgegeben werden:

  • B.1
  • A.1
  • A.2
  • B.2

Ich habe z.B. folgende Lösung mit einem ViewHelper gefunden. Ich wollte jedoch eine Lösung, die auch für bereits vorhandene Inhaltselemente funktioniert und die unabhängig vom Template eingesetzt werden kann. So habe ich mich für einen DataProcessor entschieden.

Als erstes hole ich mir alle Inhaltselemente – unabhängig davon, ob sectionIndex gesetzt ist oder nicht. Dann hänge ich meinen eigenen DataProcessor ein.

tt_content.menu_section.dataProcessing.10.dataProcessing.20 {
    where >
}
tt_content.menu_section.dataProcessing.10.dataProcessing.30 = NP\MyExtension\DataProcessing\SectionProcessor

Im DataProcessor werden die Datensätze gefiltert, so dass nur die mit sectionIndex = 1 übrig bleiben. Weiterhin wird die Sortierung korrigiert und es wird neu sortiert. Beim neuen Sorting wird der Wert der Elemente, die direkt auf der Seite abgelegt sind, mit 10000 multipliziert. Falls das Element in einem Container liegt, dann wird die Sortierung auf den Sorting-Wert des Containers mal 10000 plus die eigene Sortierung gesetzt.

Den DataProcessor lege ich unter Classes\DataProcessing\SectionProcessor.php ab:

namespace NP\MyExtension\DataProcessing;
 
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
 
class SectionProcessor implements DataProcessorInterface
{
 
    /**
     * Process content object data
     *
     * @param ContentObjectRenderer $cObj The data of the content element or page
     * @param array $contentObjectConfiguration The configuration of Content Object
     * @param array $processorConfiguration The configuration of this processor
     * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
     * @return array the processed data as key/value store
     */
    public function process(
        ContentObjectRenderer $cObj,
        array $contentObjectConfiguration,
        array $processorConfiguration,
        array $processedData
    ) {
        $processedContent = [];
        $sortMapping = [];
        foreach ($processedData['content'] as $content) {
            if ($content['data']['sectionIndex'] == 1) {
                $content['data']['sorting'] = $this->getRealSorting($content, $processedData['content']);
                $processedContent[] = $content;
            }
        }
        // debug($processedContent, 'processed content');
        usort($processedContent, [$this, 'sortContent']);
        $processedData['content'] = $processedContent;
        return $processedData;
    }
 
    /**
     * @param array $content
     * @param array $allElements
     */
    protected function getRealSorting($content, $allElements)
    {
        if ($content['data']['colPos'] != '-1') {
            return $content['data']['sorting'] * 10000;
        }
        $sorting = $content['data']['sorting'];
        foreach ($allElements as $element) {
            if ($element['data']['uid'] == $content['data']['tx_gridelements_container']) {
                $sorting += $element['data']['sorting'] * 10000;
            }
        }
        return $sorting;
    }
 
    /**
     * @param array $a
     * @param array $b
     * @return bool
     */
    protected function sortContent($a, $b)
    {
        return $a['data']['sorting'] > $b['data']['sorting'];
    }
}

Dieser Ansatz wird limitiert durch das Verschachtelungslevel der Elemente. Da könnte man diesen auf die schnelle implementierten Ansatz noch optimieren.

Plugins in GridElement scheinbar ohne Flexform-Settings

Falls jemand so wie ich einige Stunden (ok, ich übertreibe etwas) an diesem Problem verzweifeln sollte. Ich habe eine 3-spaltiges GridElement, das ich gemäß der aktuellsten Vorgabe mit dem DataProcessor umgesetzt habe. Übrigens ist die Dokumentation an dieser Stelle falsch.

Dort heißt es:

This is the default TypoScript setting provided while including the special Gridelements w/DataProcessing setup in your TS template editor:

lib.gridelements.defaultGridSetup =< lib.contentElement
lib.gridelements.defaultGridSetup {
[...]

Das Objekt lib.gridelements.defaultGridSetup gibt es nicht. Es wird tt_content.gridelements_pi1 hinzugefügt, der Rest der Angaben in der Dokumentation stimmt soweit.

Nun hatte ich also in einer Spalte ein Plugin mit ein paar Einstellungen, die über ein Flexform vorgenommen werden. In der "normalen" Content-Spalte funktioniert alles - meine Einstellungen werden im Frontend entsprechend ausgewertet. In einer Spalte in diesem GridElement hingegeben nicht - ich bekam in meinem Fall gar keine Ausgabe. Zum Glück bin ich zufällig über einen Issue gestolpert. Das Problem ist, dass der GridChildrenProcessor in Standardfall das Feld pi_flexform der Kinder parst und dabei in ein Array umwandelt. Wenn man die Inhalte der Spalte wie folgt ausgibt, dann wird die Eingabe im Feld pi_flexform nochmal verarbeitet.


Da es ja kein String mit XML-Inhalten, sondern ein Array ist, werden die Settings nicht ausgelesen und im Frontend nicht intepretiert. Das liegt an der Einstellung resolveChildFlexformData, die per default 1 ist. In diesem Issue wird vorgeschlagen, den Wert von resolveChildFlexformData per Default auf 0 zu setzen. Um breaking changes zu vermeiden, wird entschieden, diesen Wert wie vorher zu belassen, d.h. auf 1.

In meinem Fall ist die Lösung somit, diese Einstellung auf 0 zu setzen.

tt_content.gridelements_pi1.dataProcessing.10.default.options.resolveChildFlexFormData = 0