<?php
|
namespace JVBase\rest\routes;
|
|
use JVBase\base\SchemaHelper;
|
use JVBase\Instance;
|
use JVBase\managers\Cache;
|
use JVBase\managers\SEO\render\SchemaOutput;
|
use JVBase\registrar\Registrar;
|
use JVBase\rest\Rest;
|
use JVBase\rest\Route;
|
use WP_REST_Request;
|
use WP_REST_Response;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* SEO Routes Class
|
*
|
* Handles REST API endpoints for SEO configuration
|
* Works with FormController.js for unified form handling
|
*/
|
class SEORoutes extends Rest
|
{
|
use Instance;
|
// protected SchemaBuilder $registry;
|
|
public function __construct()
|
{
|
$this->cacheName = 'schema';
|
parent::__construct();
|
$this->cache = Cache::for($this->cacheName, null, $this->cacheTtl);
|
// $this->registry = SchemaBuilder::getInstance();
|
}
|
|
/**
|
* Register REST routes
|
*/
|
public function registerRoutes(): void
|
{
|
Route::for('seo')
|
->post([$this, 'handleSEO'])
|
->auth('admin')
|
->args([
|
'action' => 'string|required|enum:save,reset,preview',
|
'context'=> 'string|required'
|
])
|
->rateLimit(30)
|
->register();
|
|
Route::for('seo/fields')
|
->get([$this, 'getFields'])
|
->auth('admin')
|
->args([
|
'type'=>'string|required'
|
])
|
->register();
|
}
|
|
/**
|
* Main SEO handler - routes to appropriate action
|
*/
|
public function handleSEO(WP_REST_Request $request): WP_REST_Response
|
{
|
$action = $request->get_param('action') ?? 'save';
|
$context = $request->get_param('context');
|
|
// Verify context is valid
|
if (!$this->isValidContext($context)) {
|
return $this->validationError([
|
'context' => "Invalid context: {$context}"
|
]);
|
}
|
|
return match($action) {
|
'save' => $this->saveSEO($request),
|
'reset' => $this->resetSEO($request),
|
'preview' => $this->previewSchema($request),
|
default => $this->validationError(['action' => 'Invalid action'])
|
};
|
}
|
|
/**
|
* Save SEO configuration
|
*/
|
protected function saveSEO(WP_REST_Request $request): WP_REST_Response
|
{
|
$context = $request->get_param('context');
|
$data = $request->get_json_params();
|
|
// Remove action and context from data
|
unset($data['action'], $data['context']);
|
|
// Handle site-wide settings
|
if (in_array($context, ['site', 'business'])) {
|
return $this->saveSiteSettings($context, $data);
|
}
|
|
// Handle content/taxonomy/user type settings
|
return $this->saveTypeSettings($context, $data);
|
}
|
|
/**
|
* Save site-wide settings (WebSite or Organization)
|
*/
|
protected function saveSiteSettings(string $context, array $data): WP_REST_Response
|
{
|
$errors = [];
|
|
$config = SchemaHelper::getInstance()::for($context);
|
$result = $config->updateConfig($data);
|
|
if (is_wp_error($result)) {
|
$errors[$context] = [
|
'message' => $result->get_error_message(),
|
'errors' => $result->get_error_data()
|
];
|
}
|
|
if (!empty($errors)) {
|
return $this->validationError($errors);
|
}
|
|
// Invalidate cache
|
$this->cache->flush();
|
|
return$this->success(['message'=>ucfirst($context).' settings saved successfully']);
|
}
|
|
/**
|
* Save content/taxonomy/user type settings
|
*/
|
protected function saveTypeSettings(string $type, array $data): WP_REST_Response
|
{
|
$config = SchemaHelper::getInstance();
|
|
// Separate meta and schema data if needed
|
$meta = $data['meta'] ?? [];
|
$schema = $data['schema'] ?? $data; // If no separation, treat all as schema
|
|
$errors = [];
|
|
// Save meta configuration
|
if (!empty($meta)) {
|
$metaResult = $config->updateMeta($type, $meta);
|
if (!$metaResult) {
|
$errors[] = 'meta';
|
}
|
}
|
|
// Save schema configuration
|
if (!empty($schema)) {
|
$schemaResult = $config->updateSchema($type, $schema);
|
if (!$schemaResult) {
|
$errors[] = 'schema';
|
}
|
}
|
|
if (!empty($errors)) {
|
return $this->validationError($errors);
|
}
|
|
// Invalidate cache
|
$this->cache->flush();
|
|
return $this->success(['status'=> 'completed', 'message' => 'Configuration saved successfully']);
|
}
|
|
/**
|
* Reset SEO configuration to defaults
|
*/
|
protected function resetSEO(WP_REST_Request $request): WP_REST_Response
|
{
|
$context = $request->get_param('context');
|
$data = $request->get_json_params();
|
|
$type = $data['type']??false;
|
$format = $data['resetMeta'] ? 'meta' : ($data['resetSchema'] ? 'schema' : ($data['resetArchive'] ? 'archive' : false));
|
$hasIt = $type && $format;
|
if ($hasIt) {
|
SchemaHelper::reset($type, $format);
|
}
|
|
// Invalidate cache
|
$this->cache->flush();
|
|
return $this->success([
|
'status' => 'completed',
|
'message' => 'Reset to defaults successfully',
|
'result' => $hasIt ? SchemaHelper::getConfig($type, $format) : [],
|
]);
|
}
|
|
/**
|
* Preview schema output
|
*/
|
protected function previewSchema(WP_REST_Request $request): WP_REST_Response
|
{
|
$data = $request->get_json_params();
|
//Whether it is the Schema, Meta, Archive, or Reference schema
|
$format = $data['format']??false;
|
if (!$format) {
|
return $this->error('Required field "Format" missing');
|
}
|
//WebSite, Organization, or any registered post type/taxonomy
|
$type = $data['type']??false;
|
if (!$type) {
|
return $this->error('Required field "Type" missing');
|
}
|
$class = $data['class']??false;
|
if (!$class) {
|
return $this->error('Required "Schema Class" field missing (Ex: Thing, LocalBusiness, etc)');
|
}
|
|
// Build schema with actual form values
|
$schema = [
|
'@type' => $class,
|
];
|
|
$theClass = SchemaHelper::classFromConfig($schema);
|
|
// Add fields with their actual values
|
foreach ($data['fields'] ?? [] as $fieldName => $value) {
|
if (empty($value)) {
|
continue;
|
}
|
|
$method = 'set'.ucfirst($fieldName);
|
if (!method_exists($theClass, $method)) {
|
error_log('Attempted field not found in class '.$class.': '.$fieldName);
|
continue;
|
}
|
$theClass->$method($value);
|
}
|
$schema = $theClass->outputSchema();
|
|
return $this->success([
|
'schema' => $schema
|
]);
|
}
|
|
/**
|
* Get fields for a schema type
|
* Used for dynamic type switching
|
* TODO
|
*/
|
public function getFields(WP_REST_Request $request): WP_REST_Response
|
{
|
$type = $request->get_param('type');
|
|
$class = SchemaHelper::classFromConfig(['type' => $type]);
|
|
// Get Meta field definitions from registry
|
$fields = $this->registry->getMetaConfigForType($type);
|
|
return $this->success($fields);
|
}
|
|
/**
|
* Validate context is a valid type
|
*/
|
protected function isValidContext(string $context): bool
|
{
|
// Site-wide contexts
|
if (in_array($context, ['site', 'business'])) {
|
return true;
|
}
|
|
// Check if it's a valid content/taxonomy/user type
|
return (bool)Registrar::getInstance($context);
|
}
|
}
|