Refine hooks distinction, add assets/, rename twig-filters
- Clarify module hook vs file hook: when to use each, different call URLs - Add schema path (cms/data/schema/) to reglas importantes - Add assets/ and hook.php to module file structure - Fix variable assignment example: use string syntax for get() - Rename twig-filters.md → twig-reference.md (covers more than filters) - Move Global Variables from modular-system.md to twig-reference.md - Update all references in CLAUDE.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
224
docs/twig-reference.md
Normal file
224
docs/twig-reference.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Twig Reference
|
||||
|
||||
Referencia completa de filtros, operadores, sintaxis y variables globales disponibles en templates Acai. Solo filtros (con `|`), nunca funciones Twig.
|
||||
|
||||
## `get` — Consultar tabla de BD
|
||||
|
||||
```twig
|
||||
{{ 'table_name' | get(where, order, limit) }}
|
||||
```
|
||||
|
||||
- `table_name`: sin prefijo `cms_`
|
||||
- `where`: string SQL o objeto (opcional)
|
||||
- `order`: string de orden (opcional)
|
||||
- `limit`: int (opcional)
|
||||
|
||||
```twig
|
||||
{# Todos los registros #}
|
||||
{% set products = 'productos' | get() %}
|
||||
|
||||
{# Con WHERE string #}
|
||||
{% set active = 'productos' | get('activo=1') %}
|
||||
|
||||
{# Con WHERE objeto #}
|
||||
{% set active = 'productos' | get({activo: 1}) %}
|
||||
|
||||
{# Con WHERE + ORDER + LIMIT #}
|
||||
{% set latest = 'noticias' | get('publicado=1', 'fecha DESC', 6) %}
|
||||
|
||||
{# Completo #}
|
||||
{% set caros = 'productos' | get('precio > 100', 'precio DESC', 20) %}
|
||||
|
||||
{# Single record (primer resultado) #}
|
||||
{% set product = 'productos' | get({num: 42}) %}
|
||||
{{ product[0].nombre }}
|
||||
```
|
||||
|
||||
Iterar resultados:
|
||||
```twig
|
||||
{% for producto in 'productos' | get('activo=1', 'num DESC', 10) %}
|
||||
<h3>{{ producto.titulo }}</h3>
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
## `queryDB` — SQL directo
|
||||
|
||||
Usa nombre de tabla completo WITH prefijo `cms_`.
|
||||
|
||||
```twig
|
||||
{% set results = 'SELECT * FROM cms_productos WHERE precio > 100 ORDER BY precio ASC' | queryDB() %}
|
||||
|
||||
{# JOIN complejo #}
|
||||
{% set top = 'SELECT p.*, COUNT(v.num) as ventas
|
||||
FROM cms_productos p
|
||||
LEFT JOIN cms_ventas v ON v.producto_num = p.num
|
||||
GROUP BY p.num
|
||||
ORDER BY ventas DESC
|
||||
LIMIT 5' | queryDB() %}
|
||||
```
|
||||
|
||||
Usar solo cuando `get` no sea suficiente.
|
||||
|
||||
## `hook` — Ejecutar PHP Hook
|
||||
|
||||
```twig
|
||||
{# Llamar y mostrar resultado #}
|
||||
{{ 'hooks/module_id/' | hook({param1: 'value', param2: variable}) }}
|
||||
|
||||
{# Capturar en variable #}
|
||||
{% set result = 'hooks/calcular_precio/' | hook({cantidad: 5, tipo: 'mayoreo'}) %}
|
||||
<p>Total: ${{ result.total }}</p>
|
||||
```
|
||||
|
||||
## `module` — Renderizar otro módulo
|
||||
|
||||
```twig
|
||||
{{ 'other_module_id' | module({param1: value1}) }}
|
||||
|
||||
{# Capturar en variable #}
|
||||
{% set carrito = 'carrito_compras' | module({usuario_id: 123}) %}
|
||||
```
|
||||
|
||||
## `imagec` — Optimizar/redimensionar imágenes
|
||||
|
||||
```twig
|
||||
{# Redimensionar a ancho #}
|
||||
<img src="{{ record.image[0].urlPath | imagec(400) }}" />
|
||||
|
||||
{# En srcset #}
|
||||
<img src="{{ record.image[0].urlPath | imagec(800) }}"
|
||||
srcset="{{ record.image[0].urlPath | imagec(400) }} 400w,
|
||||
{{ record.image[0].urlPath | imagec(800) }} 800w" />
|
||||
```
|
||||
|
||||
## `translate` — Traducción
|
||||
|
||||
```twig
|
||||
{{ 'Bienvenido' | translate }}
|
||||
{{ variable | translate }}
|
||||
```
|
||||
|
||||
## `raw` — Renderizar HTML sin escapar
|
||||
|
||||
```twig
|
||||
{{ record.description | raw }}
|
||||
```
|
||||
|
||||
## `truncate` — Truncar texto
|
||||
|
||||
```twig
|
||||
{{ record.description | truncate(150) }}
|
||||
```
|
||||
|
||||
## `json_decode` — Parsear JSON
|
||||
|
||||
```twig
|
||||
{% set data = jsonString | json_decode %}
|
||||
{{ data.key }}
|
||||
```
|
||||
|
||||
## `split`, `filter` — Filtros estándar Twig
|
||||
|
||||
Misma funcionalidad que Twig estándar.
|
||||
|
||||
---
|
||||
|
||||
## Operadores y Sintaxis
|
||||
|
||||
### Concatenación
|
||||
|
||||
Twig usa `~` (no `.` ni `+`):
|
||||
|
||||
```twig
|
||||
{{ 'Hello ' ~ name ~ '!' }}
|
||||
{% set url = '/products/' ~ product.slug ~ '/' %}
|
||||
```
|
||||
|
||||
### Concatenar en filtros
|
||||
|
||||
```twig
|
||||
{% set stock = 'stocks' | get('producto_num=' ~ producto.num) %}
|
||||
```
|
||||
|
||||
### Ternario / Default
|
||||
|
||||
```twig
|
||||
{{ title | default('Default Title') }}
|
||||
{{ isActive ? 'active' : 'inactive' }}
|
||||
```
|
||||
|
||||
### Comparaciones
|
||||
|
||||
```twig
|
||||
{% if items | length > 0 %}
|
||||
{% if type == 'premium' %}
|
||||
{% if name is not empty %}
|
||||
```
|
||||
|
||||
En `c-if` usar `=` (simple). En `{% if %}` usar `==` (doble).
|
||||
|
||||
---
|
||||
|
||||
## Ejemplos complejos
|
||||
|
||||
### Galería con productos y stock
|
||||
|
||||
```twig
|
||||
{% for producto in 'productos' | get('destacado=1', 'num DESC', 12) %}
|
||||
<div class="producto-card">
|
||||
<img src="{{ producto.imagen[0].urlPath | imagec(400) }}" alt="{{ producto.titulo }}">
|
||||
<h3>{{ producto.titulo }}</h3>
|
||||
<p>{{ producto.descripcion | truncate(100) }}</p>
|
||||
|
||||
{% set stock = 'stocks' | get('producto_num=' ~ producto.num) %}
|
||||
<span>Stock: {{ stock[0].cantidad }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
### Múltiples filtros combinados
|
||||
|
||||
```twig
|
||||
{% set categorias = 'categorias' | get() %}
|
||||
{% set productos = 'productos' | get('activo=1', 'titulo ASC', 20) %}
|
||||
{% set stats = 'hooks/obtener_stats/' | hook({fecha_inicio: '2024-01-01'}) %}
|
||||
|
||||
<h1>{{ stats.titulo | translate }}</h1>
|
||||
|
||||
<nav>
|
||||
{% for cat in categorias %}
|
||||
<a href="">{{ cat.nombre }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{% for prod in productos %}
|
||||
<div>
|
||||
<img src="{{ prod.imagen[0].urlPath | imagec(300) }}" alt="">
|
||||
<h3>{{ prod.titulo }}</h3>
|
||||
</div>
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Puntos importantes
|
||||
|
||||
1. **Solo filtros, no funciones:** `'tabla' | get()` no `get('tabla')`
|
||||
2. **Upload fields son arrays:** `record.imagen[0].urlPath`, no `record.imagen`
|
||||
3. **Tablas sin prefijo `cms_`** en `get()`. Con prefijo en `queryDB()`
|
||||
4. **Concatenar con `~`:** `'stocks' | get('producto_num=' ~ producto.num)`
|
||||
|
||||
---
|
||||
|
||||
## Global Variables
|
||||
|
||||
Variables disponibles en todos los templates (módulos y general sections):
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `section_id` | Unique ID per module instance (use for anchors, JS scoping) |
|
||||
| `interno` | `true` when viewing in CMS editor, `false` on public site |
|
||||
| `server.HTTP_HOST` | Current domain |
|
||||
| `loop.index` | 1-based iteration index (inside `c-for`) |
|
||||
| `loop.index is odd` / `loop.index is even` | For alternating layouts |
|
||||
| `thisrecord` | Current record data (only in general sections) |
|
||||
Reference in New Issue
Block a user