Files
roi-theme/Shared/Infrastructure/Api/WordPress/AjaxController.php
FrankZamora 90863cd8f5 fix(structure): Correct case-sensitivity for Linux compatibility
Rename folders to match PHP PSR-4 autoloading conventions:
- schemas → Schemas
- shared → Shared
- Wordpress → WordPress (in all locations)

Fixes deployment issues on Linux servers where filesystem is case-sensitive.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 22:53:34 -06:00

216 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace ROITheme\Shared\Infrastructure\Api\Wordpress;
use ROITheme\Shared\Application\UseCases\SaveComponent\SaveComponentUseCase;
use ROITheme\Shared\Application\UseCases\SaveComponent\SaveComponentRequest;
use ROITheme\Shared\Application\UseCases\GetComponent\GetComponentUseCase;
use ROITheme\Shared\Application\UseCases\GetComponent\GetComponentRequest;
use ROITheme\Shared\Application\UseCases\DeleteComponent\DeleteComponentUseCase;
use ROITheme\Shared\Application\UseCases\DeleteComponent\DeleteComponentRequest;
use ROITheme\Shared\Infrastructure\Di\DIContainer;
/**
* AjaxController - Endpoints AJAX de WordPress
*
* RESPONSABILIDAD: Manejar HTTP (request/response)
*
* PRINCIPIOS:
* - Single Responsibility: Solo maneja HTTP, delega lógica a Use Cases
* - Dependency Inversion: Depende de Use Cases (Application)
*
* SEGURIDAD:
* - Verifica nonce
* - Verifica capabilities (manage_options)
* - Sanitiza inputs
*
* @package ROITheme\Infrastructure\Api\WordPress
*/
final class AjaxController
{
private const NONCE_ACTION = 'roi_theme_admin_nonce';
public function __construct(
private DIContainer $container
) {}
/**
* Registrar hooks de WordPress
*/
public function register(): void
{
add_action('wp_ajax_roi_theme_save_component', [$this, 'saveComponent']);
add_action('wp_ajax_roi_theme_get_component', [$this, 'getComponent']);
add_action('wp_ajax_roi_theme_delete_component', [$this, 'deleteComponent']);
add_action('wp_ajax_roi_theme_sync_schema', [$this, 'syncSchema']);
}
/**
* Endpoint: Guardar componente
*
* POST /wp-admin/admin-ajax.php?action=roi_theme_save_component
* Body: { nonce, component_name, data }
*
* @return void (usa wp_send_json_*)
*/
public function saveComponent(): void
{
// 1. Seguridad
if (!$this->verifySecurity()) {
wp_send_json_error([
'message' => 'Security check failed'
], 403);
return;
}
// 2. Sanitizar input
$componentName = sanitize_key($_POST['component_name'] ?? '');
$data = $_POST['data'] ?? [];
if (empty($componentName)) {
wp_send_json_error([
'message' => 'Component name is required'
], 400);
return;
}
// 3. Crear Request DTO
$request = new SaveComponentRequest($componentName, $data);
// 4. Ejecutar Use Case
$useCase = new SaveComponentUseCase(
$this->container->getComponentRepository(),
$this->container->getValidationService(),
$this->container->getCacheService()
);
$response = $useCase->execute($request);
// 5. Respuesta HTTP
if ($response->isSuccess()) {
wp_send_json_success($response->getData());
} else {
wp_send_json_error([
'message' => 'Validation failed',
'errors' => $response->getErrors()
], 422);
}
}
/**
* Endpoint: Obtener componente
*
* GET /wp-admin/admin-ajax.php?action=roi_theme_get_component&component_name=xxx
*/
public function getComponent(): void
{
if (!$this->verifySecurity()) {
wp_send_json_error(['message' => 'Security check failed'], 403);
return;
}
$componentName = sanitize_key($_GET['component_name'] ?? '');
if (empty($componentName)) {
wp_send_json_error(['message' => 'Component name is required'], 400);
return;
}
$request = new GetComponentRequest($componentName);
$useCase = new GetComponentUseCase(
$this->container->getComponentRepository(),
$this->container->getCacheService()
);
$response = $useCase->execute($request);
if ($response->isSuccess()) {
wp_send_json_success($response->getData());
} else {
wp_send_json_error(['message' => $response->getError()], 404);
}
}
/**
* Endpoint: Eliminar componente
*
* POST /wp-admin/admin-ajax.php?action=roi_theme_delete_component
* Body: { nonce, component_name }
*/
public function deleteComponent(): void
{
if (!$this->verifySecurity()) {
wp_send_json_error(['message' => 'Security check failed'], 403);
return;
}
$componentName = sanitize_key($_POST['component_name'] ?? '');
if (empty($componentName)) {
wp_send_json_error(['message' => 'Component name is required'], 400);
return;
}
$request = new DeleteComponentRequest($componentName);
$useCase = new DeleteComponentUseCase(
$this->container->getComponentRepository(),
$this->container->getCacheService()
);
$response = $useCase->execute($request);
if ($response->isSuccess()) {
wp_send_json_success(['message' => $response->getMessage()]);
} else {
wp_send_json_error(['message' => $response->getError()], 404);
}
}
/**
* Endpoint: Sincronizar schemas
*
* POST /wp-admin/admin-ajax.php?action=roi_theme_sync_schema
*/
public function syncSchema(): void
{
if (!$this->verifySecurity()) {
wp_send_json_error(['message' => 'Security check failed'], 403);
return;
}
$syncService = $this->container->getSchemaSyncService();
$result = $syncService->syncAll();
if ($result['success']) {
wp_send_json_success($result['data']);
} else {
wp_send_json_error(['message' => $result['error']], 500);
}
}
/**
* Verificar seguridad (nonce + capabilities)
*
* @return bool
*/
private function verifySecurity(): bool
{
// Verificar nonce
$nonce = $_REQUEST['nonce'] ?? '';
if (!wp_verify_nonce($nonce, self::NONCE_ACTION)) {
return false;
}
// Verificar permisos (solo administradores)
if (!current_user_can('manage_options')) {
return false;
}
return true;
}
}