namespace, '/' . $this->rest_base . '/suggestions', [ [ 'methods' => 'GET', 'callback' => [$this, 'get_suggestions'], 'permission_callback' => [$this, 'check_read_permissions'], 'args' => [ 'post_id' => [ 'required' => true, 'type' => 'integer', 'validate_callback' => function ($param, $request, $key) { return is_numeric($param); }, 'sanitize_callback' => 'absint', ], ], ] ] ); } /** * Get pillar content suggestions * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error Response object */ public function get_suggestions(WP_REST_Request $request) { $post_id = $request->get_param('post_id'); if (!$post_id) { return new WP_Error( 'missing_post_id', __('Post ID is required', 'thinkrank'), ['status' => 400] ); } $post_type = get_post_type($post_id); if (!$post_type) { return new WP_Error( 'invalid_post_id', __('Invalid Post ID', 'thinkrank'), ['status' => 400] ); } // Check global settings for this post type $all_settings = get_option('thinkrank_global_seo_settings', []); $link_suggestions_enabled = true; // Default to true if (isset($all_settings[$post_type]['link_suggestions'])) { $link_suggestions_enabled = (bool) $all_settings[$post_type]['link_suggestions']; } if (!$link_suggestions_enabled) { return new WP_REST_Response([], 200); } $taxonomies = get_object_taxonomies($post_type); $tax_query = []; $has_terms = false; if (!empty($taxonomies)) { $tax_query['relation'] = 'OR'; foreach ($taxonomies as $taxonomy) { $terms = get_the_terms($post_id, $taxonomy); if ($terms && !is_wp_error($terms)) { $term_ids = wp_list_pluck($terms, 'term_id'); if (!empty($term_ids)) { $tax_query[] = [ 'taxonomy' => $taxonomy, 'field' => 'term_id', 'terms' => $term_ids, ]; $has_terms = true; } } } } // Without this the query kept a tax_query holding nothing but // 'relation' => 'OR' whenever the post had no terms — $has_terms was // assigned and never read. if (!$has_terms) { $tax_query = []; } $args = [ 'post_type' => $post_type, 'posts_per_page' => 5, 'post__not_in' => [$post_id], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- admin-only endpoint listing pillar posts; there is no taxonomy to key this on. 'meta_query' => [ [ 'key' => '_thinkrank_pillar_content', 'value' => '1', 'compare' => '=', ] ], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- admin-only endpoint, optional filter. 'tax_query' => $tax_query, 'fields' => 'ids' ]; $query = new WP_Query($args); $suggestions = []; if ($query->have_posts()) { foreach ($query->posts as $p_id) { $suggestions[] = [ 'id' => $p_id, 'title' => get_the_title($p_id), 'permalink' => get_permalink($p_id), 'type' => get_post_type($p_id) ]; } } return new WP_REST_Response($suggestions, 200); } /** * Check if user can read posts * * @return bool */ public function check_read_permissions() { return current_user_can('edit_posts'); } }