Templates

Crea interfacce belle e dinamiche con il motore di template di Flux

Sintassi Jinja-like: Flux usa una sintassi template ispirata a Jinja2/Twig, familiare e potente. I file template hanno estensione .flux.html.

Sintassi Base

I template Flux usano tre tipi di delimitatori:

{# Commenti - non appaiono nell'output #}

{{ variabile }}  {# Output di variabili #}

{% if condizione %}  {# Tag di controllo #}
    Contenuto condizionale
{% endif %}

Visualizzare Dati

Mostra variabili e espressioni nel template:

{# Variabile semplice #}
<h1>Benvenuto {{ name }}</h1>

{# Accesso a proprietà #}
<p>Email: {{ user.email }}</p>
<p>Città: {{ user.address.city }}</p>

{# Array/Liste #}
<p>Primo elemento: {{ items[0] }}</p>
<p>Ultimo: {{ items[-1] }}</p>

{# Chiamate a metodi #}
<p>Posts totali: {{ user.posts().count() }}</p>

{# Espressioni #}
<p>Totale: {{ price * quantity }}</p>
<p>Anno prossimo: {{ current_year + 1 }}</p>

{# Default value #}
<p>Nome: {{ username or "Ospite" }}</p>
<p>Bio: {{ user.bio|default("Nessuna biografia") }}</p>

Filtri

Trasforma l'output con filtri concatenabili:

{# Filtri testo #}
{{ name|upper }}           {# MARIO ROSSI #}
{{ name|lower }}           {# mario rossi #}
{{ name|title }}           {# Mario Rossi #}
{{ text|truncate(100) }}   {# Tronca a 100 caratteri #}
{{ slug|slugify }}         {# converte-in-slug #}

{# Filtri numerici #}
{{ price|round(2) }}       {# 19.99 #}
{{ number|abs }}           {# Valore assoluto #}
{{ 1234567|number_format }} {# 1,234,567 #}

{# Filtri date #}
{{ created_at|date("d/m/Y") }}     {# 25/12/2024 #}
{{ updated_at|date("F j, Y") }}    {# December 25, 2024 #}
{{ timestamp|time_ago }}           {# 2 ore fa #}

{# Filtri array #}
{{ users|length }}         {# Conta elementi #}
{{ items|first }}          {# Primo elemento #}
{{ items|last }}           {# Ultimo elemento #}
{{ names|join(", ") }}     {# Mario, Luigi, Peach #}
{{ users|json }}           {# Converte in JSON #}

{# Filtri sicurezza #}
{{ html_content|safe }}    {# Non escapa HTML #}
{{ user_input|escape }}    {# Escapa HTML #}
{{ url|url_encode }}       {# URL encoding #}

{# Concatenazione filtri #}
{{ "  hello world  "|trim|title|replace(" ", "-") }}  {# Hello-World #}

Strutture di Controllo

If/Else

{% if user %}
    <p>Ciao {{ user.name }}!</p>
{% endif %}

{% if user.is_admin %}
    <a href="/admin">Pannello Admin</a>
{% elif user.is_moderator %}
    <a href="/moderate">Modera</a>
{% else %}
    <p>Utente standard</p>
{% endif %}

{# Operatori logici #}
{% if user and user.is_active %}
    <p>Utente attivo</p>
{% endif %}

{% if not user or user.is_guest %}
    <a href="/login">Accedi</a>
{% endif %}

{# Operatore ternario inline #}
<p class="{{ 'active' if user.is_online else 'offline' }}">
    {{ user.name }}
</p>

Loops

{# Loop base #}
<ul>
{% for user in users %}
    <li>{{ user.name }}</li>
{% endfor %}
</ul>

{# Loop con else (se vuoto) #}
{% for post in posts %}
    <article>{{ post.title }}</article>
{% else %}
    <p>Nessun post trovato</p>
{% endfor %}

{# Variabili loop speciali #}
{% for item in items %}
    <div class="{% if loop.first %}first{% endif %} 
                {% if loop.last %}last{% endif %}
                {% if loop.index0 % 2 == 0 %}even{% else %}odd{% endif %}">
        
        {{ loop.index }} di {{ loop.length }}: {{ item }}
        
        {# loop.index: 1, 2, 3... #}
        {# loop.index0: 0, 1, 2... #}
        {# loop.revindex: 3, 2, 1... #}
        {# loop.first: true se primo #}
        {# loop.last: true se ultimo #}
        {# loop.length: totale elementi #}
    </div>
{% endfor %}

{# Loop su dizionari #}
{% for key, value in user_data.items() %}
    <dt>{{ key|title }}</dt>
    <dd>{{ value }}</dd>
{% endfor %}

{# Loop con condizioni #}
{% for user in users if user.is_active %}
    <li>{{ user.name }}</li>
{% endfor %}

Template Inheritance

Crea layout riutilizzabili con ereditarietà:

Layout Base

{# resources/views/layouts/app.flux.html #}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My App{% endblock %}</title>
    {% block styles %}
        <link rel="stylesheet" href="/css/app.css">
    {% endblock %}
</head>
<body>
    <nav>
        {% include "partials/navigation" %}
    </nav>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <footer>
        {% block footer %}
            <p>&copy; 2024 My App</p>
        {% endblock %}
    </footer>
    
    {% block scripts %}
        <script src="/js/app.js"></script>
    {% endblock %}
</body>
</html>

Estendere il Layout

{# resources/views/pages/home.flux.html #}
{% extends "layouts/app" %}

{% block title %}Home - {{ parent() }}{% endblock %}

{% block content %}
    <h1>Benvenuto!</h1>
    <p>Questa è la homepage</p>
{% endblock %}

{% block scripts %}
    {{ parent() }}  {# Mantiene scripts del parent #}
    <script src="/js/home.js"></script>
{% endblock %}

Include e Components

Include

{# Include semplice #}
{% include "partials/header" %}

{# Include con variabili #}
{% include "partials/user-card" with {"user": current_user} %}

{# Include condizionale #}
{% include "ads/banner" if not user.is_premium %}

{# Include con fallback #}
{% include "partials/sidebar" ignore missing %}

Components

{# Definisci component #}
{# resources/views/components/alert.flux.html #}
<div class="alert alert-{{ type|default('info') }}">
    {% if title %}
        <h4>{{ title }}</h4>
    {% endif %}
    {{ slot }}
</div>

{# Usa component #}
{% component "alert" type="success" title="Ottimo!" %}
    Operazione completata con successo.
{% endcomponent %}

{# Component con slot multipli #}
{# components/card.flux.html #}
<div class="card">
    <div class="card-header">
        {{ header }}
    </div>
    <div class="card-body">
        {{ slot }}
    </div>
    {% if footer %}
        <div class="card-footer">
            {{ footer }}
        </div>
    {% endif %}
</div>

{# Uso #}
{% component "card" %}
    {% slot "header" %}
        <h3>Titolo Card</h3>
    {% endslot %}
    
    Contenuto principale della card
    
    {% slot "footer" %}
        <button>Azione</button>
    {% endslot %}
{% endcomponent %}

Forms

Helper per creare form facilmente:

{# Form base #}
<form method="POST" action="{{ route('users.store') }}">
    {{ csrf_field() }}
    
    {# Input text #}
    <input type="text" 
           name="name" 
           value="{{ old('name', user.name) }}"
           class="{{ 'error' if errors.name }}">
    {% if errors.name %}
        <span class="error">{{ errors.name[0] }}</span>
    {% endif %}
    
    {# Select #}
    <select name="role">
        {% for role in roles %}
            <option value="{{ role.id }}" 
                    {{ 'selected' if role.id == user.role_id }}>
                {{ role.name }}
            </option>
        {% endfor %}
    </select>
    
    {# Checkbox #}
    <input type="checkbox" 
           name="active" 
           value="1"
           {{ 'checked' if user.active }}>
    
    {# Radio buttons #}
    {% for option in ['basic', 'pro', 'enterprise'] %}
        <input type="radio" 
               name="plan" 
               value="{{ option }}"
               {{ 'checked' if user.plan == option }}>
        {{ option|title }}
    {% endfor %}
    
    <button type="submit">Salva</button>
</form>

{# Form helpers #}
{{ form_open('users.store', method='POST', files=true) }}
    {{ form_text('name', old('name'), class='form-control') }}
    {{ form_email('email', required=true) }}
    {{ form_select('role_id', roles, user.role_id) }}
    {{ form_textarea('bio', old('bio'), rows=5) }}
    {{ form_submit('Salva', class='btn btn-primary') }}
{{ form_close() }}

Direttive Personalizzate

Crea direttive riutilizzabili:

# app/directives.flux

# Direttiva auth
directive auth():
    if not request.user:
        return ""
    return get_slot()

# Direttiva con parametri  
directive can(action, model):
    if request.user.can(action, model):
        return get_slot()
    return ""

# Direttiva role
directive role(*roles):
    if request.user and request.user.role in roles:
        return get_slot()
    return ""
{# Uso delle direttive #}
{% auth %}
    <p>Benvenuto {{ user.name }}!</p>
    <a href="/logout">Logout</a>
{% endauth %}

{% can "edit", post %}
    <a href="{{ route('posts.edit', post.id) }}">Modifica</a>
{% endcan %}

{% role "admin", "editor" %}
    <a href="/admin">Admin Panel</a>
{% endrole %}

Assets e URL

Helper per gestire asset e generare URL:

{# Asset URLs #}
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<script src="{{ asset('js/app.js') }}"></script>
<img src="{{ asset('images/logo.png') }}" alt="Logo">

{# Versioning automatico #}
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
{# Output: /css/app.css?id=8e5a2b4c #}

{# URL generation #}
<a href="{{ url('/about') }}">About</a>
<a href="{{ route('users.show', user.id) }}">Profilo</a>
<a href="{{ route('posts.edit', {'id': post.id, 'lang': 'it'}) }}">
    Modifica
</a>

{# URL sicuri #}
<form action="{{ secure_url('/payment') }}">
<a href="{{ secure_route('checkout') }}">Checkout Sicuro</a>

{# Asset condizionali #}
{% if app.environment == 'production' %}
    <script src="{{ asset('js/analytics.js') }}"></script>
{% endif %}

Localizzazione

Supporto multilingua integrato:

{# Traduzioni semplici #}
<h1>{{ __('welcome.title') }}</h1>
<p>{{ __('welcome.message', {'name': user.name}) }}</p>

{# Pluralizzazione #}
<p>{{ __choice('posts.count', post_count, {'count': post_count}) }}</p>
{# 0: Nessun post | 1: Un post | 2+: :count posts #}

{# Traduzioni con fallback #}
<p>{{ __('custom.message') or 'Default message' }}</p>

{# Switch lingua #}
{% for locale in ['it', 'en', 'es'] %}
    <a href="{{ route('locale.switch', locale) }}" 
       class="{{ 'active' if app.locale == locale }}">
        {{ locale|upper }}
    </a>
{% endfor %}

{# Date localizzate #}
<time>{{ post.created_at|date_locale('full') }}</time>
{# Output: venerdì 25 dicembre 2024 #}

Cache nei Template

Ottimizza le performance con cache fragment:

{# Cache fragment #}
{% cache "sidebar", minutes=10 %}
    {# Contenuto costoso da generare #}
    {% for category in get_all_categories() %}
        <li>{{ category.name }} ({{ category.posts_count }})</li>
    {% endfor %}
{% endcache %}

{# Cache con chiave dinamica #}
{% cache "user-widget-" ~ user.id, minutes=5 %}
    <div class="user-stats">
        Posts: {{ user.posts().count() }}
        Comments: {{ user.comments().count() }}
    </div>
{% endcache %}

{# Cache condizionale #}
{% cache "homepage", minutes=30, if=not user %}
    {# Cache solo per utenti non loggati #}
{% endcache %}

{# Cache con tag #}
{% cache "products", tags=["products", "catalog"] %}
    {{ render_product_list() }}
{% endcache %}

Debug e Testing

Strumenti per debug durante lo sviluppo:

{# Dump variabili #}
{{ dump(user) }}
{{ dd(request.all()) }}  {# Dump and die #}

{# Debug info #}
{% if app.debug %}
    <div class="debug">
        Query: {{ db.query_count() }}
        Time: {{ execution_time() }}ms
        Memory: {{ memory_usage() }}MB
    </div>
{% endif %}

{# Commenti condizionali #}
{% comment %}
    Questo è un commento lungo
    su più righe che non verrà
    mai renderizzato
{% endcomment %}

{# Mostra solo in development #}
{% if app.environment == 'local' %}
    <div class="dev-tools">
        <!-- Developer tools -->
    </div>
{% endif %}

Best Practices

📁 Organizzazione

  • Usa layout per struttura comune
  • Dividi in partials riutilizzabili
  • Componenti per UI elements
  • Separa logica dal template

🚀 Performance

  • Cache fragment costosi
  • Eager load relazioni
  • Minimizza query nei template
  • Usa CDN per asset statici

🔒 Sicurezza

  • Escapa sempre l'output utente
  • Usa CSRF token nei form
  • Non esporre dati sensibili
  • Valida permessi nel template

Esempio Completo: Blog Post

{# views/blog/show.flux.html #}
{% extends "layouts/blog" %}

{% block title %}{{ post.title }} - {{ parent() }}{% endblock %}

{% block meta %}
    <meta name="description" content="{{ post.excerpt|escape }}">
    <meta property="og:title" content="{{ post.title }}">
    <meta property="og:image" content="{{ asset(post.featured_image) }}">
{% endblock %}

{% block content %}
    <article class="blog-post">
        <header>
            <h1>{{ post.title }}</h1>
            <div class="meta">
                <span>{{ post.author.name }}</span>
                <time>{{ post.published_at|date("d M Y") }}</time>
                <span>{{ post.read_time }} min lettura</span>
            </div>
        </header>
        
        {% if post.featured_image %}
            <img src="{{ asset(post.featured_image) }}" 
                 alt="{{ post.title }}"
                 class="featured-image">
        {% endif %}
        
        <div class="content">
            {{ post.content|safe }}
        </div>
        
        <footer>
            <div class="tags">
                {% for tag in post.tags %}
                    <a href="{{ route('blog.tag', tag.slug) }}" 
                       class="tag">#{{ tag.name }}</a>
                {% endfor %}
            </div>
            
            {% can "update", post %}
                <a href="{{ route('posts.edit', post.id) }}" 
                   class="btn btn-edit">Modifica</a>
            {% endcan %}
        </footer>
    </article>
    
    {% include "blog/partials/share-buttons" with {
        'url': route('blog.show', post.slug),
        'title': post.title
    } %}
    
    {% component "comments/section" post=post %}
{% endblock %}

{% block scripts %}
    {{ parent() }}
    <script src="{{ asset('js/highlight.js') }}"></script>
{% endblock %}
🎨 Ben fatto! Ora sai creare template dinamici e belli. Esplora le Funzioni Built-in per scoprire tutti gli helper disponibili.