Typo3: TCA Default-Werte dynamisch setzen, per TSConfig oder Script

26. November 2016

Problem: Man möchte, dass beim Anlegen eines neuen Datensatzes in Typo3 (z.B. News- oder Kalender-Eintrag) im Backend automatisch bestimmte Default-Werte für die Eingabefelder (TCA) gesetzt werden. Die default-Werte des TCA sollen sich aber dynamisch ändern, z.B. abhängig von der aktuell gewählten Seite (pid).

Anwendungsbeispiel: Mehrere Redakteure legen News für unterschiedliche Zentren an. Für jedes Zentrum gibt es einen eigenen SysFolder. Abhängig von Ordner soll die News direkt einer bestimmten News-Kategorie zugeordnet werden.

Für Suchmaschinen: Setzen von default-Werten TCA Typo3. Typo3 dynamically set default TCA values with TypoScript. Hook Signal Slot for setting TCA default value. News (tx_news) automatisch einer News-Kategorie / sys_category zuweisen, abhängig von der pid. SysFolder automatisch bestimmte default-Werte oder News-Kategorie. TCAdefaults per Hook setzen. TCA default-Werte dynamisch in Signal Slot setzen. Typo3 7.6 FormEngine getSingleField_preProcess getSingleFieldClass Hook. Typo3 TCA default Werte dynamisch per Script setzen, abhängig von der pid aktueller Seite.

Einfache Anwendungsfälle

Lösung per PageCondition in page TSconfig mit TCAdefaults

Definition in der pageTsConfig: Beim Durchlesen der Typo3 Dokumentation zu dem Thema klingt die page TSconfig für TCEFORM zunächst relativ vielversprechend:

TCEFORM.[table name].[field].config.[key] = Wert

Diese Einstellung funktioniert allerdings nur, wenn man damit z.B. die Anzahl der Zeilen eines Textfeldes ändern möchte, hier am Beispiel der Extension News (tx_news):

TCEFORM.tx_news_domain_model_news.title.config.type = text
TCEFORM.tx_news_domain_model_news.title.config.rows = 10

Irreführend ist, dass das Setzen eines default-Wertes damit nicht möglich, dafür aber mit TCAdefaults:

# No chance!!
TCEFORM.tx_news_domain_model_news.title.config.default = Mein Default-Titel
# So geht es:
TCAdefaults.tx_news_domain_model_news.title = Wundervoll!

Mit Hilfe einer einfache Page-Condition kann damit die Aufgabe für simple Anwendungsfälle gelöst werden:

[PIDinRootline = 21]
   TCAdefaults.tx_deineextension_domain_model_entry.title = Artikel auf Seite 21
[PIDinRootline = 23]
   TCAdefaults.tx_deineextension_domain_model_entry.title = Artikel auf Seite 23
[global]

Kleines Wunder nebenbei: das Setzen der Werte funktioniert auch mit MM-Relationen, z.B. für sys_category! Solle eine News direkt bestimmten sys_category-Kategorien zugeordnet werden, kann das über eine kommaseparierte Liste mit den uids der sys_category gemacht werden:

TCAdefaults.tx_news_domain_model_news.categories = 1,2,7

Komplexere Anwendungsfälle

Lösung per Hook – bis Typo3 Version 4.7

Wie in einem schönen Artikel von Irene Höppner beschrieben, gab es bis zu Typo3 Version 4.7 einen wundervollen Hook (getSingleField_preProcess bzw. getSingleFieldClass), um das Problem zu lösen. Ein Artikel auf StackOverflow bringt die Lösung auf den Punkt: tt_news Autor automatisch per Hook setzen
Seit dem Einsatz der neuen FormEngine wurden aber ersatzlos alle Hooks und Signal/Slots entfernt.

Stefan Frömken schlägt für Typo3 > 6.2 eine modernere Lösung vor, die sich aber für die meisten Anwendungsfälle etwas zu aufwändig anfühlt.

Lösung per dynamischem User TSconfig in der ext_localconf.php

In einem Artikel von Netzhaut beschrieben, besteht die Möglichkeit, dass User TSconfig dynamisch einzubinden. Das erlaubt z.B. auch die Verwendung von PHP Code.

Entgegen des TCA (dessen final generiertes Array im Cache landet), wird der Inhalt der ext_localconf.php bei jedem Seitenaufruf im Backend neu gerendert. Dadurch wird in dem Beispiel der TimeStamp jedes Mal neu generiert:

