Dynamische Backend-Formulare in TYPO3 mit „flux“

Komplexe Anwendungen wie Produktdatenbanken erfordern, Datensätzen dynamisch Eigenschaften bereitzustellen – ein Fall-Beispiel mit „flux“ und TYPO3.

von

Technische Produkte haben meist sehr viele Eigenschaften, wie beispielsweise Abmessungen, Gewicht, Angaben zu Betriebsumgebungen und weitere, spezielle Angaben zu Schnittstellen oder Softwareanforderungen. Diese sind jedoch nicht statisch, sondern es können jederzeit neue Eigenschaften hinzukommen oder auch bestehende Eigenschaften entfallen – z.B. bei der Einführung neuer Produkte oder Produktgruppen.

PIM-Systeme zur Produktverwaltung

Bei einer sehr hohen Anzahl von Produkten kommen zur Verwaltung der Produktdaten und -Medien oft sogenannte Produktinformationsmanagement-Systeme (PIM-Systeme) zum Einsatz. Diese bieten meist einen Webservice als Schnittstelle zu Content-Management Systemen wie TYPO3 an, um Produkte in die Website zu integrieren.

Unterhalb einer gewissen Anzahl an Produkten kann die Einführung eines PIM-Systems jedoch eine hohe Investition darstellen. Dennoch besteht der Wunsch, Produktdaten strukturiert verwalten und auf der Website darstellen zu können. Wie dies in TYPO3 möglich ist, soll dieses Fall-Beispiel zeigen.

Datensätze mit statischen Eigenschaften

Nehmen wir den einfachsten Fall: Produkte sollen in TYPO3 als einzelne Datensätze gepflegt werden können. Hierzu könnte man eine Tabelle für Produkt-Objekte anlegen, die in einzelnen Spalten die gewünschten Eigenschaften enthält.

Dies ist natürlich unflexibel: Neue Eigenschaften erfordern eine Anpassung der Tabelle, Eigenschaften können so nicht durch den Redakteur bearbeitet werden. Eine dynamische Speicherung von Eigenschaften über Flexform oder JSON in einer einzigen Spalte wäre zwar denkbar, wirft aber neue Fragen auf: Woher kommt die Definition dieser Eigenschaften? Wie wird gesteuert, welche Eigenschaften für die aktuelle Produktgruppe überhaupt gültig sind?

Der TYPO3-Core bietet hierzu keine praktikable Lösung.

Das Konzept von Sachmerkmalsleisten

Produkteigenschaften lassen sich meist in sinnvolle Einheiten – den Sachmerkmalsleisten – gruppieren, die dann wiederum für bestimmte Produktgruppen gültig sind, beispielsweise „Dimensionen“, „Anschlüsse“ oder „Downloads“. Einige dieser Leisten sind für alle Produkte anwendbar, manche jedoch nur für spezielle Produktgruppen.

Folgende Lösung sieht vor, dass eine zentrale Pflege von Merkmalen und Sachmerkmalsleisten möglich ist. Den Produktgruppen werden dann diese Sachmerkmalsleisten zugewiesen, damit an den Produkten (Artikeln) genau die gültigen Produkteigenschaften im Backend-Formular bereitgestellt und bearbeitet werden können.

Dynamische Eigenschaften mit „flux“

Die Extension „flux“ bietet die Möglichkeit, Eigenschaften über Fluid-Templates, TypoScript oder PHP bereitzustellen und diese Werte als Flexform zu speichern. Über die PHP-API können wir uns diese Funktionen zu Nutze machen, um dynamische Eigenschaften und Sachmerkmalsleisten zu realisieren.

Das Domain Model in TYPO3

Wir benötigen folgende Objekte mit den dazugehörigen Datenbanktabellen:

  • Product (für Produkte)
  • ProductGroup (für Produktgruppen)
  • Property (für Eigenschaften)
  • PropertyGroup (für Sachmerkmalsleisten)

Zu diesen Objekten liegen die zugehörigen Models (mit Getter- und Setter-Methoden der statischen Eigenschaften) sowie Repositories und TCAs vor.

