Latte templates#
This chapter is about the PHP side of templates: what the system has added to Latte, how to write your own filter, and how snippets and signals hold together.
What belongs where
How a template is split into files, how the system picks a layout and what flows into a template from the presenter — that is in the section for front-end coders: Splitting a template into Latte files. Here you will find only what a developer adding something to templates from PHP needs.
Custom extensions#
app/Core/Latte/Extensions/ — nine extensions, each adding filters, functions or tests:
| Extension | What it adds |
|---|---|
Url |
working with addresses |
Date |
date and time formatting |
Price |
price formatting by currency |
Email, Phone |
contact formatting |
String |
text adjustments (e.g. replacing blacklisted words) |
FormPair |
rendering a label–field pair |
SafeLink |
the safeLink() function — a link that does not fail on a missing target |
Theme |
themeAsset(), themeUrl(), the isTheme() test |
Plus three macros in app/Core/Latte/Macros/: MultiControlMacros, SafeLinkMacro and
ThemeMacro.
Procedure: adding my own filter#
- Find out whether it belongs in an existing extension. Price formatting into
PriceLatteExtension, text handling intoStringLatteExtension. - If not, create a new extension in
app/Core/Latte/Extensions/,extends Latte\Extension, and implementgetFilters()/getFunctions()/getTests(). - Write nothing into the configuration. The extension is found by itself:
LatteExtensionsCompilerwalks theExtensions/andMacros/folders with the robot loader, registers every instantiable class extendingLatte\Extensionand attaches it toLatteFactory. - Put the logic in a helper, not in the closure's body — a filter should be a one-line redirect.
- Verify it in both sections if the filter is meant to work on the front end and in the administration.
Formatting belongs in a filter, not in a template
A currency condition in the middle of a template will appear in fifteen places within a year — and be forgotten in fourteen of them when it is fixed.
A new extension only takes effect after the container cache is cleared
Registration runs while the DI container is compiled. Until temp/cache is deleted the
filter does not exist and Latte reports an unknown filter — which looks like a typo in
the name.
Snippets and their PHP counterpart#
A snippet's name is also the argument to redrawControl(). The convention: a short
lowerCamelCase name, Wrapper shortened to Wrap, and the wrapping area named after the
inner snippet plus Area.
A snippet's name must be a LITERAL
A name composed from a variable is not registered and redrawControl() then silently
does nothing. No error is printed — the page simply is not redrawn.
AJAX redraws only the INSIDE of a snippet
The attributes of the element carrying the snippet stay stale — the class and any
data- attribute alike. When a redraw is supposed to change a state expressed by a
class, that class has to be inside.
{var} inside a {snippet} has its own scope
A variable defined outside the snippet may not exist inside it after a redraw — during an AJAX redraw only that snippet is rendered, not the template around it.
Redrawing blindly is not the way
redrawControl() without an argument redraws everything. Always name the target —
otherwise snippets that changed for an unrelated reason end up in the response too.
In detail: Components and Code conventions.
Translations in a template#
Three different behaviours depending on where you are — a Front presenter needs
{translator $translatorDomain}, an Admin presenter and components do not. It is the most
common source of "why did it not translate" and has a chapter of its own:
Translations.
A missing translation in a route can bring down a whole language version
Not an empty space — a page error. It concerns the texts an address is composed from, not labels. In detail: Translations, the section on the database layer.
Reserved variable names#
Some names are taken by the template system
$form, $flashes, $control, $presenter, $lang, $locale, $theme and others.
Overwriting one surfaces not as an error but as strange behaviour — typically as
something entirely different breaking on the same page.
Debug mode and AJAX#
A deprecated call in debug mode breaks the AJAX response
Tracy adds output to the response that breaks the JSON. It surfaces as "AJAX does not work" — with no warning and no console error, because the response simply cannot be parsed.
Where to look#
| I want | Where |
|---|---|
| the extensions and their filters | app/Core/Latte/Extensions/ |
| the macros | app/Core/Latte/Macros/ |
| the automatic registration | app/Core/DI/LatteExtensionsCompiler.php |
| the engine settings | config/Shared/latte.neon |
| the structure of templates and layouts | Splitting a template into Latte files |
Follow-up chapters: Components · Forms · Translations · Front-end themes