feat(theme): add [roi_post_grid] shortcode for static pages
- 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>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Application\UseCases\RenderPostGrid;
|
||||
|
||||
/**
|
||||
* RenderPostGridRequest - DTO de entrada para renderizar post grid shortcode
|
||||
*
|
||||
* RESPONSABILIDAD: Encapsular todos los atributos del shortcode [roi_post_grid]
|
||||
*
|
||||
* USO:
|
||||
* ```php
|
||||
* $request = RenderPostGridRequest::fromArray([
|
||||
* 'category' => 'precios-unitarios',
|
||||
* 'posts_per_page' => 6,
|
||||
* 'columns' => 3
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @package ROITheme\Shared\Application\UseCases\RenderPostGrid
|
||||
*/
|
||||
final readonly class RenderPostGridRequest
|
||||
{
|
||||
public function __construct(
|
||||
public string $category = '',
|
||||
public string $excludeCategory = '',
|
||||
public string $tag = '',
|
||||
public string $author = '',
|
||||
public int $postsPerPage = 9,
|
||||
public int $columns = 3,
|
||||
public string $orderby = 'date',
|
||||
public string $order = 'DESC',
|
||||
public bool $showPagination = false,
|
||||
public int $offset = 0,
|
||||
public string $excludePosts = '',
|
||||
public bool $showThumbnail = true,
|
||||
public bool $showExcerpt = true,
|
||||
public bool $showMeta = true,
|
||||
public bool $showCategories = true,
|
||||
public int $excerptLength = 20,
|
||||
public string $class = '',
|
||||
public string $id = '',
|
||||
public int $paged = 1
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Factory method: Crear desde array de atributos del shortcode
|
||||
*
|
||||
* @param array<string, mixed> $atts Atributos sanitizados del shortcode
|
||||
* @return self
|
||||
*/
|
||||
public static function fromArray(array $atts): self
|
||||
{
|
||||
return new self(
|
||||
category: (string) ($atts['category'] ?? ''),
|
||||
excludeCategory: (string) ($atts['exclude_category'] ?? ''),
|
||||
tag: (string) ($atts['tag'] ?? ''),
|
||||
author: (string) ($atts['author'] ?? ''),
|
||||
postsPerPage: self::clampInt((int) ($atts['posts_per_page'] ?? 9), 1, 50),
|
||||
columns: self::clampInt((int) ($atts['columns'] ?? 3), 1, 4),
|
||||
orderby: self::sanitizeOrderby((string) ($atts['orderby'] ?? 'date')),
|
||||
order: self::sanitizeOrder((string) ($atts['order'] ?? 'DESC')),
|
||||
showPagination: self::toBool($atts['show_pagination'] ?? false),
|
||||
offset: max(0, (int) ($atts['offset'] ?? 0)),
|
||||
excludePosts: (string) ($atts['exclude_posts'] ?? ''),
|
||||
showThumbnail: self::toBool($atts['show_thumbnail'] ?? true),
|
||||
showExcerpt: self::toBool($atts['show_excerpt'] ?? true),
|
||||
showMeta: self::toBool($atts['show_meta'] ?? true),
|
||||
showCategories: self::toBool($atts['show_categories'] ?? true),
|
||||
excerptLength: max(1, (int) ($atts['excerpt_length'] ?? 20)),
|
||||
class: (string) ($atts['class'] ?? ''),
|
||||
id: (string) ($atts['id'] ?? ''),
|
||||
paged: max(1, (int) ($atts['paged'] ?? 1))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertir a array de parametros para QueryBuilder
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toQueryParams(): array
|
||||
{
|
||||
return [
|
||||
'category' => $this->category,
|
||||
'exclude_category' => $this->excludeCategory,
|
||||
'tag' => $this->tag,
|
||||
'author' => $this->author,
|
||||
'posts_per_page' => $this->postsPerPage,
|
||||
'orderby' => $this->orderby,
|
||||
'order' => $this->order,
|
||||
'offset' => $this->offset,
|
||||
'exclude_posts' => $this->excludePosts,
|
||||
'paged' => $this->paged,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertir a array de opciones para Renderer
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toRenderOptions(): array
|
||||
{
|
||||
return [
|
||||
'columns' => $this->columns,
|
||||
'show_thumbnail' => $this->showThumbnail,
|
||||
'show_excerpt' => $this->showExcerpt,
|
||||
'show_meta' => $this->showMeta,
|
||||
'show_categories' => $this->showCategories,
|
||||
'excerpt_length' => $this->excerptLength,
|
||||
'class' => $this->class,
|
||||
'id' => $this->id,
|
||||
'show_pagination' => $this->showPagination,
|
||||
'posts_per_page' => $this->postsPerPage,
|
||||
];
|
||||
}
|
||||
|
||||
private static function clampInt(int $value, int $min, int $max): int
|
||||
{
|
||||
return max($min, min($max, $value));
|
||||
}
|
||||
|
||||
private static function toBool(mixed $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
return $value === 'true' || $value === '1' || $value === 1;
|
||||
}
|
||||
|
||||
private static function sanitizeOrderby(string $value): string
|
||||
{
|
||||
$allowed = ['date', 'title', 'modified', 'rand', 'comment_count'];
|
||||
return in_array($value, $allowed, true) ? $value : 'date';
|
||||
}
|
||||
|
||||
private static function sanitizeOrder(string $value): string
|
||||
{
|
||||
$upper = strtoupper($value);
|
||||
return in_array($upper, ['ASC', 'DESC'], true) ? $upper : 'DESC';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ROITheme\Shared\Application\UseCases\RenderPostGrid;
|
||||
|
||||
use ROITheme\Shared\Domain\Contracts\PostGridQueryBuilderInterface;
|
||||
use ROITheme\Shared\Domain\Contracts\PostGridShortcodeRendererInterface;
|
||||
use ROITheme\Shared\Domain\Contracts\ComponentSettingsRepositoryInterface;
|
||||
use ROITheme\Shared\Domain\Constants\VisibilityDefaults;
|
||||
|
||||
/**
|
||||
* Caso de uso: Renderizar grid de posts para shortcode
|
||||
*
|
||||
* RESPONSABILIDAD: Orquestar la obtencion de settings, construccion de query
|
||||
* y renderizado del grid. No contiene logica de negocio, solo coordinacion.
|
||||
*
|
||||
* @package ROITheme\Shared\Application\UseCases\RenderPostGrid
|
||||
*/
|
||||
final class RenderPostGridUseCase
|
||||
{
|
||||
private const COMPONENT_NAME = 'post-grid';
|
||||
|
||||
public function __construct(
|
||||
private readonly PostGridQueryBuilderInterface $queryBuilder,
|
||||
private readonly PostGridShortcodeRendererInterface $renderer,
|
||||
private readonly ComponentSettingsRepositoryInterface $settingsRepository
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Ejecuta el caso de uso: obtiene settings, construye query y renderiza
|
||||
*
|
||||
* @param RenderPostGridRequest $request DTO con atributos del shortcode
|
||||
* @return string HTML del grid renderizado
|
||||
*/
|
||||
public function execute(RenderPostGridRequest $request): string
|
||||
{
|
||||
// 1. Obtener settings del componente post-grid desde BD
|
||||
$settings = $this->getSettings();
|
||||
|
||||
// 2. Construir query con los parametros del shortcode
|
||||
$query = $this->queryBuilder->build($request->toQueryParams());
|
||||
|
||||
// 3. Renderizar grid con query, settings y opciones
|
||||
$html = $this->renderer->render(
|
||||
$query,
|
||||
$settings,
|
||||
$request->toRenderOptions()
|
||||
);
|
||||
|
||||
// 4. Limpiar query (importante para evitar conflictos)
|
||||
wp_reset_postdata();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene settings del componente post-grid
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getSettings(): array
|
||||
{
|
||||
$settings = $this->settingsRepository->getComponentSettings(self::COMPONENT_NAME);
|
||||
|
||||
if (empty($settings)) {
|
||||
return VisibilityDefaults::getForComponent(self::COMPONENT_NAME);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user