Dazu kommt diese Zeile in die ext_localconf.php der eigenen Extension:

 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addUserTSConfig(
   'TCAdefaults.tx_nnportfolio_domain_model_entry.title = Aktueller tstamp: '.mktime()
);

Lösung per dynamischem Page TSconfig in der ext_localconf.php

Das Gleiche funktioniert mit einer dynamischen Erweiterung der PageTSconfig:

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig(
   'TCAdefaults.tx_nnportfolio_domain_model_entry.title = Aktueller tstamp: '.mktime()
);

Maximal komplexe Anwendungsfälle

Wem das noch nicht reicht, der könnte das Problem noch deutlich flexibler lösen. In folgendem Beispiel sollen die Default-Werte für einen Datensatz per TypoScript-Setup definierbar sein. Dazu definieren wir alle Default-Werte im Setup – key ist dabei die pid der entsprechenden Seite:

plugin.tx_nnportfolio_nnportfolio {
   settings {
      defaultValuesByPid {
         21 {
            title = Hallo! Du bist auf Seite 21.
            addresses = 2,3
            categories = 1,2,3,4,5
         }
         23 {
            title = Das ist Seite 23.
            addresses = 1
            categories = 5
         }
 
      }      
   }
}

In die ext_localconf.php kommt eine Funktion, die unseren eigenen “Hook” aufruft und die TCA-Spalten für unsere Extension übergibt:

if (TYPO3_MODE == 'BE') {
   \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('Nng\\Nnportfolio\\Hooks\\TCAdefaultValuesHook')
      ->setDefaultValuesFromTS( $GLOBALS['TCA']['tx_nnportfolio_domain_model_entry']['columns'] );
}

Dann fehlt nur noch der entsprechende Hook. Er enthält einige zusätzliche Methoden, um im Backend an das TypoScript Setup für die aktuell ausgewählte Seite zu kommen.

 
<?php
 
namespace Nng\Nnportfolio\Hooks;
 
 
class TCAdefaultValuesHook {
 
   /**
   *   Get TypoScript Setup in backend
   *
   */
   public static function getTsSetup () {
 
      $pageUid = self::get_current_pid($pageUid);
 
      $sysPageObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
      $rootLine = $sysPageObj->getRootLine($pageUid);
 
      $TSObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\ExtendedTemplateService');
      $TSObj->tt_track = 0;
      $TSObj->init();
      $TSObj->runThroughTemplates($rootLine);
      $TSObj->generateConfig();
 
      $typoscriptService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\Extbase\\Service\\TypoScriptService');
 
      return $typoscriptService->convertTypoScriptArrayToPlainArray(
         (array) $TSObj->setup['plugin.']['tx_nnportfolio_nnportfolio.']
      );
 
   }
 
   /**
   *   Get current PID in backend.
   *   Uses various fallbacks depending on current view and backend module.
   *   ToDo: Ask somebody, how this can be done simple :)
   */
   public static function get_current_pid ( $pageUid = null ) {
      if (!$pageUid) $pageUid = (int) $GLOBALS['_REQUEST']['popViewId'];
      if (!$pageUid) $pageUid = (int) preg_replace( '/(.*)(id=)([0-9]*)(.*)/i', '\\3', $GLOBALS['_REQUEST']['returnUrl'] );
      if (!$pageUid) $pageUid = (int) preg_replace( '/(.*)(id=)([0-9]*)(.*)/i', '\\3', $GLOBALS['_POST']['returnUrl'] );
      if (!$pageUid) $pageUid = (int) preg_replace( '/(.*)(id=)([0-9]*)(.*)/i', '\\3', $GLOBALS['_GET']['returnUrl'] );
      if (!$pageUid) $pageUid = (int) $GLOBALS['TSFE']->id;
      if (!$pageUid) $pageUid = (int) $_GET['id'];
      if (!$pageUid) {
         list($page) = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordsByField('pages', 'pid', 0);
          $pageUid = intval($page['uid']);
      }
      return $pageUid;
   }
 
 
   /**
   *   Set TCA default value from TypoScript setup
   *
   */
   public static function setDefaultValuesFromTS ( &$columns ) {
 
      $settings = self::getTsSetup();
      $pid = self::get_current_pid();
 
      if ($defaultValuesByPid = $settings['settings']['defaultValuesByPid'][$pid]) {      
         foreach ($defaultValuesByPid as $k=>$v) {
            $columns[$k]['config']['default'] = $v;
         }
      }
   }
 
 
}