TCA im yaml

TCA aus yaml laden

Nachdem ich jetzt seit Jahren meine Inhaltselemente in unlesbaren TCA-Arrays definiere, ist mir endlich die zündende Idee gekommen.

Bei der Integration eine Designs steht man als Entwickler oft vor dem Punkt: Eigentlich wäre es schöner, wenn der Redakteur hier ein einfachen Element mit zwei Feldern hat - aber ach - da muss ich ja wieder TCA schreiben.

Da ich das oft genug gemacht habe, ist das für mich kein Problem. Aber ich habe einen Haufen Kollegen für die ist das ein Graus.

Deshalb hat sich über die Jahre eine Struktur entwickelt die das ganze ein bisschen vereinfachen soll.

Zuerst werden alle Elmente in einem einzigen Array gespeichert.

  $newElements = [
    'basic_elements' => [
      'title' => 'Basic elements',
      'postion' => 'after:special',
      'elements' => [
        'specialTextImage' => [
          'title' => 'Text mit Bild',
          'ctype' => 'specialTextImage',
          'icon' => 'content-image',
          'config' => [
            'columnsOverrides' => [
              'image' => [
                'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
                  'image', [
                  'appearance' => [
                    'createNewRelationLinkTitle' => 'Hinzufügen',
                  ],
                  'overrideChildTca' => [
                    'types' => [
                      \TYPO3\CMS\Core\Resource\File::FILETYPE_IMAGE => [
                        'showitem' => '
		                    --palette--;LLL:EXT:lang/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette,
		                    --palette--;;filePalette',
                      ],
                    ],
                    'columns' => [
                      'crop' => [
                        'config' => [
                          'cropVariants' => [
                            'desktop' => [
                              'title' => 'desktop',
                              'allowedAspectRatios' => [
                                'NaN' => [
                                  'title' => 'Frei',
                                  'value' => 0.0,
                                ],
                                'square' => [
                                  'title' => 'Fast Quadratisch',
                                  'value' => 455 / 450,
                                ],
                              ],
                              'cropArea' => [
                                'x' => 0,
                                'y' => 0,
                                'width' => 1,
                                'height' => 1,
                              ],
                            ],
                          ],
                        ],
                      ],
                    ],

                  ],
                  'maxitems' => 1,
                ],
                  'png,jpg,jpeg,svg'
                ),
              ],
            ],
            'showitem' => '
                            --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
                                --palette--;;general, --palette--;;header, subheader, image,
                            --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, --palette--;;frames,
                            --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
                                --palette--;;hidden,--palette--;;access,
                       ',
          ],
        ]
      ],
    ],
  ];
site/Configuration/TCA/Overrides/tt_content.php

Wird alles in einem Rutsch mittels foreach registriert:

  foreach ($newElements as $groupdId => $group) {
    ExtensionManagementUtility::addTcaSelectItemGroup(
      'tt_content',
      'CType',
      $groupdId,
      $group['title'],
      $group['position'] ?? 'bottom');
    foreach ($group['elements'] as $ctype => $element) {
      ExtensionManagementUtility::addTcaSelectItem(
        'tt_content',
        'CType',
        [
          $element['title'],
          $ctype,
          $element['icon'],
          $groupdId,
        ],
      );

      $GLOBALS['TCA']['tt_content']['types'][$ctype] = $element['config'];
      $GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes'][$ctype] = $element['icon'];

    }
  }
site/Configuration/TCA/Overrides/tt_content.php

Soweit so gut, aber hey, da war noch was. Wir müssen noch einen Eintrag in pageTsConfig machen. Damit der unser schönes Element auch in NewContentElementWizard angezeigt wird:

mod {
    wizards {
        newContentElement.wizardItems {
            new-elements {
                header = New Elements
                elements {
                    specialTextImage {
                        iconIdentifier = content-image
                        title = Text mit Bild
                        description = 
                        tt_content_defValues {
                            CType = specialTextImage
                        }
                    }
                }

                show = *
            }
        }
    }
}
site/Configuration/TSconfig/page.typoscript

Achja und dann brauchen wir auch noch typoscript. Das geht ganz grob so:
site/Configuration/typoscript/tt_content.typoscript

    monosite_menu_products =< lib.contentElement
    monosite_menu_products {
        templateName = MenuProducts
	}
site/Configuration/typoscript/tt_content.typoscript

Natürlich muss hier noch ein Template erstellt werden. Aber das erspare ich euch.

Wie geschrieben, das Ganze war für die Kollegen ein Graus und dass ich das pageTsConfig schreiben muss hat mich auch ganz schön genervt.

TCA in yaml

Dann kam die zündende Idee: Warum nicht alles in eine yaml-Struktur schreiben?
Und wenn ich schon dabei bin, dann zerlege ich noch diesen gruseligen showItem-String.

Das ganze sieht dann so aus:

sections:
  basic_elements:
    title: "Basic elements yaml"
    elements:
      text:
        title: Text
        icon: "content-text"
        defaultValues:
          header: "Defaulttext"
        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"
                - "subheader"
                - "bodytext"
            -
              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"
