Kapp-Hamburg
Web-Entwicklung & Hosting – Thorben Nissen

TypoScript & Fluid in eID-Skript

Für einen AJAX-Request wollte ich gern ein eID-Skript verwenden, um nicht das ganze Frontend laden zu müssen. Ich wollte aber dennoch Fluid für das Template nutzen und auch die Pfade per TypoScript konfigurieren können. In einem inzwischen etwas eingestaubten Artikel habe ich einen guten Ansatz gefunden. eID-Skripts haben grundsätzlich den Vorteil, dass diese sehr früh in der Bearbeitung des Requests ansetzen und damit einiges an Ausführungszeit gespart wird. In den aktuellen Versionen (7.6 und höher) wird direkt im Bootstrap entschieden, welcher Handler für den aktuellen Request in Frage kommt.

Die Konfiguration eines eID-Skripts sieht immer noch genauso verstaubt aus wie früher:

$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['your_eid_key'] = PathTo\YourExt\Controller\EidController::class . '::indexAction';

Dadurch, dass der Request-Prozess nun aber durch den Bootstrap, über die Request-Handler und im Falle dieser Konfiguration über den Dispatcher läuft, sieht die indexAction dann wiefolgt aus:

/**
 * @param \Psr\Http\Message\ServerRequestInterface $request
 * @param \Psr\Http\Message\ResponseInterface      $response
 */
public function indexAction(ServerRequestInterface $request, ResponseInterface $response)
{
    switch ($request->getMethod()) {
        case 'GET':
            $this->processGetRequest($request, $response);
            break;
        case 'POST':
            $this->processPostRequest($request, $response);
            break;
        default:
            $response->withStatus(405, 'Method not allowed');
    }

    return $response;
}

In diesem Fall wollte ich zwischen GET (nur ein Formular anzeigen) und POST (Formular verarbeiten) unterscheiden. Sollte das Skript mit einer anderen Methode aufgerufen werden, soll es mit dem Status 405 Method not allowed antworten. Da ich im JavaScript den Content-Type der Antwort prüfe, setze ich diesen explizit. Für einen GET-Request kann nur text/html zurück kommen, für POST kann aber sowohl text/html als auch application/json zurück gegeben werden.

/**
 * @param \Psr\Http\Message\ServerRequestInterface $request
 * @param \Psr\Http\Message\ResponseInterface      $response
 */
protected function processGetRequest(ServerRequestInterface $request, ResponseInterface $response) {
    $view = $this->getView();

    $response->withHeader('Content-type', ['text/html; charset=UTF-8']);
    $response->getBody()->write($view->render());
}

/**
 * @param \Psr\Http\Message\ServerRequestInterface $request
 * @param \Psr\Http\Message\ResponseInterface      $response
 */
protected function processPostRequest(ServerRequestInterface $request, ResponseInterface $response)
{
    $view = $this->getView();
    $hasErrors = false;
    // ... some logic

    if ($hasErrors) {
        $response->withHeader('Content-type', ['text/html; charset=UTF-8']);
        $response->getBody()->write($view->render());
    } else {
        $response->withHeader('Content-type', ['application/json; charset=UTF-8']);
        $response->getBody()->write(json_encode(['success' => true]));
    }
}

Der spannenste Teil ist aber das Initialisieren der View. Zunächst wird das PageRepository instanitiert und die aktuelle Rootline ermittelt. In diesem Fall benutze ich die Startseite der aktuellen Domain für die Rootline, da sich dort auch das gesammelte TypoScript befindet. Danach wird das TypoScript geparsed, eine StandaloneView erzeugt und die Layout-, Template-, und PartialRootPaths gesetzt. Um auf die locallang.xlf in der Extension zugreifen zu können, setze ich im Request noch den Extension-Namen. Mit $fluidView->setTemplate('index') setze ich dann das konkrete Template. Wird keine passende Template-Datei gefunden, bricht das Skript hier mit einer Exception ab.

/**
 * @return \TYPO3\CMS\Fluid\View\StandaloneView
 */
protected function getView() {
    $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
    $templateService = GeneralUtility::makeInstance(TemplateService::class);
    // get the rootline
    $rootLine = $pageRepository->getRootLine($pageRepository->getDomainStartPage(GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY')));
    // initialize template service and generate typoscript configuration
    $templateService->init();
    $templateService->runThroughTemplates($rootLine);
    $templateService->generateConfig();

    $fluidView = new StandaloneView();
    $fluidView->setLayoutRootPaths($templateService->setup['plugin.']['tx_yourext.']['view.']['layoutRootPaths.']);
    $fluidView->setTemplateRootPaths($templateService->setup['plugin.']['tx_yourext.']['view.']['templateRootPaths.']);
    $fluidView->setPartialRootPaths($templateService->setup['plugin.']['tx_yourext.']['view.']['partialRootPaths.']);
    $fluidView->getRequest()->setControllerExtensionName('YourExt');
    $fluidView->setTemplate('index');

    return $fluidView;
}

Den Code habe ich für TYPO3 CMS 7.6 geschrieben, sollte aber auch für 8.7 funktionieren.