# Twig — Filtros personalizados de Acai Este documento describe los filtros Twig propios de Acai (`get`, `queryDB`, `hook`, `module`, `imagec`, `translate`) y los filtros estándar más usados (`raw`, `truncate`, `json_decode`, `split`, `filter`). Acai usa **filtros con pipe `|`**, nunca funciones. Léelo antes de escribir cualquier expresión Twig dentro de `index-base.tpl` o de una sección general. Cubre también la concatenación con `~`, los ternarios, el operador `default` y la diferencia entre `c-if` (=) y `{% if %}` (==). ## `get` — Consultar tabla de BD ```twig {{ 'table_name' | get(where, order, limit) }} ``` - `table_name`: **sin prefijo `cms_`** - `where`: string SQL u objeto (opcional) - `order`: string de orden (opcional) - `limit`: entero (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) %} {# 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) %}

{{ producto.titulo }}

{% endfor %} ``` Concatenar valor dinámico en WHERE — usa el operador `~`: ```twig {% set stock = 'stocks' | get('producto_num=' ~ producto.num) %} ``` ## `queryDB` — SQL directo Usa el nombre de tabla **completo CON prefijo `cms_`**. Solo cuando `get` no sea suficiente (JOINs, agregaciones complejas). ```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() %} ``` ## `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'}) %}

Total: ${{ result.total }}

``` El primer argumento es la ruta del endpoint del hook (`hooks//`). El objeto pasa los parámetros, que el PHP recibe como variables (`$cantidad`, `$tipo`). ## `module` — Renderizar otro módulo ```twig {{ 'other_module_id' | module({param1: value1}) }} {# Capturar en variable #} {% set carrito = 'carrito_compras' | module({usuario_id: 123}) %} ``` Equivale a ``. ## `imagec` — Optimizar imágenes ```twig {# Redimensionar a ancho específico #} {# Con srcset #} ``` Acai genera versiones optimizadas (webp + tamaños) y las cachea. Usa siempre `imagec` para imágenes en producción. ## `translate` — Texto editable y traducción Cualquier string con `| translate` se resuelve contra la tabla `textos_generales` del proyecto. Cumple **dos funciones a la vez**: 1. **Traducción**: cada fila guarda la versión del texto por cada idioma habilitado. 2. **Edición de contenidos**: es el canal oficial para que el usuario final modifique esos textos sin tocar código. `| translate` no es solo i18n — es el mecanismo por el que un texto "hardcodeado" se vuelve editable desde el CMS. ```twig {{ 'Bienvenido' | translate }} {{ variable | translate }} {{ 'Contáctanos' | translate | raw }} ``` Cómo funciona: - Los strings envueltos en `| translate` se buscan en `textos_generales`. - Si existe la fila, devuelve el valor guardado (en el idioma activo). - Si no existe, devuelve el texto original tal cual (fallback). - Las filas se editan desde el admin del CMS o vía `CmsApi` (update sobre `textos_generales`). Reglas críticas: - **NO crees archivos JSON de traducciones, `.po`, ni ningún sistema i18n externo.** El único sistema de textos traducibles/editables es la tabla `textos_generales`. - **NO hardcodees textos en el código del módulo** si el usuario debe poder editarlos. Envuélvelos en `| translate`. - Para **cambiar un texto** (traducir o editar), edita la fila correspondiente en `textos_generales` — nunca modifiques el código. - Para **añadir un texto nuevo editable**, basta con escribir el string con `| translate` en el código; el sistema lo recoge y el usuario lo puede editar desde el admin. ## Filtros estándar ### `raw` — Renderizar HTML sin escapar ```twig {{ record.description | raw }} ``` Imprescindible para `wysiwyg` y para HTML construido en variables. ### `truncate` — Truncar texto ```twig {{ record.description | truncate(150) }} ``` ### `json_decode` — Parsear JSON ```twig {% set data = jsonString | json_decode %} {{ data.key }} ``` ### `split`, `filter`, `length`, `default`, `lower`, `upper`, `trim`, `replace` Funcionan igual que en Twig estándar. ```twig {{ title | default('Sin título') }} {{ items | length }} {{ name | upper }} ``` ## Operadores y sintaxis ### Concatenación con `~` Twig usa `~` (no `.` ni `+`): ```twig {{ 'Hello ' ~ name ~ '!' }} {% set url = '/products/' ~ product.slug ~ '/' %} ``` En filtros: ```twig {% set stock = 'stocks' | get('producto_num=' ~ producto.num) %} ``` ### Ternario ```twig {{ isActive ? 'active' : 'inactive' }} {{ title | default('Default Title') }} ``` ### Comparaciones | Contexto | Igualdad | |----------|----------| | `c-if` | `=` (un solo igual) | | `{% if %}` | `==` (doble igual) | ```twig {# Atributo Acai - un igual #}
{# Twig estándar - doble igual #} {% if type == 'premium' %} {% if items | length > 0 %} {% if name is not empty %} ``` ## Ejemplos complejos ### Galería con productos y stock ```twig {% for producto in 'productos' | get('destacado=1', 'num DESC', 12) %}
{{ producto.titulo }}

{{ producto.titulo }}

{{ producto.descripcion | truncate(100) }}

{% set stock = 'stocks' | get('producto_num=' ~ producto.num) %} Stock: {{ stock[0].cantidad }}
{% 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'}) %}

{{ stats.titulo | translate }}

{% for prod in productos %}

{{ prod.titulo }}

{% endfor %} ``` ### Composición con `` y configuración global ```twig {% set logoUrl = tienda.logo.0.urlPath ? 'https://' ~ server.HTTP_HOST ~ tienda.logo.0.urlPath : 'https://' ~ server.HTTP_HOST ~ '/template/estandar/images/logo.png' %} {{ tienda.nombre }} ``` ## Reglas críticas 1. **Solo filtros, nunca funciones.** `'tabla' | get()`, no `get('tabla')`. 2. **Tablas sin prefijo `cms_`** en `get()`. **Con prefijo `cms_`** en `queryDB()`. 3. **Upload fields son arrays.** `record.imagen[0].urlPath`, no `record.imagen`. 4. **Concatenación con `~`**, no con `.` ni `+`. 5. **`c-if` usa `=`**, **`{% if %}` usa `==`**. 6. **Foreign keys con sufijo `_num`**: `categoria_num`, no `categoria_id`. 7. **`enlace` ya tiene barras** — no las añadas. 8. **PK siempre es `num`**, nunca `id`. 9. **`| translate` para textos editables** — nunca crees JSONs de i18n. 10. Usa `imagec(width)` para imágenes en producción.