Template Loaders
Loaders are the mechanism Latte uses to retrieve the source code of your templates. Most commonly, templates are files stored on disk, but Latte's flexible loader system allows you to load them from virtually anywhere, or even generate them dynamically.
What is a Loader?
Typically, when you work with templates, you think of .latte
files residing in your project's directory
structure. This is handled by Latte's default FileLoader. However, the connection between a
template name (like 'main.latte'
or 'components/card.latte'
) and its actual source code content doesn't
have to be a direct file path mapping.
This is where loaders come in. A loader is an object responsible for taking a template name (an identifier string) and
providing Latte with its source code. Latte relies entirely on the configured loader for this task. This applies not only to the
initial template requested via $latte->render('main.latte')
but also to every template referenced within
using tags like {include ...}
, {layout ...}
, {embed ...}
, or {import ...}
.
Why use a custom loader?
- Loading from alternative sources: Fetch templates stored in a database, a cache (like Redis or Memcached), a version control system (like Git, based on a specific commit), or generated dynamically.
- Implementing custom naming conventions: You might want to use shorter aliases for templates or implement a specific search path logic (e.g., looking in a theme directory first, then fallback to a default directory).
- Adding security or access control: A custom loader could verify user permissions before loading certain templates.
- Preprocessing: While generally discouraged (compiler passes are better), a loader could theoretically preprocess template content before handing it to Latte.
You configure the loader for your Latte\Engine
instance using the setLoader()
method:
$latte = new Latte\Engine;
// Use the default FileLoader for files in '/path/to/templates'
$loader = new Latte\Loaders\FileLoader('/path/to/templates');
$latte->setLoader($loader);
A loader must implement the Latte\Loader
interface.
Built-in Loaders
Latte provides several standard loaders:
FileLoader
This is the default loader used by Latte\Engine
if no other loader is specified. It loads templates
directly from the filesystem.
You can optionally set a root directory to restrict access:
use Latte\Loaders\FileLoader;
// The following will only allow loading templates within /var/www/html/templates
$loader = new FileLoader('/var/www/html/templates');
$latte->setLoader($loader);
// $latte->render('../../../etc/passwd'); // This would throw an exception
// Render template located at /var/www/html/templates/pages/contact.latte
$latte->render('pages/contact.latte');
It resolves template names relative to the current template when using tags like {include}
or
{layout}
, unless an absolute path is given.
StringLoader
This loader retrieves template content from an associative array where keys are template names (identifiers) and values are the template source code strings. It's particularly useful for testing or small applications where templates might be stored within PHP code itself.
use Latte\Loaders\StringLoader;
$loader = new StringLoader([
'main.latte' => 'Hello {$name}, include is below:{include helper.latte}',
'helper.latte' => '{var $x = 10}Included content: {$x}',
// Add more templates as needed
]);
$latte->setLoader($loader);
$latte->render('main.latte', ['name' => 'World']);
// Outputs: Hello World, include is below:Included content: 10
If you only need to render a single template directly from a string without needing includes or inheritance referencing other
named string templates, you can pass the string directly to render()
or renderToString()
when using
StringLoader
without an array:
$loader = new StringLoader;
$latte->setLoader($loader);
$templateString = 'Hello {$name}!';
$output = $latte->renderToString($templateString, ['name' => 'Alice']);
// $output contains 'Hello Alice!'
Creating a Custom Loader
To create your own loader (e.g., to load templates from a database, cache, version control, or other source), you need to create a class that implements the Latte\Loader interface.
Let's look at what each method needs to do.
getContent (string $name): string
This is the core method of the loader. Its responsibility is to retrieve and return the full source code content for the
template identified by $name
(as passed to $latte->render()
or returned by getReferredName()).
If the template cannot be found or accessed, this method must throw a Latte\RuntimeException
.
public function getContent(string $name): string
{
// Example: Fetch from a hypothetical internal storage
$content = $this->storage->read($name);
if ($content === null) {
throw new Latte\RuntimeException("Template '$name' cannot be loaded.");
}
return $content;
}
getReferredName (string $name, string $referringName): string
This method handles the resolution of template names used within tags like {include}
, {layout}
, etc.
When Latte encounters, for example, {include 'partial.latte'}
inside main.latte
, it calls this method
with $name = 'partial.latte'
and $referringName = 'main.latte'
.
The method's job is to resolve $name
into a canonical identifier (e.g., an absolute path, a unique database key),
which will be used when calling other loader methods, based on the context provided by $referringName
.
public function getReferredName(string $name, string $referringName): string
{
return ...;
}
getUniqueId (string $name): string
Latte uses the compiled template cache for performance. Each compiled template file needs a unique name derived from the source
template's identifier. This method provides a string that uniquely identifies the template $name
.
For file-based templates, the absolute path might work. For database templates, a combination of a prefix and the database ID is common.
public function getUniqueId(string $name): string
{
return ...;
}
Example: Simple Database Loader
This example demonstrates the basic structure of a loader retrieving templates stored in a database table named
templates
with columns name
(unique identifier), content
, and updated_at
.
use Latte;
class DatabaseLoader implements Latte\Loader
{
public function __construct(
private \PDO $db,
) {
}
public function getContent(string $name): string
{
$stmt = $this->db->prepare('SELECT content FROM templates WHERE name = ?');
$stmt->execute([$name]);
$content = $stmt->fetchColumn();
if ($content === false) {
throw new Latte\RuntimeException("Template '$name' not found in database.");
}
return $content;
}
// This simple example assumes template names ('homepage', 'article', etc.)
// are unique IDs and templates don't reference each other relatively.
public function getReferredName(string $name, string $referringName): string
{
return $name;
}
public function getUniqueId(string $name): string
{
// Using a prefix and the name itself is unique and sufficient here
return 'db_' . $name;
}
}
// Usage:
$pdo = new \PDO(/* connection details */);
$loader = new DatabaseLoader($pdo);
$latte->setLoader($loader);
$latte->render('homepage'); // Loads template with name 'homepage' from DB
Custom loaders give you complete control over where your Latte templates come from, enabling integration with various storage systems and workflows.