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>© 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.