cache_name = 'schema'; parent::__construct(); $this->registry = SchemaBuilder::getInstance(); } /** * Register REST routes */ public function registerRoutes(): void { // Main SEO endpoint - handles save, reset, preview register_rest_route($this->namespace, '/seo', [ [ 'methods' => 'POST', 'callback' => [$this, 'handleSEO'], 'permission_callback' => fn() => current_user_can('manage_options'), 'args' => [ 'action' => [ 'required' => false, 'type' => 'string', 'default' => 'save', 'enum' => ['save', 'reset', 'preview'] ], 'context' => [ 'required' => true, 'type' => 'string', 'description' => 'site, business, or content/taxonomy/user type' ] ] ] ]); // Get fields for a schema type (for dynamic type switching) register_rest_route($this->namespace, '/seo/fields', [ [ 'methods' => 'GET', 'callback' => [$this, 'getFields'], 'permission_callback' => fn() => current_user_can('manage_options'), 'args' => [ 'type' => [ 'required' => true, 'type' => 'string' ] ] ] ]); } /** * 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 = ConfigManager::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 new WP_REST_Response([ 'success' => true, 'status' => 'completed', 'message' => ucfirst($context) . ' settings saved successfully' ]); } /** * Save content/taxonomy/user type settings */ protected function saveTypeSettings(string $type, array $data): WP_REST_Response { $config = ConfigManager::for($type); // 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($meta); if (is_wp_error($metaResult)) { $errors['meta'] = [ 'message' => $metaResult->get_error_message(), 'errors' => $metaResult->get_error_data() ]; } } // Save schema configuration if (!empty($schema)) { $schemaResult = $config->updateConfig($schema); if (is_wp_error($schemaResult)) { $errors['schema'] = [ 'message' => $schemaResult->get_error_message(), 'errors' => $schemaResult->get_error_data() ]; } } if (!empty($errors)) { return $this->validationError($errors); } // Invalidate cache $this->cache->flush(); return new WP_REST_Response([ 'success' => true, '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(); $resetMeta = $data['resetMeta'] ?? false; $resetSchema = $data['resetSchema'] ?? false; $config = ConfigManager::for($context); // Reset based on what was requested if ($resetMeta) { $config->resetMeta(); } if ($resetSchema) { $config->resetConfig(); } if (!$resetMeta && !$resetSchema) { // Default: reset everything $config->resetAll(); } // Invalidate cache $this->cache->flush(); return new WP_REST_Response([ 'success' => true, 'status' => 'completed', 'message' => 'Reset to defaults successfully', 'meta' => $config->meta(), 'schema' => $config->schema() ]); } /** * Preview schema output */ protected function previewSchema(WP_REST_Request $request): WP_REST_Response { $data = $request->get_json_params(); $schemaType = $data['type'] ?? 'Thing'; // Get field definitions from registry $fieldDefinitions = $this->registry->getMetaConfigForType($schemaType); // Build schema with actual form values $schema = [ '@type' => $schemaType, '@id' => get_home_url() . '/#' . strtolower($schemaType), ]; // Add fields with their actual values foreach ($data['fields'] ?? [] as $fieldName => $value) { if (empty($value)) { continue; } $schema[$fieldName] = $value; } return new WP_REST_Response([ 'success' => true, 'schema' => $schema ]); } /** * Get fields for a schema type * Used for dynamic type switching */ public function getFields(WP_REST_Request $request): WP_REST_Response { $type = $request->get_param('type'); // Get MetaManager field definitions from registry $fields = $this->registry->getMetaConfigForType($type); return new WP_REST_Response($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 $this->checkContent($context, true); } }