Using Fluid in TYPO3 

Here are some examples of how Fluid can be used in TYPO3:

Changed in version 14.0

These classes were marked as deprecated in TYPO3 v13.3 and have been removed in v14:

  • \TYPO3\CMS\Fluid\View\StandaloneView
  • \TYPO3\CMS\Fluid\View\TemplateView
  • \TYPO3\CMS\Fluid\View\AbstractTemplateView
  • \TYPO3\CMS\Extbase\Mvc\View\ViewResolverInterface
  • \TYPO3\CMS\Extbase\Mvc\View\GenericViewResolver

ViewHelper namespaces 

Defining global Fluid namespaces 

New in version 14.1

The extension-level configuration file Configuration/Fluid/Namespaces.php registers and extends global Fluid namespaces. Previously, the configuration $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces'] was used.

For example, we can define two global namespaces with the identifiers 'myext' and 'mycmp':

EXT:my_extension/Configuration/Fluid/Namespaces.php
<?php

return [
    'myext' => ['MyVendor\\MyExtension\\ViewHelpers'],
    'mycmp' => ['MyVendor\\MyExtension\\Components'],
];
Copied!

Assuming you have defined a Fluid component in EXT:my_extension/Resources/Private/Components/Button/Button.fluid.html then you can access the Button component via

EXT:my_extension/Resources/Private/Templates/SomeOtherTemplate.fluid.html
<mycmp:button title="{title}" teaser="{teaser}" />
Copied!

It is possible to override ViewHelpers that are in another extension. This is done by TYPO3 reading and merging Configuration/Fluid/Namespaces.php files in loaded extensions in the usual loading order. Loading order can be changed by declaring dependencies in composer.json (and possibly ext_emconf.php). In other words, if an extension registers a namespace that has already been registered by another extension, Fluid will merge the namespaces.

Example (my_extension2 depends on my_extension1):

EXT:my_extension1/Configuration/Fluid/Namespaces.php
<?php

return [
    'myext' => ['MyVendor\\MyExtension1\\ViewHelpers'],
];
Copied!
EXT:my_extension2/Configuration/Fluid/Namespaces.php
<?php

return [
    'myext' => ['MyVendor\\MyExtension2\\ViewHelpers'],
];
Copied!

This results in namespace definition:

[
    'myext' => [
        'MyVendor\\MyExtension1\\ViewHelpers',
        'MyVendor\\MyExtension2\\ViewHelpers',
    ],
];
Copied!

The processing order is in reverse, which means that <myext:demo /> would first check for EXT:my_extension2/Classes/ViewHelpers/DemoViewHelper.php, and then fall back to EXT:my_extension1/Classes/ViewHelpers/DemoViewHelper.php.

Importing Fluid namespaces locally 

Say you have defined a Fluid component in EXT:my_extension/Resources/Private/Components/Button/Button.fluid.html. Instead of defining the Fluid namespace globally you can specify the Fluid namespace like this:

EXT:my_extension/Resources/Private/Templates/SomeOtherTemplate.fluid.html
<html
    xmlns:my="http://typo3.org/ns/MyVendor/MyExtension/Components"
    data-namespace-typo3-fluid="true"
>

<my:button title="{title}" teaser="{teaser}"/>
Copied!

The namespace here is 'my'. For further information visit ViewHelper namespaces in Fluid explained.

Using the generic view factory (ViewFactoryInterface) 

New in version 13.3

Class \TYPO3\CMS\Core\View\ViewFactoryInterface has been added as a generic view factory interface to create views that return an instance of \TYPO3\CMS\Core\View\ViewInterface . This implements the "V" of "MVC" in a generic way and is used throughout the TYPO3 core.

You can inject an instance of the \TYPO3\CMS\Core\View\ViewFactoryInterface to create an instance of a \TYPO3\CMS\Core\View\ViewInterface where you need one.

EXT:my_extension/Classes/Controller/MyController.php (Not Extbase)
<?php

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;

final readonly class MyController
{
    public function __construct(
        private ViewFactoryInterface $viewFactory,
    ) {}

    public function myAction(ServerRequestInterface $request): string
    {
        $viewFactoryData = new ViewFactoryData(
            templateRootPaths: ['EXT:my_extension/Resources/Private/Templates'],
            partialRootPaths: ['EXT:my_extension/Resources/Private/Partials'],
            layoutRootPaths: ['EXT:my_extension/Resources/Private/Layouts'],
            request: $request,
        );
        $view = $this->viewFactory->create($viewFactoryData);
        $view->assign('mykey', 'myValue');
        return $view->render('path/to/template');
    }
}
Copied!

The ViewFactoryInterface needs an instance of \TYPO3\CMS\Core\View\ViewFactoryData , which is a data object and should therefore be created via new.

Best practices in creating a ViewFactoryData instance:

  • Hand over request of type \Psr\Http\Message\ServerRequestInterface if possible. See Getting the PSR-7 request object.
  • Use the tuple $templateRootPaths, $partialRootPaths and $layoutRootPaths if possible by providing an array of "base" paths like 'EXT:my_extension/Resources/Private/(Templates|Partials|Layouts)'
  • Avoid using parameter $templatePathAndFilename
  • Call render('path/within/templateRootPath') without file-ending on the returned ViewInterface instance.