diff --git a/includes/class-rest-post-controller.php b/includes/class-rest-post-controller.php index 6e6652b..b453aef 100644 --- a/includes/class-rest-post-controller.php +++ b/includes/class-rest-post-controller.php @@ -85,6 +85,7 @@ public function get_collection_params() { 'ancestors', 'next', 'prev', + 'seo', ), ), ), @@ -328,6 +329,10 @@ public static function get_item_schema() { 'description' => __( '(Optional by request) Previous data.' ), 'type' => array( 'object', 'null' ), ), + 'seo' => array( + 'description' => __( '(Optional by request) Yoast SEO meta data. Null when the Yoast SEO plugin is not active. The `schema` property is a JSON-LD string.' ), + 'type' => array( 'object', 'null' ), + ), ), ); @@ -442,6 +447,7 @@ public function get_additional_fields_for_response( $request ) { 'next', 'prev', 'depth', + 'seo', ); // Trim off outside whitespace from the comma delimited list. diff --git a/includes/class-rest-posts-controller.php b/includes/class-rest-posts-controller.php index b0f3c7b..29753c6 100644 --- a/includes/class-rest-posts-controller.php +++ b/includes/class-rest-posts-controller.php @@ -141,6 +141,7 @@ public function get_collection_params() { 'ancestors', 'next', 'prev', + 'seo', ), ), ), @@ -223,6 +224,7 @@ public function get_additional_fields_for_response( $request ) { 'ancestors', 'next', 'prev', + 'seo', ); // Trim off outside whitespace from the comma delimited list. diff --git a/includes/utils/class-post.php b/includes/utils/class-post.php index 68b0d65..bb41c32 100644 --- a/includes/utils/class-post.php +++ b/includes/utils/class-post.php @@ -72,6 +72,10 @@ public static function get_postdata( $post, $additional_fields = array(), $param } } + if ( in_array( 'seo', $additional_fields ) ) { + $data['seo'] = self::get_seodata( $post ); + } + // Inherit additional fields for siblings, parent, children, next, prev post. $inherit_fields = array_intersect( $additional_fields, @@ -206,6 +210,44 @@ function ( $sibling ) use ( $post ) { return $data; } + /** + * Get Yoast SEO meta data for a post. + * + * Uses the Yoast SEO surface API. Returns null when Yoast SEO is not active + * or has no meta for the post. + * + * @param \WP_Post $post Post object. + * + * @return array|null + */ + private static function get_seodata( $post ) { + if ( ! function_exists( 'YoastSEO' ) ) { + return null; + } + + $meta = YoastSEO()->meta->for_post( $post->ID ); + + if ( ! $meta ) { + return null; + } + + $json = json_decode( wp_json_encode( $meta->get_head()->json ), true ); + + if ( ! is_array( $json ) ) { + return null; + } + + // Ship schema as a JSON string so JSON-LD keys (@context, @graph) survive client-side key transforms. + if ( isset( $json['schema'] ) ) { + $json['schema'] = wp_json_encode( $json['schema'] ); + } + + // Keyed by human-readable labels, which key transforms would mangle. + unset( $json['twitter_misc'] ); + + return $json; + } + /** * Get the next/previous hierarchical post type (eg: Pages) *