5 + 1 Best practices for readable Fluid templates
As TYPO3 developers, we deal with Fluid templates on a daily basis. While Fluid is a powerful templating language, it sometimes has its pitfalls that can lead to complicated code. And complicated code can quickly lead to unmaintainable code, which is why we collected some best practices to improve the readability of your Fluid templates.
1. Define indentation guidelines (and stick to them)
This is a simple but crucial one:
- Decide between tabs and spaces.
- Define your indentation rules in an editorconfig file.
- Decide how you want to indent longer HTML and ViewHelper tags.
- To avoid unnecessary white space in your output, you can use <f:spaceless>.
<f:variable
name="homeLink"
value="{f:uri.page(
pageUid: '{f:security.ifAuthenticated(then: settings.page.home, else: settings.page.login)}',
absolute: 1
)}"
/>
2. Use layouts and sections
Reduce complexity and duplications in your template files by using separate layout files and named sections in your template file. This separates boilerplate layout code from the actual functionality your template file covers.
<!-- FeatureLayout.html -->
<div class="featureWrapper">
<div class="featureHeader">
<f:image image="{headerImage}" class="featureHeaderImage" />
<div class="featureHeaderContent">
<f:render section="Header" optional="1" />
</div>
</div>
<div class="featureBreadcrumb">
<f:render partial="Breadcrumb" arguments="{
breadcrumbItems: breadcrumbItems
}" />
</div>
<div class="featureContent">
<f:render section="Main" />
</div>
</div>
<!-- List.html -->
<f:layout name="FeatureLayout" />
<f:section name="Header">
<!-- Optional content for header -->
</f:section>
<f:section name="Main">
<f:for each="{myData}" as="myItem">
<!-- ... -->
</f:for>
</f:section>
3. Optimize inline notations
Fluid has quite a few tricks up its sleeve when it comes to inline notation. Here are a few examples:
{f:security.ifAuthenticated(then: '{settings.page.home}', else: '{settings.page.login}')}
<!-- string and brackets can be omitted for variables: -->
{f:security.ifAuthenticated(then: settings.page.home, else: settings.page.login)}
<f:count>{navigationItems}</f:count>
<!-- arrow notation can be used to pass variable content to a (supporting) ViewHelper: -->
{navigationItems -> f:count()}
<!-- This can even be nested: -->
{data -> f:format.json() -> f:format.htmlspecialchars()}
Even more tricks are listed in the Fluid syntax documentation.
4. Avoid escaping whenever possible
trish wrote a great blog post about escaping in Fluid which still holds true today. However, there are a few tricks that can help you to avoid it altogether:
<!-- Original with escaping -->
<f:link.page pageUid="123" title="{f:translate(
key: 'LinkTitle',
arguments: {
0: '{f:translate(key: \'User\')}'
}
)}">
Link
</f:link.page>
<!-- Alternative 1: Skip one nesting level with f:variable -->
<f:variable name="linkTitle"><f:translate key="MyTranslationLabel" arguments="{
0: '{f:translate(key: 'User')}'
}" /></f:variable>
<f:link.page pageUid="123" title="{linkTitle}">
Link
</f:link.page>
<!-- Alternative 2: Use simplified variable notation in arrays -->
<f:variable name="userLabel" value="{f:translate(key: 'User')}" />
<f:link.page pageUid="123" title="{f:translate(key: 'LinkTitle', arguments: { 0: userLabel })}">
Link
</f:link.page>
5. Don't pass {_all} to partials
This one might be counter-intuitive: Doesn't it make things easier if you have access to all variables in your partial?
During initial development, this might be the case. But if you want to extend or refactor your templates in the future, it is quite hard to re-use the partial if you don't know its required input data. And isn't that the reason why partials exist, to be able to reuse code?
So instead, define your variables explicitly. This also provides an opportunity to use other, more generic variable names in your partial.
<f:render partial="Navigation" arguments="{
navigationItems: breadcrumbNavigation
}" />
Bonus: Use components
This wouldn't be a sitegeist blog post about Fluid if we didn't mention Fluid Components. Years of experience in working with Fluid Components show that our fluid code (both in templates and in components) became much more maintainable. Clear separation between integration – in templates, partials and layouts, which usually just assemble a few components – and the components itself lead to extensive code reuse.
With Fluid Components, we performed major frontend refactorings, which previously would have required a relaunch, just by refactoring a bunch components while leaving their API intact. Deliberate component parameter naming makes templates much more human-readable. And the included data structures strip away complicated ViewHelper code.
In short, once you start using components, you get hooked quickly and don't want to go back. ;-)
Write code for humans
My final recommendation to you: Write your templates for humans, not for the Fluid parser. PHP and TYPO3 got huge performance boosts in the last years, which is why we shouldn't pay too much attention to micro optimizations in our code. It's more important that you and your colleagues will understand your intentions one year in the future. The parser doesn't care.