WEBcoast Logo

TypoScript & Fluid in eID scripts

For implementing the backend for an ajax request I'd like to use an eID script to avoid loading the whole frontend. But I wanted to use Fluid for templating and TypoScript for configuring the template paths. In an older article (german) I found a good approach to start with. eID scripts in general have the great advantage, that they come in early in the whole request processing and therefore save some time and resources. In the more current versions (7.6 and newer) it is decided directly in the boostrap, which handler should take care of the request.

The configuration of an eID script looks as ancient as ever:

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

Due to the request process is handled in the bootstrap, the request handlers and, in case of this configuration (above), through the dispatcher, the indexAction looks like this:

/**
 * @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;
}

I wanted to handle GET (just show a form) and POST (process the form) separately. Should the script, however, be called with another HTTP method, it shall respond with the status 405 Method not allowed. As I check the returned Content-Type header in javascript, I explicitly set it. The response to a GET request can only be of type text/html, but the POST request can also have application/json as Content-Type.

/**
 * @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]));
    }
}

The most interesting part is though the initialization of the view. First the PageRepository is instantiated and the rootline is determined. In this case I use the current domain's homepage as the rootline, because this is where all the TypoScript templates reside. Afterwards the TypoScript gets parsed, a StandaloneView is created and the layout, template and partial root paths are set. To access the extension's locallang.xlf, I set the extension name in the view's request object. At last I set the concrete template by calling $fluidView->setTemplate('index'). If no matching template file is found, the script throws an exception at this point.

/**
 * @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;
}

I wrote this code for TYPO3 CMS 7.6, but it should also work in 8.7.