Feature: #108508 - Fluid components integration 

See forge#108508

Description 

Fluid 4.3 introduced the concept of components to Fluid (see Components). Since then, it was already possible to use components in TYPO3 projects by creating a custom ComponentCollection class that essentially connects a folder of template files to a Fluid ViewHelper namespace. Using that class it was also possible to use an alternative folder structure for a component collection and to allow passing arbitrary arguments to components within that collection.

Now it is possible to define component collections purely with configuration. For the most common use cases, it is no longer necessary to create a custom PHP class, which makes it much easier for integrators to setup components in TYPO3 projects.

Registering component collections 

The new extension-level configuration file Configuration/Fluid/ComponentCollections.php is introduced, which allows extensions to register one or multiple new component collections. It is also possible to extend existing collections registered by other extensions (such as adding template paths to override components defined by another extension).

Basic example:

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

return [
    'MyVendor\\MyExtension\\Components' => [
        'templatePaths' => [
            10 => 'EXT:my_extension/Resources/Private/Components',
        ],
    ],
];
Copied!

Components in that collection can then be used in any Fluid template:

<html
    xmlns:my="http://typo3.org/ns/MyVendor/MyExtension/Components"
    data-namespace-typo3-fluid="true"
>

<my:organism.header.navigation />
Copied!

By default, component collections use a folder structure that requires a separate folder per component. This is handy if you want to put other files right next to your component template, such as the matching CSS or JS file, or even a custom language file. Using the example above, <my:organism.header.navigation /> would point to EXT:my_extension/Resources/Private/Components/Organism/Header/Navigation/Navigation.fluid.html.

If not otherwise specified, components use a strict API, meaning that all arguments that are passed to a component need to be defined with <f:argument> in the component template.

Both defaults can be adjusted per collection by providing configuration options:

  • templateNamePattern allows you to use a different folder structure, available variables are {path} and {name}. For <my:organism.header.navigation>, {path} would be Organism/Header and {name} would be Navigation.
  • setting additionalArgumentsAllowed to true allows passing undefined arguments to components.

Advanced example:

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

return [
    'MyVendor\\MyExtension\\Components' => [
        'templatePaths' => [
            10 => 'EXT:my_extension/Resources/Private/Components',
        ],
        'templateNamePattern' => '{path}/{name}',
        'additionalArgumentsAllowed' => true,
    ],
];
Copied!

Using this example <my:organism.header.navigation /> would point to EXT:my_extension/Resources/Private/Components/Organism/Header/Navigation.fluid.html (note the missing Navigation folder).

It is possible to influence certain aspects of Fluid components using PSR-14 events, see PSR-14 events for Fluid components

Creating components 

A typical component looks just like a normal Fluid template, except that it defines all of its arguments with the Argument ViewHelper <f:argument>. Also, the Slot ViewHelper <f:slot> can be used to receive HTML content.

Example:

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

<f:argument name="title" type="string" />
<f:argument name="link" type="string" />
<f:argument name="icon" type="string" optional="{true}" />

<a href="{link}" class="teaserCard">
    <f:if condition="{icon}">
        <my:atom.icon identifier="{icon}">
    </f:if>
    <div class="teaserCard__title">{title}</div>
    <div class="teaserCard__content"><f:slot /></div>
</a>
Copied!

The example also demonstrates that components can (and should) use other components, in this case <my:atom.icon>. Depending on the use case, it might also make sense to pass the output of one component to another component via a slot:

<html
    xmlns:my="http://typo3.org/ns/MyVendor/MyExtension/Components"
    data-namespace-typo3-fluid="true"
>

<my:molecule.teaserCard
    title="TYPO3"
    link="https://typo3.org/"
    icon="typo3"
>
    <my:atom.text>{content}</my:atom.text>
</my:molecule.teaserCard>
Copied!

You can learn more about components in Defining Components. Note that this is part of the documentation of Fluid Standalone, which means that it doesn't mention TYPO3 specifics.

Migration and co-existence with class-based collections 

Configuration-based and class-based component collections can be used side by side. For more advanced use cases, it might still be best to ship a custom class to define a component collection. However, most use cases can easily be migrated to the configuration-based approach, since they usually just consist of boilerplate code around the configuration options.

Since the new approach is not available in TYPO3 13, it is possible to ship both variants to provide backwards-compatibility: If a specific component collection is defined both via class and via configuration, in TYPO3 13 the class will be used, while in TYPO3 14 the configuration will be used and the class will be ignored completely.

Extending component collections from other extensions 

It is possible to extend the configuration of other extensions using the introduced configuration file. This allows integrators to merge their own set of components into an existing component collection:

EXT:vendor_extension/Configuration/Fluid/ComponentCollections.php
<?php

return [
    'SomeVendor\\VendorExtension\\Components' => [
        'templatePaths' => [
            10 => 'EXT:vendor_extension/Resources/Private/Components',
        ],
    ],
];
Copied!
EXT:my_extension/Configuration/Fluid/ComponentCollections.php
<?php

return [
    'SomeVendor\\VendorExtension\\Components' => [
        'templatePaths' => [
            1765990741 => 'EXT:my_extension/Resources/Private/Extensions/VendorExtension/Components',
        ],
    ],
];
Copied!

For template paths, the familiar rule applies: They will be sorted by their keys and will be processed in reverse order. In this example, if my_extension defines a component that already exists in vendor_extension, it will override the original component in vendor_extension.

Impact 

Fluid component collections no longer need to be defined by creating a custom class, but can now be registered purely by configuration. Existing class-based collections will continue to work. If a collection namespace is registered both by a class and by configuration, the configuration overrules the class and any custom code in the class is ignored.