Table of Contents
- Motor de Plantillas Template Engine
- Descripción General
- Inicialización
- Renderización de Plantillas
- Sintaxis del Motor de Plantillas
- Estructuras de Control
- Bucles
- Herencia de Plantillas
- Inclusión de Archivos
- Orden de Procesamiento
- Ejemplo Completo de Uso
- Características de Seguridad
- Manejo de Errores
- Buenas Prácticas
- Limitaciones Actuales
- Resumen de Sintaxis
Motor de Plantillas Template Engine
Descripción General
El Template Engine es un motor de plantillas personalizado implementado con el patrón Singleton que permite crear vistas dinámicas con soporte para herencia de plantillas, inclusión de archivos, variables, condicionales y bucles.
Inicialización
Configuración Inicial
El motor debe ser inicializado una única vez proporcionando la ruta base donde se encuentran las plantillas:
use Conexvirt\View\Template;
// Primera inicialización (requerida)
$template = Template::getInstance('/path/to/views');
// Llamadas posteriores (sin parámetro)
$template = Template::getInstance();
Nota: Si no se proporciona la ruta base en la primera llamada, se lanzará una excepción.
Renderización de Plantillas
Método render()
Renderiza una plantilla procesando todas las directivas y retorna el contenido HTML final:
// Renderizar plantilla sin datos
$html = $template->render('home');
// Renderizar con datos
$html = $template->render('users/profile', [
'username' => 'Roberto',
'email' => 'roberto@example.com'
]);
Método assign()
Asigna datos globales que estarán disponibles en todas las plantillas:
$template->assign([
'siteName' => 'Mi Aplicación',
'year' => 2024
]);
Sintaxis del Motor de Plantillas
1. Variables {{ }}
Las variables se escriben entre llaves dobles y son escapadas automáticamente con htmlspecialchars():
{{ username }}
{{ email }}
{{ siteName }}
Ejemplo:
<h1>Bienvenido {{ username }}</h1>
<p>Tu correo es: {{ email }}</p>
2. Helper old()
Recupera valores previos de formularios (útil tras validaciones):
{{ old('email') }}
{{ old('nombre', 'Valor por defecto') }}
Ejemplo:
<input type="text" name="email" value="{{ old('email') }}">
<input type="text" name="nombre" value="{{ old('nombre', 'Anónimo') }}">
3. Helper errors()
Obtiene mensajes de error de validación:
{{ errors()['email'][0] }}
{{ errors()['password'][1] }}
Ejemplo:
<span class="error">{{ errors()['email'][0] }}</span>
4. Helper asset()
Genera rutas de recursos estáticos:
{{ asset('css/style.css') }}
{{ asset('images/logo.png') }}
Ejemplo:
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
<img src="{{ asset('images/logo.png') }}" alt="Logo">
Estructuras de Control
Condicionales {% if %}
Evalúa condiciones con funciones PHP como empty(), !empty(), isset(), !isset():
{% if !empty(username) %}
<p>Hola {{ username }}</p>
{% endif %}
{% if empty(errors()['email']) %}
<p class="success">Email válido</p>
{% endif %}
{% if isset(isAdmin) %}
<a href="/admin">Panel Admin</a>
{% endif %}
Funciones soportadas:
empty(variable)- Verifica si está vacío!empty(variable)- Verifica si NO está vacíoisset(variable)- Verifica si existe!isset(variable)- Verifica si NO existe
Ejemplo completo:
{% if !empty(errors()['email']) %}
<div class="alert alert-danger">
{{ errors()['email'][0] }}
</div>
{% endif %}
{% if !empty(old('remember')) %}
<input type="checkbox" name="remember" checked>
{% endif %}
Bucles
Estructura {% foreach %}
Itera sobre arrays de datos:
{% foreach users as user %}
<li>{{ user }}</li>
{% endforeach %}
Ejemplo completo:
<ul>
{% foreach productos as producto %}
<li>{{ producto }}</li>
{% endforeach %}
</ul>
<div class="usuarios">
{% foreach nombres as nombre %}
<span class="badge">{{ nombre }}</span>
{% endforeach %}
</div>
Renderización:
$html = $template->render('lista', [
'productos' => ['Laptop', 'Mouse', 'Teclado'],
'nombres' => ['Ana', 'Juan', 'María']
]);
Herencia de Plantillas
Directiva {% extends %}
Permite que una plantilla herede de otra plantilla base:
Archivo: layouts/base.php
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<header>{{ header }}</header>
<main>
{{ content }}
</main>
<footer>{{ footer }}</footer>
</body>
</html>
Archivo: home.php
{% extends 'layouts/base' %}
{% section 'header' %}
<h1>Mi Sitio Web</h1>
{% endsection %}
{% section 'content' %}
<p>Bienvenido {{ username }}</p>
{% endsection %}
{% section 'footer' %}
<p>© 2024 - Todos los derechos reservados</p>
{% endsection %}
Uso:
$html = $template->render('home', [
'title' => 'Inicio',
'username' => 'Roberto'
]);
Inclusión de Archivos
Directiva {% include %}
Incluye el contenido de otra plantilla dentro de la actual:
Archivo: partials/menu.php
<nav>
<a href="/">Inicio</a>
<a href="/about">Acerca de</a>
<a href="/contact">Contacto</a>
</nav>
Archivo: page.php
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{% include 'partials/menu' %}
<main>
{{ content }}
</main>
{% include 'partials/footer' %}
</body>
</html>
Nota: Los includes soportan recursividad, es decir, un archivo incluido puede incluir otros archivos.
Orden de Procesamiento
El motor procesa las directivas en el siguiente orden:
- Extends - Herencia de plantillas
- Includes - Inclusión de archivos
- Variables - Reemplazo de
{{ var }} - Condicionales - Procesamiento de
{% if %} - Bucles - Procesamiento de
{% foreach %}
Ejemplo Completo de Uso
Estructura de Archivos
views/
├── layouts/
│ └── app.php
├── partials/
│ ├── header.php
│ └── footer.php
└── users/
└── profile.php
Archivo: layouts/app.php
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>{{ pageTitle }}</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
{% include 'partials/header' %}
<main class="container">
{{ content }}
</main>
{% include 'partials/footer' %}
</body>
</html>
Archivo: users/profile.php
{% extends 'layouts/app' %}
{% section 'content' %}
<div class="profile">
<h1>Perfil de {{ username }}</h1>
{% if !empty(bio) %}
<p>{{ bio }}</p>
{% endif %}
<h3>Lenguajes de Programación:</h3>
<ul>
{% foreach languages as language %}
<li>{{ language }}</li>
{% endforeach %}
</ul>
{% if !empty(errors()['email']) %}
<div class="alert">{{ errors()['email'][0] }}</div>
{% endif %}
</div>
{% endsection %}
Renderización en PHP
use Conexvirt\View\Template;
// Inicializar motor
$template = Template::getInstance(__DIR__ . '/views');
// Asignar datos globales
$template->assign([
'siteName' => 'Mi Aplicación'
]);
// Renderizar vista
$html = $template->render('users/profile', [
'pageTitle' => 'Perfil de Usuario',
'username' => 'Roberto Calel',
'bio' => 'Desarrollador Full Stack',
'languages' => ['PHP', 'JavaScript', 'Python']
]);
echo $html;
Características de Seguridad
Escapado Automático
Todas las variables son escapadas automáticamente con htmlspecialchars() para prevenir ataques XSS:
// Si username contiene: <script>alert('XSS')</script>
{{ username }}
// Salida: <script>alert('XSS')</script>
Valores por Defecto
Si una variable no existe, el motor retorna una cadena vacía en lugar de generar un error:
{{ variableInexistente }}
// Salida: (cadena vacía)
Manejo de Errores
Plantilla No Encontrada
// Lanza Exception: Template not found: /path/to/views/inexistente.php
$html = $template->render('inexistente');
Include No Encontrado
{% include 'archivo/inexistente' %}
<!-- Salida: <!-- Include not found: archivo/inexistente --> -->
Buenas Prácticas
1. Organización de Archivos
views/
├── layouts/ # Plantillas base
├── partials/ # Componentes reutilizables
├── pages/ # Páginas completas
└── components/ # Componentes específicos
2. Nomenclatura
- Usar nombres descriptivos para plantillas
- Usar snake_case para archivos:
user_profile.php - Usar camelCase para variables:
{{ userName }}
3. Separación de Lógica
// ❌ Evitar lógica compleja en plantillas
{% if user.role === 'admin' && user.active === true %}
// ✅ Procesar datos antes de renderizar
$template->render('admin', [
'showAdminPanel' => $user->isActiveAdmin()
]);
4. Reutilización con Includes
// ✅ Componentes reutilizables
{% include 'partials/alert' %}
{% include 'partials/pagination' %}
Limitaciones Actuales
- No soporta
elseoelseifen condicionales - Los bucles solo soportan valores escalares simples
- No soporta operadores complejos en condiciones (
&&,||,==) - No soporta acceso a propiedades de objetos en plantillas
Resumen de Sintaxis
| Directiva | Sintaxis | Descripción |
|---|---|---|
| Variable | {{ var }} |
Muestra variable escapada |
| Old | {{ old('field') }} |
Valor previo de formulario |
| Errors | {{ errors()['field'][0] }} |
Mensaje de error |
| Asset | {{ asset('path') }} |
Ruta de recurso |
| If | {% if condition %} ... {% endif %} |
Condicional |
| Foreach | {% foreach array as item %} ... {% endforeach %} |
Bucle |
| Extends | {% extends 'template' %} |
Herencia |
| Section | {% section 'name' %} ... {% endsection %} |
Define sección |
| Include | {% include 'template' %} |
Incluye archivo |