Migration 

This page lists required migration steps when upgrading to a new major version of the extension.

Version 1.0.0 

Version 1.0.0 replaces the previous PHP-class rendering model (DataProcessor / DataProvider / Presenter) with the HANDLEBARSTEMPLATE content object. All rendering configuration moves to TypoScript; custom PHP classes are no longer the entry point.

Removed classes and interfaces 

The following classes and interfaces have been removed and have no replacement:

  • \CPSIT\Typo3Handlebars\DataProcessing\DataProcessor (interface)
  • \CPSIT\Typo3Handlebars\DataProcessing\AbstractDataProcessor
  • \CPSIT\Typo3Handlebars\Data\DataProvider (interface)
  • \CPSIT\Typo3Handlebars\Data\Response\ProviderResponse (interface)
  • \CPSIT\Typo3Handlebars\Presenter\Presenter (interface)
  • \CPSIT\Typo3Handlebars\Presenter\AbstractPresenter
  • \CPSIT\Typo3Handlebars\DataProcessing\SimpleProcessor

The handlebars.processor service tag has also been removed.

TypoScript entry point 

Before: Each content element was routed to a DataProcessor class via a USER content object:

tt_content.header = USER
tt_content.header.userFunc = Vendor\Extension\DataProcessing\HeaderProcessor->process
Copied!

After: Use HANDLEBARSTEMPLATE directly:

tt_content.header = HANDLEBARSTEMPLATE
tt_content.header {
    templateName = Header

    variables {
        header = TEXT
        header.field = header

        subheader = TEXT
        subheader.field = subheader
    }
}
Copied!

Data preparation (DataProvider → variables / dataProcessing) 

Before: Data was prepared in a DataProvider class and returned as a ProviderResponse object, which the Presenter then passed to the renderer.

After: Data is prepared entirely in TypoScript:

  • Simple field values: use variables with content objects such as TEXT, FILES, etc.
  • Database relations and menus: use standard TYPO3 dataProcessing processors (e.g., database-query, menu).
  • Per-record variable processing inside a loop: use process-variables.

Template selection (Presenter → templateName) 

Before: The Presenter called $this->renderer->render('path/to/template', $data).

After: The template is declared in TypoScript:

tt_content.my_element {
    templateName = MyElement
}
Copied!

For conditional template selection, use stdWrap on templateName:

tt_content.my_element {
    templateName = MyElement
    templateName.override.if {
        isTrue.field = tx_myext_variant
        value = special
    }
    templateName.override = MyElementSpecial
}
Copied!

Helper registration 

Before: Helpers were registered via Services.yaml tags:

Configuration/Services.yaml
services:
  Vendor\Extension\Renderer\Helper\GreetHelper:
    tags:
      - name: handlebars.helper
        identifier: 'greet'
        method: 'greetById'
Copied!

After: Use the #[AsHelper] attribute directly on the class or method:

EXT:my_extension/Classes/Renderer/Helper/GreetHelper.php
use CPSIT\Typo3Handlebars\Attribute\AsHelper;
use CPSIT\Typo3Handlebars\Renderer\Helper\Helper;
use DevTheorem\Handlebars\HelperOptions;

#[AsHelper('greet')]
final readonly class GreetHelper implements Helper
{
    public function render(HelperOptions $options): string { /* ... */ }
}
Copied!

The Services.yaml tag approach still works and can be used if you cannot modify the helper class (e.g., a third-party class).

Template path configuration 

Template path configuration via Services.yaml and TypoScript remains unchanged. In addition, paths can now also be set per-content-object directly in HANDLEBARSTEMPLATE:

tt_content.textmedia = HANDLEBARSTEMPLATE
tt_content.textmedia {
    templateRootPaths.10 = EXT:my_extension/Resources/Private/Templates
    partialRootPaths.10 = EXT:my_extension/Resources/Private/Partials
}
Copied!