- Create PostGridShortcodeRegistrar for WordPress shortcode registration - Implement RenderPostGridUseCase following Clean Architecture - Add PostGridQueryBuilder for custom WP_Query construction - Add PostGridShortcodeRenderer for HTML/CSS generation - Register shortcode in DIContainer with proper DI - Add shortcode usage guide in post-grid admin panel - Fix sidebar layout: add hide_for_logged_in check to wrapper visibility Shortcode attributes: category, tag, author, posts_per_page, columns, show_pagination, show_thumbnail, show_excerpt, show_meta, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
Especificacion de Shortcode Post Grid
Purpose
Crear un shortcode [roi_post_grid] que permita insertar grids de posts en cualquier pagina o entrada de WordPress, con filtros configurables por categoria, tag, autor y otras opciones. Sigue Clean Architecture delegando logica a Use Cases.
Context
Problema Actual
El componente post-grid implementado en templates-unificados solo funciona en templates de archivo (home.php, archive.php, etc.) porque depende del loop principal de WordPress (global $wp_query).
Solucion
Crear un shortcode que:
- Siga Clean Architecture (ShortcodeRegistrar → UseCase → Repository)
- Reutilice estilos del componente
post-gridexistente - Permita filtrar posts por categoria, tag, autor
- No dependa del loop principal de WordPress
Relacion con templates-unificados
- Este shortcode complementa (no reemplaza) el componente post-grid
- El componente post-grid sigue funcionando en templates de archivo
- El shortcode permite insertar grids en contenido arbitrario
Nota sobre shortcodes legacy
Los shortcodes existentes (apu_table, apu_row) estan en Inc/apu-tables.php (patron legacy). Este nuevo shortcode sigue el patron moderno de Clean Architecture.
Requirements
Requirement: Arquitectura del Shortcode
The shortcode MUST follow Clean Architecture patterns.
Scenario: Ubicacion del ShortcodeRegistrar
- WHEN se implementa el shortcode
- THEN DEBE estar en
Shared/Infrastructure/Wordpress/PostGridShortcodeRegistrar.php - AND DEBE usar namespace
ROITheme\Shared\Infrastructure\Wordpress - AND DEBE registrar el shortcode via add_shortcode
Scenario: Delegacion a Use Case
- WHEN el shortcode se ejecuta
- THEN PostGridShortcodeRegistrar DEBE delegar a RenderPostGridUseCase
- AND NO DEBE contener logica de negocio
- AND solo sanitiza atributos y pasa al Use Case
Scenario: Ubicacion del Use Case
- WHEN se implementa la logica del shortcode
- THEN DEBE estar en
Shared/Application/UseCases/RenderPostGrid/RenderPostGridUseCase.php - AND DEBE usar namespace
ROITheme\Shared\Application\UseCases\RenderPostGrid - AND DEBE orquestar Query, Renderer y Settings
Scenario: Ubicacion del QueryBuilder
- WHEN se construye el WP_Query
- THEN DEBE estar en
Shared/Infrastructure/Query/PostGridQueryBuilder.php - AND DEBE usar namespace
ROITheme\Shared\Infrastructure\Query - AND DEBE recibir parametros y retornar WP_Query
Scenario: Ubicacion del ShortcodeRenderer
- WHEN se genera el HTML del grid
- THEN DEBE estar en
Shared/Infrastructure/Ui/PostGridShortcodeRenderer.php - AND DEBE usar namespace
ROITheme\Shared\Infrastructure\Ui - AND DEBE inyectar CSSGeneratorInterface
Requirement: Estructura de Clases
Each class MUST have single responsibility following SRP.
Scenario: Responsabilidad de PostGridShortcodeRegistrar
- WHEN se define PostGridShortcodeRegistrar
- THEN DEBE tener metodo estatico
register()para add_shortcode - AND DEBE tener metodo
handleShortcode(array $atts): string - AND handleShortcode DEBE sanitizar atributos
- AND handleShortcode DEBE obtener UseCase del DIContainer
- AND handleShortcode DEBE retornar resultado del UseCase
- AND NO DEBE tener mas de 50 lineas
Scenario: Responsabilidad de RenderPostGridUseCase
- WHEN se define RenderPostGridUseCase
- THEN DEBE recibir RenderPostGridRequest como input
- AND DEBE inyectar PostGridQueryBuilderInterface via constructor
- AND DEBE inyectar PostGridShortcodeRendererInterface via constructor
- AND DEBE inyectar ComponentSettingsRepositoryInterface via constructor
- AND DEBE orquestar: obtener settings, construir query, renderizar
- AND DEBE retornar string HTML
- AND NO DEBE tener mas de 80 lineas
Scenario: Responsabilidad de PostGridQueryBuilder
- WHEN se define PostGridQueryBuilder
- THEN DEBE recibir parametros de filtro (category, tag, etc.)
- AND DEBE construir array de argumentos WP_Query
- AND DEBE retornar WP_Query configurado
- AND NO DEBE generar HTML
- AND NO DEBE tener mas de 100 lineas
Scenario: Responsabilidad de PostGridShortcodeRenderer
- WHEN se define PostGridShortcodeRenderer
- THEN DEBE recibir WP_Query y configuracion
- AND DEBE inyectar CSSGeneratorInterface
- AND DEBE generar HTML del grid
- AND DEBE generar CSS inline
- AND NO DEBE construir queries
- AND NO DEBE tener mas de 150 lineas
Requirement: Interfaces en Domain/Application
Interfaces MUST be defined for dependency injection.
Scenario: Interface del QueryBuilder
- WHEN se define la interface
- THEN DEBE estar en
Shared/Domain/Contracts/PostGridQueryBuilderInterface.php - AND DEBE tener metodo
build(array $params): \WP_Query
Scenario: Interface del Renderer
- WHEN se define la interface
- THEN DEBE estar en
Shared/Domain/Contracts/PostGridShortcodeRendererInterface.php - AND DEBE tener metodo
render(\WP_Query $query, array $settings, array $options): string
Scenario: Request DTO
- WHEN se define el DTO de entrada
- THEN DEBE estar en
Shared/Application/UseCases/RenderPostGrid/RenderPostGridRequest.php - AND DEBE ser readonly class con propiedades tipadas
- AND DEBE contener todos los atributos del shortcode
Requirement: Atributos del Shortcode
The shortcode MUST accept configurable attributes.
Scenario: Atributo category
- WHEN se usa
[roi_post_grid category="precios-unitarios"] - THEN DEBE filtrar posts por slug de categoria
- AND DEBE aceptar multiples categorias separadas por coma
- AND ejemplo:
category="cat1,cat2,cat3"
Scenario: Atributo exclude_category
- WHEN se usa
[roi_post_grid exclude_category="noticias"] - THEN DEBE excluir posts de esas categorias
- AND DEBE aceptar multiples categorias separadas por coma
Scenario: Atributo tag
- WHEN se usa
[roi_post_grid tag="concreto"] - THEN DEBE filtrar posts por slug de tag
- AND DEBE aceptar multiples tags separados por coma
Scenario: Atributo author
- WHEN se usa
[roi_post_grid author="admin"] - THEN DEBE filtrar posts por login de autor
- AND DEBE aceptar ID numerico o login string
Scenario: Atributo posts_per_page
- WHEN se usa
[roi_post_grid posts_per_page="6"] - THEN DEBE limitar cantidad de posts mostrados
- AND default DEBE ser 9
- AND DEBE aceptar valores entre 1 y 50
Scenario: Atributo columns
- WHEN se usa
[roi_post_grid columns="4"] - THEN DEBE definir columnas en desktop
- AND default DEBE ser 3
- AND DEBE aceptar valores 1, 2, 3 o 4
Scenario: Atributo orderby
- WHEN se usa
[roi_post_grid orderby="title"] - THEN DEBE ordenar posts por ese campo
- AND default DEBE ser "date"
- AND opciones validas: date, title, modified, rand, comment_count
Scenario: Atributo order
- WHEN se usa
[roi_post_grid order="ASC"] - THEN DEBE definir direccion del orden
- AND default DEBE ser "DESC"
- AND opciones validas: ASC, DESC
Scenario: Atributo show_pagination
- WHEN se usa
[roi_post_grid show_pagination="true"] - THEN DEBE mostrar paginacion si hay mas posts
- AND default DEBE ser false
Scenario: Atributo offset
- WHEN se usa
[roi_post_grid offset="3"] - THEN DEBE saltar los primeros N posts
- AND default DEBE ser 0
Scenario: Atributo exclude_posts
- WHEN se usa
[roi_post_grid exclude_posts="123,456"] - THEN DEBE excluir posts por ID
- AND DEBE aceptar IDs separados por coma
Scenario: Atributos de visualizacion
- WHEN se usan atributos de visualizacion
- THEN show_thumbnail default true
- AND show_excerpt default true
- AND show_meta default true
- AND show_categories default true
- AND excerpt_length default 20
Scenario: Atributo class
- WHEN se usa
[roi_post_grid class="my-custom-grid"] - THEN DEBE agregar clase CSS adicional al contenedor
Scenario: Atributo id para paginacion multiple
- WHEN se usa
[roi_post_grid id="grid-1" show_pagination="true"] - THEN DEBE usar query var unico
paged_grid-1 - AND permite multiples shortcodes paginados en misma pagina
Requirement: Obtencion de Settings
The shortcode MUST obtain styles from post-grid component settings.
Scenario: Lectura de configuracion
- WHEN RenderPostGridUseCase se ejecuta
- THEN DEBE usar ComponentSettingsRepositoryInterface
- AND DEBE obtener settings de componente 'post-grid'
- AND DEBE aplicar colores, spacing, visual_effects del componente
Scenario: Settings no encontrados
- WHEN no existen settings de post-grid en BD
- THEN DEBE usar valores default definidos en VisibilityDefaults
- AND NO DEBE fallar con error
Requirement: Renderizado HTML
The shortcode MUST generate valid HTML with proper escaping.
Scenario: Estructura HTML del grid
- WHEN el shortcode renderiza
- THEN DEBE generar contenedor con clase
roi-post-grid-shortcode - AND DEBE generar row con clase Bootstrap
row - AND cada card DEBE tener columna responsive
Scenario: Clases responsive de columnas
- WHEN columns es 3
- THEN cada card DEBE tener
col-12 col-md-6 col-lg-4 - AND para columns=4:
col-12 col-md-6 col-lg-3 - AND para columns=2:
col-12 col-md-6 - AND para columns=1:
col-12
Scenario: Sin resultados
- WHEN el query no encuentra posts
- THEN DEBE mostrar mensaje "No se encontraron publicaciones"
- AND NO DEBE romper layout
Scenario: Escaping obligatorio
- WHEN se genera HTML
- THEN DEBE usar esc_html() para textos
- AND DEBE usar esc_attr() para atributos
- AND DEBE usar esc_url() para URLs
- AND DEBE usar wp_kses_post() para excerpts
Requirement: CSS via CSSGenerator
The shortcode MUST use CSSGeneratorInterface for styles.
Scenario: Generacion de CSS
- WHEN PostGridShortcodeRenderer genera CSS
- THEN DEBE inyectar CSSGeneratorInterface
- AND DEBE usar settings de post-grid desde BD
- AND DEBE generar CSS inline en el shortcode
Scenario: Selector unico
- WHEN se genera CSS
- THEN DEBE usar selector
.roi-post-grid-shortcode - AND si se especifica id, usar
.roi-post-grid-shortcode-{id} - AND NO DEBE conflictuar con post-grid del template
Requirement: Registro del Shortcode
The shortcode MUST be registered in WordPress.
Scenario: Registro en bootstrap
- WHEN WordPress carga el tema
- THEN functions-addon.php DEBE llamar PostGridShortcodeRegistrar::register()
- AND DEBE estar disponible en editor clasico y Gutenberg
Scenario: Metodo register estatico
- WHEN se llama PostGridShortcodeRegistrar::register()
- THEN DEBE ejecutar add_shortcode('roi_post_grid', ...)
- AND DEBE usar DIContainer para obtener dependencias
Implementation Order
Fase 1: Interfaces y DTOs
- Crear
Shared/Domain/Contracts/PostGridQueryBuilderInterface.php - Crear
Shared/Domain/Contracts/PostGridShortcodeRendererInterface.php - Crear
Shared/Application/UseCases/RenderPostGrid/RenderPostGridRequest.php
Fase 2: Use Case
- Crear
Shared/Application/UseCases/RenderPostGrid/RenderPostGridUseCase.php - Implementar orquestacion de query, settings, render
Fase 3: Infrastructure - Query
- Crear
Shared/Infrastructure/Query/PostGridQueryBuilder.php - Implementar construccion de WP_Query con todos los filtros
Fase 4: Infrastructure - Renderer
- Crear
Shared/Infrastructure/Ui/PostGridShortcodeRenderer.php - Implementar generacion HTML y CSS
Fase 5: Infrastructure - Registrar
- Crear
Shared/Infrastructure/Wordpress/PostGridShortcodeRegistrar.php - Implementar registro y sanitizacion de atributos
Fase 6: Registro y DI
- Registrar en DIContainer las implementaciones
- Llamar register() en functions-addon.php
Fase 7: Testing
- Probar en pagina estatica
- Verificar filtros por categoria, tag
- Verificar paginacion con id unico
File Structure
Shared/
├── Domain/
│ └── Contracts/
│ ├── PostGridQueryBuilderInterface.php
│ └── PostGridShortcodeRendererInterface.php
├── Application/
│ └── UseCases/
│ └── RenderPostGrid/
│ ├── RenderPostGridRequest.php
│ └── RenderPostGridUseCase.php
└── Infrastructure/
├── Query/
│ └── PostGridQueryBuilder.php
├── Ui/
│ └── PostGridShortcodeRenderer.php
└── Wordpress/
└── PostGridShortcodeRegistrar.php
Examples
Ejemplo: Grid basico
[roi_post_grid]
Ejemplo: Posts de una categoria
[roi_post_grid category="precios-unitarios" posts_per_page="6"]
Ejemplo: Multiples shortcodes con paginacion
[roi_post_grid id="grid-cursos" category="cursos" show_pagination="true"]
[roi_post_grid id="grid-tutoriales" category="tutoriales" show_pagination="true"]
Dependencies
Existentes (reutilizar)
CSSGeneratorInterface- Para generar CSSComponentSettingsRepositoryInterface- Para leer config de post-gridDIContainer- Para inyeccion de dependencias- Bootstrap 5 grid system - Para layout responsive
Nuevas (crear)
PostGridQueryBuilderInterface- Contrato para query builderPostGridShortcodeRendererInterface- Contrato para rendererRenderPostGridRequest- DTO de entradaRenderPostGridUseCase- OrquestadorPostGridQueryBuilder- Implementacion queryPostGridShortcodeRenderer- Implementacion renderPostGridShortcodeRegistrar- Registro WordPress
Testing Checklist
- Shortcode renderiza sin atributos
- Filtro por categoria funciona
- Filtro por multiples categorias funciona
- Exclusion de categoria funciona
- Filtro por tag funciona
- Columnas 1, 2, 3, 4 funcionan
- Paginacion con id unico funciona
- Multiples shortcodes paginados funcionan
- CSS se aplica desde settings de post-grid
- Mensaje "sin posts" aparece cuando corresponde
- Escaping correcto en todo el HTML
- Funciona en editor clasico
- Funciona en Gutenberg
- No hay errores PHP
- Clases tienen menos de 150 lineas