Skip to content

Twig Integration

Phexium uses Twig for HTML templating. Templates render ViewModel data with no business logic.

Template Structure

Templates extend a shared layout:

{% extends 'layout.html.twig' %}

{% block title %}Books - CleanShelf{% endblock %}

{% block content %}
<h1>Books</h1>
{% for book in books %}
    <p>{{ book.title }} by {{ book.author }}</p>
{% endfor %}
{% endblock %}

Twig Extensions

The demo application provides three Twig extensions for common template needs.

ConnectedUserExtension

Exposes authenticated user information as a global connected_user variable:

{% if connected_user.is_authenticated %}
    <span>Welcome, {{ connected_user.email }}</span>
{% endif %}

Available properties:

  • is_authenticated - Whether a user is logged in
  • email - Current user's email address

The extension uses lazy loading: user data is fetched from the session only when accessed in the template.

FlashExtension

Provides the flash global variable for session-based notifications:

{% for type, messages in flash.all %}
    {% for message in messages %}
    <div class="alert alert-{{ type }}">{{ message }}</div>
    {% endfor %}
{% endfor %}

Flash messages are typically set in controllers after successful operations:

$this->sessionService->addFlashMessage('success', 'Book created successfully');

PermissionExtension

Adds the can() function for permission-based rendering:

{% if can('book.delete') %}
<button class="btn btn-danger">Delete</button>
{% endif %}

{% if can('loan.view_all') %}
    {# Admin-only content #}
{% endif %}

The function delegates to the current user's context to check RBAC permissions.

Source Files

  • app/demo/Shared/Presentation/Twig/ConnectedUserExtension.php
  • app/demo/Shared/Presentation/Twig/ConnectedUser.php
  • app/demo/Shared/Presentation/Twig/FlashExtension.php
  • app/demo/Shared/Presentation/Twig/PermissionExtension.php

Forms

Forms display values and validation errors:

<input type="text"
       name="title"
       value="{{ formValues.title|default('') }}"
       class="{% if errors.title %}is-invalid{% endif %}">

{% if errors.title %}
<div class="invalid-feedback">{{ errors.title }}</div>
{% endif %}

Best Practices

  • Extend shared layout for consistency
  • Use blocks for customizable sections
  • Keep logic minimal—use presenters for formatting
  • Include partials for reusable components
  • Auto-escaping is enabled (XSS protection)

See Also