Um dynamische Eigenschaften zu speichern, benötigt die Produkttabelle eine Spalte „pi_flexform“ mit der entsprechenden TCA-Konfiguration des Typs „flex“. 

Dann kommt „flux“ ins Spiel: Damit TYPO3 dynamisch die passenden Backend-Formularfelder anzeigen kann, benötigen wir einfach nur einen eigenen Flux-Provider:

Classes/Provider/ProductConfigurationProvider.php


<?php
namespace Medienreaktor\Products\Provider;

use FluidTYPO3\Flux\Provider\AbstractProvider;
use FluidTYPO3\Flux\Provider\ProviderInterface;

class ProductConfigurationProvider extends AbstractProvider implements ProviderInterface
{
    /**
     * @var string
     */
    protected $tableName = 'tx_products_domain_model_product';

    /**
     * @var string
     */
    protected $fieldName = 'pi_flexform';

    /**
     * productRepository
     *
     * @var \Medienreaktor\Products\Domain\Repository\ProductRepository
     * @inject
     */
    protected $productRepository = null;

    /**
     * @param array $row
     * @return \FluidTYPO3\Flux\Form|NULL
     */
    public function getForm(array $row)
    {
        $form = \FluidTYPO3\Flux\Form::create();
        $form->setName('dynamicProperties');

        $product = $this->productRepository->findByUid($row['uid']);

        if ($product) {
            $productGroup = $product->getProductGroup();

            // Create a sheet for each property group
            foreach ($productGroup->getPropertyGroups() as $group) {
               $sheet = $form->createContainer(
                  'Sheet',
                  $group->getUid(),
                  $group->getName()
               );

               // Create a field for each property
               foreach ($group->getProperties() as $property) {

                  // The property holds the field type, e.g. 'Input'
                  $sheet->createField(
                     $property->getType(),
                     $property->getUid(),
                     $property->getName()
                  );
               }
            }
        }

        return $form;
    }
}

Registrierung des Providers in der ext_localconf der Extension:

ext_localconf.php


<?php
defined('TYPO3_MODE') || die('Access denied.');

call_user_func(
    function($extKey)
    {
        \FluidTYPO3\Flux\Core::registerConfigurationProvider(\Medienreaktor\Products\Provider\ProductConfigurationProvider::class);

        ...
    },
    $_EXTKEY
);

Dieser Provider stellt die Backend-Formularfelder für das „pi_flexform“-Feld bereit, indem die Sachmerkmalsleisten der zugehörigen Produktgruppe abgefragt und für diese die passenden Eingabefelder bereitgestellt werden.

Im Model des Produkts können wir diese dynamischen Eigenschaften folgendermaßen abrufen und für das Template-Rendering bereitstellen:

Classes/Domain/Model/Product.php


<?php
namespace Medienreaktor\Products\Domain\Model;

class Product extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
{
    ...

    public function getDynamicProperties() {
        $flexFormContent = $this->piFlexform;
        $flexFormService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Service\\FlexFormService');
        $fields = $flexFormService->convertFlexFormContentToArray($flexFormContent);

        return $fields;
    }

    ...
}

Je nach zugewiesenen Sachmerkmalsleisten werden im TYPO3-Backend die passenden Eingabefelder angezeigt.

Passende, dynamische Merkmale am Produkt im Backend-Formular

Für das Kundenprojekt, indem diese Lösung zum Einsatz kommt, haben wir auf ähnliche Weise zusätzlich eine Vererbungslogik implementiert (Produkteigenschaften können auch an den Produktgruppen gepflegt werden und diese werden automatisch auf die Produkte vererbt bzw. können dort überschrieben werden) – der Einfachheit halber haben wir diesen Code aber in obigen Beispielen entfernt.

Fazit und Download

Die Extension „flux“ bietet hier Möglichkeiten, die weit über die Fähigkeiten des TYPO3-Cores hinausgehen und unzählige weitere, komplexe Anwendungsszenarien möglich machen – z.B. auch die dynamische Konfiguration von Inhaltselementen durch den Redakteur (ähnlich DCE oder GridElements).

Download von „flux“ auf GitHub und im TER.