site/Configuration/TCA/Overrides/tt_content.yaml

Jetzt muss ich nur noch das yaml einlesen und in mein Array mergen bevor alles registriert wird.

Ich merge das hier mit dem Array von oben. Dann habe ich immer noch die Möglichkeit, bei komplizierten Anforderungen mit meine Elemente mit php zu beschreiben.

  if (file_exists(GeneralUtility::getFileAbsFileName('EXT:site/Configuration/TCA/Overrides/tt_content.yaml'))) {
    $tt_content = (new YamlFileLoader())->load(GeneralUtility::getFileAbsFileName('EXT:site/Configuration/TCA/Overrides/tt_content.yaml'));
    if (key_exists('sections', $tt_content) && is_array($tt_content['sections'])) {
      foreach ($tt_content['sections'] as &$section) {
        foreach ($section['elements'] as &$element) {
          $divs = [];
          foreach ($element['config']['showItem'] as $div) {
            $fields = implode(',', $div['fields']);
            $divs[] = '--div--;' . $div['title'] . ',' . $fields;
          }
          $element['config']['showitem'] = implode(',', $divs);
        }
      }
      $newElements = array_replace_recursive($tt_content['sections'], $newElements);
    }
  }
site/Configuration/TCA/Overrides/tt_content.php

Soweit so gut, aber hey, da war noch was. Wir müssen noch einen Eintrag in pageTsConfig machen. Damit der unser schönes Element auch in NewContentElementWizard angezeigt wird. Um das zu lösen, brauchen wir einen Listener den wir zuerste registrieren müssen.

services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false

  VENDOR\site\:
    resource: '../Classes/*'

  VENDOR\site\Listener\PageTsConfig:
    tags:
      - name: event.listener
        identifier: 'tx-site-page-ts-config'
        event: TYPO3\CMS\Core\TypoScript\IncludeTree\Event\ModifyLoadedPageTsConfigEvent
site/Configuration/Service.yaml
<?php

declare(strict_types=1);

namespace VENDOR\site\Listener;

/*
 * This file is part of TYPO3 CMS-based extension "container" by b13.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 */
use TYPO3\CMS\Core\TypoScript\IncludeTree\Event\ModifyLoadedPageTsConfigEvent;
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class PageTsConfig
{

  public function __invoke(ModifyLoadedPageTsConfigEvent $event): void
  {
    $tsConfig = $event->getTsConfig();
    if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() > 11) {
      $tsConfig = array_merge(['pageTsConfig-package-site' => $this->getPageTsString()], $tsConfig);
    } else {
      $tsConfig['default'] = trim($this->getPageTsString() . "\n" . ($tsConfig['default'] ?? ''));
    }
    $event->setTsConfig($tsConfig);
  }


  public function getPageTsString(): string
  {
    $pageTs = '';
    if (file_exists(GeneralUtility::getFileAbsFileName('EXT:site/Configuration/TCA/Overrides/tt_content.yaml'))) {
      $tt_content = (new YamlFileLoader())->load(GeneralUtility::getFileAbsFileName('EXT:site/Configuration/TCA/Overrides/tt_content.yaml'));
      if (key_exists('sections', $tt_content) && is_array($tt_content['sections'])) {
        foreach ($tt_content['sections'] as &$section) {
          foreach ($section['elements'] as &$element) {
            $divs = [];
            foreach ($element['config']['showItem'] as $div) {
              $fields = implode(',', $div['fields']);
              $divs[] = '--div--;' . $div['title'] . ',' . $fields;
            }
            $element['config']['showitem'] = implode(',', $divs);
          }
        }
        $newElements = $tt_content['sections'];
      }

      // group containers by group

      foreach ($newElements as $group => $groupConfigurations) {

        $content = '
mod.wizards.newContentElement.wizardItems.' . $group . '.header = ' . $groupConfigurations['title'] . '
mod.wizards.newContentElement.wizardItems.' . $group . '.show = *
';
        foreach ($groupConfigurations['elements'] as $cType => $elementConfiguration) {
          if (isset($elementConfiguration['defaultValues']) && is_array($elementConfiguration['defaultValues'])) {
            array_walk($elementConfiguration['defaultValues'], static function (&$item, $key) {
              $item = $key . ' = ' . $item;
            });
            $ttContentDefValues = 'CType = ' . $cType . LF . implode(LF, $elementConfiguration['defaultValues']);
          } else {
            $ttContentDefValues = 'CType = ' . $cType;
          }

          $content .= 'mod.wizards.newContentElement.wizardItems.' . $group . '.elements {
' . $cType . ' {
    title = ' . $elementConfiguration['title'] . '
    description = ' . ($elementConfiguration['description'] ?? '') . '
    iconIdentifier = ' . ($elementConfiguration['icon'] ?? '') . '
    tt_content_defValues {
    ' . $ttContentDefValues . '
    }
}
}
';
        }
        $pageTs .= LF . $content;
      }
    }
    return $pageTs;
  }
}
site/Classes/Listener/PageTsConfig.php

Comments

No Comments

Write comment

* These fields are required