Providing External Components

New in version Fluid 4.3

Fluid supports components that are defined as Fluid templates out-of-the-box. However, it is also possible to integrate external component libraries into your Fluid-based project. This makes it possible to bridge the gap between Fluid and other templating engines, such as Twig or Mustache.

Architecture Overview

Fluid's component features are based on several classes and interfaces. Some are meant to be extended/implemented, others merely exist for internal purposes and are annotated as @internal in the PHPDoc header.

The ComponentDefinitionProviderInterface, together with ComponentRendererInterface, are the primary interfaces that can be used (together with a ViewHelperResolver delegate) to "teach" Fluid how to interact with components. The definition provider returns instances of ComponentDefinition, which is an immutable DTO.

The ComponentAdapter is an adapter class that translates between the ViewHelper API and the described components API. It is used as default ViewHelper implementation for all components during parse-time and will never be used once a template is cached. It is marked @internal and is not intended to be extended/replaced.

The ComponentTemplateResolverInterface is an internal interface that is related to Fluid's own component implementation. Same goes for ComponentRenderer, which renders Fluid-based component templates. AbstractComponentCollection is the base implementation for Fluid-based components, which are documented in a separate chapter about Components.

Custom ComponentRenderer

The ComponentRendererInterface specifies how a component should be rendered. This is the place where e. g. an external templating engine would be initiated to render a specific template:

CustomComponentRenderer.php (component renderer)
namespace Vendor\MyPackage;

use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;

final readonly class CustomComponentRenderer implements ComponentRendererInterface
{
    public function renderComponent(
        string $viewHelperName,
        array $arguments,
        array $slots,
        RenderingContextInterface $parentRenderingContext,
    ): string {
        $view = new TemplatingEngine();
        $view->setVariables($arguments);
        $view->setSlots($slots);
        return $view->render($viewHelperName);
    }
}
Copied!

The $parentRenderingContext can be used to extract additional information from the parent Fluid template that should be passed to the component, such as a Request object.

Custom ComponentDefinitionProvider

The ComponentDefinitionProviderInterface provides Fluid with all necessary information to resolve, validate and render external components. Depending on the available component metadata, the Fluid parser is even able to pre-validate the supplied component parameters (defined and required arguments, booleans). However, it is also possible to provide a "non-strict" implementation where any argument can be supplied to the external components. The interface must always be used in combination with ViewHelperResolverDelegateInterface.

CustomComponentCollection.php (component definition provider)
namespace Vendor\MyPackage;

use TYPO3Fluid\Fluid\Core\Component\ComponentDefinition;
use TYPO3Fluid\Fluid\Core\ViewHelper\UnresolvableViewHelperException;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperResolverDelegateInterface;
use Vendor\MyPackage\CustomComponentRenderer;

final class CustomComponentCollection implements ViewHelperResolverDelegateInterface, ComponentDefinitionProviderInterface
{
    public function getComponentDefinition(string $viewHelperName): ComponentDefinition
    {
        $metadata = TemplatingEngine::getComponentMetadata($viewHelperName);
        $definition = new ComponentDefinition(
            // map metadata...
        );
        return $definition;
    }

    public function getComponentRenderer(): ComponentRendererInterface
    {
        return new CustomComponentRenderer();
    }

    public function resolveViewHelperClassName(string $viewHelperName): string
    {
        if (!TemplatingEngine::componentExists($viewHelperName)) {
            throw new UnresolvableViewHelperException(
                // ...
            );
        }
        return ComponentAdapter::class;
    }

    public function getNamespace(): string
    {
        return static::class;
    }
}
Copied!