Files
agenticSystem/docs/twig-filters.md
2026-04-17 10:24:43 +00:00

6.4 KiB

Twig Filters Reference

Acai usa filtros Twig con sintaxis |. No usar funciones Twig — solo filtros.

get — Consultar tabla de BD

{{ '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)
{# 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:

{% 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_.

{% 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

{# 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

{{ 'other_module_id' | module({param1: value1}) }}

{# Capturar en variable #}
{% set carrito = 'carrito_compras' | module({usuario_id: 123}) %}

imagec — Optimizar/redimensionar imágenes

{# 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 — Texto editable y traducción

Cualquier string con | translate se resuelve contra la tabla textos_generales del proyecto. Esta tabla cumple dos funciones a la vez:

  1. Traducción: cada fila guarda la versión del texto por cada idioma habilitado del proyecto.
  2. Edición de contenidos: es el canal oficial para que el usuario final (o el agente) modifique esos textos sin tocar código. El filtro | translate no es solo i18n — es el mecanismo por el que un texto "hardcodeado" en una plantilla se vuelve editable desde el CMS.
{{ 'Bienvenido' | translate }}
{{ variable | translate }}

Cómo funciona:

  • Los strings envueltos en | translate en las plantillas o en el código de los módulos 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 via cmsApi (update sobre textos_generales).

Reglas críticas para el agente:

  • 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 los textos en el código del módulo si se espera que el usuario pueda cambiarlos. Envuélvelos siempre en | translate: {{ 'Contáctanos' | translate }} en vez de Contáctanos.
  • Para cambiar un texto (traducirlo o editarlo), el flujo correcto es editar la fila correspondiente en textos_generales — nunca modificar el código de la plantilla.
  • Para añadir un texto nuevo editable, basta con escribir el string en el código con | translate; el sistema lo recogerá y el usuario podrá editarlo desde el admin. No hace falta insertar la fila manualmente (aunque se puede via cmsApi si quieres pre-cargar traducciones).

raw — Renderizar HTML sin escapar

{{ record.description | raw }}

truncate — Truncar texto

{{ record.description | truncate(150) }}

json_decode — Parsear JSON

{% 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 +):

{{ 'Hello ' ~ name ~ '!' }}
{% set url = '/products/' ~ product.slug ~ '/' %}

Concatenar en filtros

{% set stock = 'stocks' | get('producto_num=' ~ producto.num) %}

Ternario / Default

{{ title | default('Default Title') }}
{{ isActive ? 'active' : 'inactive' }}

Comparaciones

{% 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

{% 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

{% 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)