Module 2: Timber & Twig

Separating Logic from Views.

The MVC Pattern

Stop writing PHP inside your HTML. Timber allows you to write clean, maintainable code by separating the Controller (PHP) from the View (Twig).

Key Concepts

1. Installing Timber

We use Composer to install the library:

composer require timber/timber

2. The Controller (PHP)

In your theme's `index.php` or `single.php`, you prepare the data:

$context = Timber::context();
$context['post'] = Timber::get_post();
$context['foo'] = 'bar';
Timber::render('single.twig', $context);

3. The View (Twig)

In `views/single.twig`, you display the data:

{% extends "base.twig" %}

{% block content %}
    <h1>{{ post.title }}</h1>
    <div>{{ post.content }}</div>
    <p>Custom data: {{ foo }}</p>
{% endblock %}

4. Template Inheritance

Define a `base.twig` with your header and footer, then extend it. This eliminates code duplication.

5. Custom Twig Filters

Add custom logic to your templates via `functions.php`:

add_filter('timber/twig', function($twig) {
    $twig->addFilter(new Twig\TwigFilter('my_filter', function($text) {
        return 'Modified: ' . $text;
    }));
    return $twig;
});

6. Integrating NocoDB Data

Fetch data from NocoDB in the controller and pass it to Twig:

$client = new \GuzzleHttp\Client();
$response = $client->get('https://noco.db/api/v1/db/data/v1/Project/Table');
$context['external_data'] = json_decode($response->getBody(), true);

7. Caching Context

Cache expensive data fetches using Transients before passing to context:

$context['menu'] = get_transient('main_menu') ?: Timber::get_menu('main');

8. Debugging Twig

Enable debug mode in `config/environments/development.php` to use `{{ dump(post) }}`.

9. Component Architecture

Break down your UI into small, reusable Twig files (e.g., `tease-post.twig`) and include them:

{% include 'tease-post.twig' with { post: item } %}

10. Security in Twig

Twig auto-escapes output by default, preventing XSS. Use `|raw` only when you trust the content source.