Basic usage¶
This page shows how to create a single module and which components are necessary to process data from TYPO3, prepare it and finally output it in the frontend using a Handlebars template.
The CType header
serves as an example.
Note
This example only describes the standard process for creating a new module. Further possibilities are described in the section Extended usage.
Example¶
Note
All examples are written in PHP 7.4.
Preparations
Before the actual components are created, some preliminary work is necessary.
Installation
Install the extension using Composer.
Define dependencies
Add the extension as dependency to your extension as described here.
Services.yaml
Create a basic
Services.yaml
file in your extension. Make sure to read and follow the guidelines described at Dependency injection.TypoScript configuration
Create a TypoScript configuration file and include it in your site's root template. Make sure to include static TypoScript from EXT:fluid_styled_content.
Create a new
DataProcessor
Each
DataProcessor
must implementFr\Typo3Handlebars\DataProcessing\DataProcessorInterface
.There's already a default
DataProcessor
in place that provides some basic logic and is required in case you want to develop components like described on this page. Just extend yourDataProcessor
fromFr\Typo3Handlebars\DataProcessing\AbstractDataProcessor
and implement the abstract methodrender()
:# Classes/DataProcessing/HeaderProcessor.php namespace Vendor\Extension\DataProcessing; use Fr\Typo3Handlebars\DataProcessing\AbstractDataProcessor; class HeaderProcessor extends AbstractDataProcessor { protected function render(): string { $data = $this->provider->get($this->cObj->data); return $this->presenter->present($data); } }
Attention
Use the correct namespace
It is very important to create the
DataProcessor
with the correct namespace, as all other components will be automatically registered based on it.Register
DataProcessor
as serviceThe
HeaderProcessor
must now be registered in theServices.yaml
in the next step:# Configuration/Services.yaml services: Vendor\Extension\DataProcessing\HeaderProcessor: tags: ['handlebars.processor']
All related components (
DataProvider
,Presenter
) are now automatically assigned to thisDataProcessor
and registered accordingly.Tip
If all
DataProcessors
share the same configuration, they can also be registered all at once with the following configuration:# Configuration/Services.yaml services: Vendor\Extension\DataProcessing\: resource: '../Classes/DataProcessing/**/*Processor.php' tags: ['handlebars.processor']
Create a new
DataProvider
Next, a
DataProvider
must be created that prepares the module's data and makes it available to theDataProcessor
again. EachDataProvider
must implementFr\Typo3Handlebars\Data\DataProviderInterface
.# Classes/Data/HeaderProvider.php namespace Vendor\Extension\Data; use Fr\Typo3Handlebars\Data\DataProviderInterface; use Fr\Typo3Handlebars\Data\Response\ProviderResponseInterface; use Vendor\Extension\Data\Response\HeaderProviderResponse; class HeaderProvider implements DataProviderInterface { public function get(array $data): ProviderResponseInterface { return (new HeaderProviderResponse($data['header'])) ->setHeaderLayout((int)$data['header_layout']) ->setHeaderLink($data['header_link']) ->setSubheader($data['subheader']); } }
As you can see, the
DataProvider
returns an instance of a so-calledProviderResponse
object. This holds the prepared data for higher-level transfer within the rendering process. Create it in the associated namespace:# Classes/Data/Response/HeaderProviderResponse.php namespace Vendor\Extension\Data\Response; use Fr\Typo3Handlebars\Data\Response\ProviderResponseInterface; class HeaderProviderResponse implements ProviderResponseInterface { public const LAYOUT_DEFAULT = 0; private string $header; private int $headerLayout = self::LAYOUT_DEFAULT; private string $headerLink = ''; private string $subheader = ''; public function __construct(string $header) { $this->header = $header; $this->validate(); } // Getters and setters... public function toArray(): array { return [ 'header' => $this->header, 'headerLayout' => $this->headerLayout, 'headerLink' => $this->headerLink, 'subheader' => $this->subheader, ]; } private function validate(): void { if ('' === trim($this->header)) { throw new \InvalidArgumentException('Header must not be empty.', 1626108393); } } }
Create a new
Presenter
To complete the rendering process, a new
Presenter
calledHeaderPresenter
must be created. It must implement theFr\Typo3Handlebars\Presenter\PresenterInterface
; furthermore, anFr\Typo3Handlebars\Presenter\AbstractPresenter
is already available with the defaultRenderer
already specified as a dependency.# Classes/Presenter/HeaderPresenter.php namespace Vendor\Extension\Presenter; use Fr\Typo3Handlebars\Data\Response\ProviderResponseInterface; use Fr\Typo3Handlebars\Exception\UnableToPresentException; use Fr\Typo3Handlebars\Presenter\AbstractPresenter; class HeaderPresenter extends AbstractPresenter { public function present(ProviderResponseInterface $data): string { if (!($data instanceof HeaderProviderResponse)) { throw new UnableToPresentException( 'Received unexpected response from provider.', 1613552315 ); } // Use data from ProviderResponse or implement custom logic $renderData = $data->toArray(); return $this->renderer->render( 'Extensions/FluidStyledContent/Header', $renderData ); } }
Set up TypoScript configuration
Finally, you have to configure via TypoScript that instead of the default Fluid rendering the special Handlebars rendering should be executed for all content elements of the CType
header
.For this purpose, each
DataProcessor
provides a methodprocess(string $content, array $configuration)
as entry point.# Configuration/TypoScript/setup.typoscript tt_content.header = USER tt_content.header.userFunc = Vendor\Extension\DataProcessing\HeaderProcessor->process
Optional: Create custom
Helpers
If your templates use custom
Helpers
, you will need to create them additionally. Read Create a custom Helper to learn what options are available for creating your ownHelpers
.Flush caches
Changes were made to the service configuration and also the rendering was overwritten using TypoScript. Therefore, it is now necessary to ensure that the caches are flushed and the service container is reconfigured.
Sources¶
See also
View the sources on GitHub: