..  include:: /Includes.rst.txt

..  _feature-108508-1765987901:

===============================================
Feature: #108508 - Fluid components integration
===============================================

See :issue:`108508`

Description
===========

Fluid 4.3 introduced the concept of components to Fluid (see
`Components <https://docs.typo3.org/permalink/fluid:components>`_). Since then, it
was already possible to use components in TYPO3 projects by creating a custom
:php:`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
: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:

..  code-block:: php
    :caption: EXT:my_extension/Configuration/Fluid/ComponentCollections.php

    <?php

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

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

..  code-block:: html

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

    <my:organism.header.navigation />

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,
:html:`<my:organism.header.navigation />` would point to
:file:`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
:html:`<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 :html:`<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:

..  code-block:: php
    :caption: 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,
        ],
    ];

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

It is possible to influence certain aspects of Fluid components using PSR-14 events,
see :ref:`PSR-14 events for Fluid components <feature-108508-1765987847>`

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> <https://docs.typo3.org/permalink/t3viewhelper:typo3fluid-fluid-argument>`_.
Also, the `Slot ViewHelper <f:slot> <https://docs.typo3.org/permalink/t3viewhelper:typo3fluid-fluid-slot>`_
can be used to receive HTML content.

Example:

..  code-block:: html
    :caption: 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>

The example also demonstrates that components can (and should) use other components, in this
case :html:`<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:

..  code-block:: html

    <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>

You can learn more about components in
`Defining Components <https://docs.typo3.org/permalink/fluid:components-definition>`_. 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:

..  code-block:: php
    :caption: EXT:vendor_extension/Configuration/Fluid/ComponentCollections.php

    <?php

    return [
        'SomeVendor\\VendorExtension\\Components' => [
            'templatePaths' => [
                10 => 'EXT:vendor_extension/Resources/Private/Components',
            ],
        ],
    ];

..  code-block:: php
    :caption: EXT:my_extension/Configuration/Fluid/ComponentCollections.php

    <?php

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

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.

..  index:: Fluid, ext:fluid
