whoami
Lucas Jenß
cat /etc/motd
The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- drwxr-xr-x
- ▶ Mac OS X
- ▶ Unix
Custom TYPO3 data processors for Fluid variables
Fluid keeps rendering surprisingly separate from the data behind it. A template receives variables such as {page}, {data}, {settings}, and anything supplied by a controller or data processor. When a project needs a label, visibility rule, or derived value, placing the logic directly in Fluid makes the template harder to test and often harder to reuse.
A custom TYPO3 data processor gives that transformation a clear home. It receives the current variable set, performs a focused change, and returns values that Fluid can consume. This approach suits repeated content elements, editorial notices, and calculated labels. The important distinction is that a processor does not rewrite every Fluid view variable: it contributes selected keys, and the shape of those keys becomes part of your template contract.
Where Fluid variables come from
During rendering, Fluid builds its variables from the page, content object, TypoScript, and any configured data processors. The standard {data} variable often represents the current record, while {page} exposes page-level information. A processor sits between those sources and the template, receiving the variables available at that point. That position makes it useful for enriching a record without coupling the transformation to markup.
The returned array is the processor’s real output. A key such as siteNotice becomes {siteNotice} in Fluid, and nested keys become dot paths such as {siteNotice.heading}. Returning ['siteNotice' => [...]] does not change {data} or {page}; it creates or replaces only its own namespace. If several processors produce the same key, their order matters, so use distinct names unless later processing is intentional.
Context can also drive the transformation. For a notice published for a Sydney or Melbourne office, local time may determine the label. Those cities use AEST or AEDT depending on daylight saving, while Brisbane remains on UTC+10 year-round. Keeping that rule in PHP avoids scattering date checks through templates. An Australian agency may also maintain a TYPO3 site beside a WordPress property; resources for Elementor theme options belong to that separate platform and should not become Fluid variables.
Designing a safe variable contract
Start with the smallest shape the template actually needs. For example, a site notice might need heading, summary, and enabled. Put processor-specific options under a siteNotice key so they remain separate from values another extension might pass. This is especially helpful in Australian development teams, where “organisation” and “initialise” may appear in content while a technical key still follows the project’s chosen convention.
Decide whether the processor creates a new variable or returns a modified copy of an inherited one. Creating {siteNotice} is usually safer than replacing {data}, because {data} may already contain fields needed by other Fluid sections. If a derived page label really belongs to {data}, copy the current array, change the one field, and return the whole copy under data. To change {siteNotice.heading}, likewise return the complete siteNotice array.
Keep configuration values separate from runtime variables. processorConfiguration carries options from your rendering setup; contentObjectVariables carries values already available to the template. Escape text in Fluid, validate allowed audiences, and parse booleans deliberately, since an option such as 0 or false arrives as configuration text rather than a native boolean. Australian quotes often separate GST from the AUD amount, so an enrichment service also needs a clear budget owner before it becomes a render dependency.
Implementing the processor
Put the class in a Composer-autoloaded extension and implement TYPO3’s DataProcessorInterface. The example uses the three-argument getData contract used by modern TYPO3 releases; check the interface in your installed version when maintaining an older site.
<?php
declare(strict_types=1);
namespace Vendor\Site\DataProcessing;
use TYPO3\CMS\Core\DataProcessing\DataProcessorInterface;
final class SiteNoticeProcessor implements DataProcessorInterface
{
public function getData(
array $contentObjectConfiguration,
array $contentObjectVariables,
array $processorConfiguration
): array {
$options = $processorConfiguration['siteNotice'] ?? [];
$options = is_array($options) ? $options : [];
$existing = $contentObjectVariables['siteNotice'] ?? [];
$existing = is_array($existing) ? $existing : [];
$heading = trim((string)($options['heading'] ?? ($existing['heading'] ?? '')));
$summary = trim((string)($options['summary'] ?? ($existing['summary'] ?? '')));
$enabled = filter_var(
$options['enabled'] ?? ($existing['enabled'] ?? false),
FILTER_VALIDATE_BOOLEAN
);
return [
'siteNotice' => array_replace($existing, [
'heading' => $heading,
'summary' => $summary,
'enabled' => $enabled,
]),
];
}
}
The second argument is the current Fluid variable set. Reading siteNotice from it lets the processor preserve keys supplied by an earlier processor. The array_replace call then applies only the notice’s own fields, leaving unrelated keys within that namespace intact. The first argument describes the content object and is available when the transformation needs more context. If you add constructor dependencies later, expose the class as a public service in the extension’s service configuration.
The class has no template-specific markup or direct database access, which makes it straightforward to unit test. When something fails, confirm the actual boundary before changing the implementation; handling native library load failures demonstrates that discipline in an Android NDK context. For a Fluid processor, inspect the returned array first, then the configuration and rendered template.
Three approaches suit different kinds of change:
| Approach | What Fluid receives | Effect on other variables | Good fit |
|---|---|---|---|
A new {siteNotice} namespace |
A new or replaced notice array | {data} and {page} stay unchanged |
Shared notices, flags and computed labels |
A copied {data} array |
The complete record array with your changed field | Other namespaces stay unchanged | A deliberate change to a record field |
| Inline template logic | Only the edited output | No reusable processing state | A genuinely one-off presentation change |
Connecting it to Fluid
Attach the class through the dataProcessing configuration of the Fluid content object. The numeric key establishes processing order; the nested values become processorConfiguration for the class.
page = PAGE
page.10 = FLUID
page.10 {
file = EXT:site_package/Resources/Private/Templates/Page.html
dataProcessing {
10 = Vendor\Site\DataProcessing\SiteNoticeProcessor
10.siteNotice {
heading = Latest site update
summary = Check the maintenance window
enabled = 1
}
}
}
The template can then use the result without any PHP calls. Keep conditionals close to the data they protect, and avoid assuming that a nested key exists when a processor might not have run.
<f:if condition="{siteNotice.enabled}">
<h2>{siteNotice.heading}</h2>
<p>{siteNotice.summary}</p>
</f:if>
Configure the processor where the page template is rendered, not in every component that displays the notice. That centralises default text and access rules and gives you one place to apply caching or a timeout. The separation of work from waiting described in chaining non-blocking Akka queries comes from Scala rather than PHP, but it remains a useful design reference: never make a visitor wait for an unbounded upstream call.
Testing and troubleshooting
Start with the output, not the markup. Log the processor’s returned array in a development environment, then check the namespace Fluid receives. A missing key often means the processor was never configured, the class was not autoloaded, or a later processor replaced the namespace. For boolean options, test 0, 1, true, false and an absent value; PHP casts the non-empty string "false" to true unless parsing is explicit.
The same discipline applies when a runtime dependency fails: confirm the failing boundary before changing the implementation. For Fluid, the equivalent boundary is configuration, PHP processing and final template output, in that order. This prevents a template workaround from hiding a processor that never ran or returned an unexpected shape.
If enrichment depends on a slow service, keep that work out of the render path where possible. Fetch upstream data before rendering, apply a timeout, and cache a stable result rather than making every visitor wait. A processor that changes only local values can usually run during each request, but a processor that depends on remote data deserves a more deliberate caching strategy.
Test the processor with unit cases for defaults, overrides and unexpected input, then confirm the rendered notice in a real template. In a casual Australian stand-up, “no worries” can close a discussion without settling every edge case, so acceptance criteria still need explicit examples. Australian public-sector procurements often specify WCAG 2.2 AA, making heading order, link purpose and contrast part of acceptance rather than final polish.
Create a branch for the feature, add the processor to a site extension, and connect one dataProcessing entry before touching the template. Return a single, documented namespace, cover it with tests for defaults and overrides, and render it once in the real page context. That narrow slice gives editorial staff a predictable notice variable while keeping the PHP logic reusable for future content elements. Implement that path in your next release.
cat ~/interests.json
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
Solving SVG import issues in Adobe Illustrator CS6 and CC
When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
Validating IP-Addresses in PHP
Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt