# thinkrank/1.0.0/includes/api/class-seo-score-endpoint.php

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.0.0. 340 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.0.0/code/includes/api/class-seo-score-endpoint.php
- Raw: https://pluginprobe.com/plugins/thinkrank/1.0.0/raw/includes/api/class-seo-score-endpoint.php
- Modified: 2025-08-10T07:57:44+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.0.0/code/includes/api/class-seo-score-endpoint.php#L10-L20`.

```php
<?php
/**
 * SEO Score REST API Endpoint
 * 
 * Handles REST API endpoints for SEO score calculation and history
 * 
 * @package ThinkRank\API
 * @since 1.0.0
 */

declare(strict_types=1);

namespace ThinkRank\API;

use ThinkRank\AI\SEOScoreCalculator;
use ThinkRank\Core\Database;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * SEO Score Endpoint Class
 * 
 * Provides REST API endpoints for:
 * - /wp-json/thinkrank/v1/seo-score/calculate
 * - /wp-json/thinkrank/v1/seo-score/history
 * 
 * @since 1.0.0
 */
class SEOScoreEndpoint {
    
    /**
     * SEO Score Calculator instance
     *
     * @var SEOScoreCalculator
     */
    private SEOScoreCalculator $calculator;
    
    /**
     * Constructor
     *
     * @param SEOScoreCalculator $calculator SEO Score Calculator instance
     */
    public function __construct(SEOScoreCalculator $calculator) {
        $this->calculator = $calculator;
    }
    
    /**
     * Initialize the endpoint
     * 
     * @return void
     */
    public function init(): void {
        add_action('rest_api_init', [$this, 'register_routes']);
    }
    
    /**
     * Register REST API routes
     * 
     * @return void
     */
    public function register_routes(): void {
        // Calculate SEO score endpoint
        register_rest_route('thinkrank/v1', '/seo-score/calculate', [
            'methods' => 'POST',
            'callback' => [$this, 'calculate_score'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'post_id' => [
                    'required' => true,
                    'type' => 'integer',
                    'validate_callback' => [$this, 'validate_post_id'],
                ],
                'target_keyword' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'save_score' => [
                    'required' => false,
                    'type' => 'boolean',
                    'default' => true,
                ],
                'live_content' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'wp_kses_post',
                ],
            ],
        ]);

        // Get existing SEO score endpoint
        register_rest_route('thinkrank/v1', '/seo-score/get', [
            'methods' => 'GET',
            'callback' => [$this, 'get_existing_score'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'post_id' => [
                    'required' => true,
                    'type' => 'integer',
                    'validate_callback' => [$this, 'validate_post_id'],
                ],
            ],
        ]);
        
        // Get score history endpoint
        register_rest_route('thinkrank/v1', '/seo-score/history', [
            'methods' => 'GET',
            'callback' => [$this, 'get_score_history'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'post_id' => [
                    'required' => true,
                    'type' => 'integer',
                    'validate_callback' => [$this, 'validate_post_id'],
                ],
                'limit' => [
                    'required' => false,
                    'type' => 'integer',
                    'default' => 10,
                    'minimum' => 1,
                    'maximum' => 50,
                ],
            ],
        ]);
        
        // Get latest score endpoint
        register_rest_route('thinkrank/v1', '/seo-score/latest', [
            'methods' => 'GET',
            'callback' => [$this, 'get_latest_score'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'post_id' => [
                    'required' => true,
                    'type' => 'integer',
                    'validate_callback' => [$this, 'validate_post_id'],
                ],
            ],
        ]);
    }
    
    /**
     * Calculate SEO score for a post
     * 
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object
     */
    public function calculate_score(WP_REST_Request $request) {
        try {
            $post_id = $request->get_param('post_id');
            $target_keyword = $request->get_param('target_keyword') ?? '';
            $save_score = $request->get_param('save_score') ?? true;
            $live_content = $request->get_param('live_content') ?? '';

            // Analyze content - use live content if provided, otherwise saved content
            if (!empty($live_content)) {
                $content_data = $this->calculator->analyze_live_content($live_content, $post_id);
            } else {
                $content_data = $this->calculator->analyze_post_content($post_id);
            }
            
            if (empty($content_data)) {
                return new WP_Error(
                    'post_not_found',
                    'Post not found or has no content',
                    ['status' => 404]
                );
            }
            
            // Get post metadata
            $metadata = [
                'title' => get_post_meta($post_id, '_thinkrank_seo_title', true) ?: $content_data['title'],
                'description' => get_post_meta($post_id, '_thinkrank_meta_description', true) ?: '',
            ];
            
            // Calculate score
            $score_data = $this->calculator->calculate_score(
                $content_data,
                $metadata,
                ['target_keyword' => $target_keyword]
            );
            
            // Save score if requested
            if ($save_score) {
                $user_id = get_current_user_id();
                $score_id = $this->calculator->save_score($post_id, $user_id, $score_data);
                
                if ($score_id) {
                    $score_data['score_id'] = $score_id;
                }
            }
            
            return new WP_REST_Response([
                'success' => true,
                'data' => $score_data,
            ], 200);
            
        } catch (\Exception $e) {
            return new WP_Error(
                'calculation_failed',
                'Failed to calculate SEO score: ' . $e->getMessage(),
                ['status' => 500]
            );
        }
    }

    /**
     * Get existing SEO score for a post
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object
     */
    public function get_existing_score(WP_REST_Request $request) {
        try {
            $post_id = $request->get_param('post_id');

            // Get existing score data from database
            $existing_data = $this->calculator->get_existing_score_data($post_id);

            if ($existing_data) {
                return new WP_REST_Response([
                    'success' => true,
                    'data' => $existing_data,
                    'message' => __('Existing SEO score retrieved successfully', 'thinkrank')
                ], 200);
            } else {
                return new WP_REST_Response([
                    'success' => false,
                    'data' => null,
                    'message' => __('No existing SEO analysis found', 'thinkrank')
                ], 200); // 200 because it's not an error, just no data
            }

        } catch (\Exception $e) {
            return new WP_Error('get_score_error', $e->getMessage(), ['status' => 500]);
        }
    }

    /**
     * Get score history for a post
     * 
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object
     */
    public function get_score_history(WP_REST_Request $request) {
        try {
            $post_id = $request->get_param('post_id');
            $limit = $request->get_param('limit') ?? 10;
            
            $history = $this->calculator->get_score_history($post_id, $limit);
            
            return new WP_REST_Response([
                'success' => true,
                'data' => $history,
            ], 200);
            
        } catch (\Exception $e) {
            return new WP_Error(
                'history_failed',
                'Failed to retrieve score history: ' . $e->getMessage(),
                ['status' => 500]
            );
        }
    }
    
    /**
     * Get latest score for a post
     * 
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object
     */
    public function get_latest_score(WP_REST_Request $request) {
        try {
            $post_id = $request->get_param('post_id');
            
            $latest_score = $this->calculator->get_latest_score($post_id);
            
            return new WP_REST_Response([
                'success' => true,
                'data' => $latest_score,
            ], 200);
            
        } catch (\Exception $e) {
            return new WP_Error(
                'latest_failed',
                'Failed to retrieve latest score: ' . $e->getMessage(),
                ['status' => 500]
            );
        }
    }
    
    /**
     * Check permissions for API access
     * 
     * @param WP_REST_Request $request Request object
     * @return bool True if user has permission
     */
    public function check_permissions(WP_REST_Request $request): bool {
        // Check if user is logged in
        if (!is_user_logged_in()) {
            return false;
        }
        
        // Check if user can edit posts
        if (!current_user_can('edit_posts')) {
            return false;
        }
        
        // For specific post operations, check if user can edit the specific post
        $post_id = $request->get_param('post_id');
        if ($post_id && !current_user_can('edit_post', $post_id)) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Validate post ID parameter
     * 
     * @param mixed $value Parameter value
     * @param WP_REST_Request $request Request object
     * @param string $param Parameter name
     * @return bool True if valid
     */
    public function validate_post_id($value, WP_REST_Request $request, string $param): bool {
        if (!is_numeric($value) || $value <= 0) {
            return false;
        }
        
        $post = get_post((int) $value);
        return $post !== null;
    }
}

```
