I’ll build this as a strong inc/integrations/wordpress-seo.php integration file for an RX medical theme: safe guards, Yoast SEO support, schema, breadcrumbs, meta cleanup, sitemap controls, Open Graph/Twitter helpers, and performance-friendly hooks.
Yes friend — create this file:
inc/integrations/wordpress-seo.php
Then include it from your theme functions.php:
require_once get_template_directory() . '/inc/integrations/wordpress-seo.php';
This file adds advanced Yoast SEO integration for your RX medical theme: breadcrumbs, schema graph, medical article schema, Open Graph, Twitter image, sitemap image support, canonical cleanup, robots control, title/meta fallback, admin notices, body classes, and shortcode helpers. Yoast officially supports schema customization through filters like wpseo_schema_graph, breadcrumb implementation through yoast_breadcrumb(), and sitemap image filters such as wpseo_sitemap_urlimages. WordPress themes should also support title-tag via add_theme_support().
<?php
/**
* RX Theme - WordPress SEO / Yoast SEO Integration
*
* File: inc/integrations/wordpress-seo.php
*
* Purpose:
* - Deep Yoast SEO compatibility
* - Medical website schema improvements
* - Breadcrumb rendering helper
* - Open Graph / Twitter fallback images
* - XML sitemap image support
* - Canonical and robots control
* - SEO-safe archive / search / author handling
* - Medical Article / FAQ / HowTo schema helpers
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'RX_Theme_WordPress_SEO' ) ) :
final class RX_Theme_WordPress_SEO {
/**
* Singleton instance.
*
* @var RX_Theme_WordPress_SEO|null
*/
private static $instance = null;
/**
* Theme prefix.
*
* @var string
*/
private $prefix = 'rx_theme';
/**
* Get instance.
*
* @return RX_Theme_WordPress_SEO
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*/
private function __construct() {
$this->hooks();
}
/**
* Register hooks.
*
* @return void
*/
private function hooks() {
/**
* Theme support.
*/
add_action( 'after_setup_theme', array( $this, 'add_theme_supports' ), 5 );
/**
* Yoast presence classes / admin notice.
*/
add_filter( 'body_class', array( $this, 'body_classes' ) );
add_action( 'admin_notices', array( $this, 'yoast_missing_notice' ) );
/**
* Breadcrumbs.
*/
add_filter( 'wpseo_breadcrumb_links', array( $this, 'breadcrumb_links' ) );
add_filter( 'wpseo_breadcrumb_single_link', array( $this, 'breadcrumb_single_link' ), 10, 2 );
add_filter( 'wpseo_breadcrumb_output_wrapper', array( $this, 'breadcrumb_wrapper' ) );
add_filter( 'wpseo_breadcrumb_output_class', array( $this, 'breadcrumb_class' ) );
/**
* Titles and meta.
*/
add_filter( 'wpseo_title', array( $this, 'seo_title' ) );
add_filter( 'wpseo_metadesc', array( $this, 'meta_description' ) );
add_filter( 'wpseo_canonical', array( $this, 'canonical_url' ) );
add_filter( 'wpseo_robots', array( $this, 'robots_rules' ) );
/**
* Open Graph.
*/
add_filter( 'wpseo_opengraph_title', array( $this, 'seo_title' ) );
add_filter( 'wpseo_opengraph_desc', array( $this, 'meta_description' ) );
add_filter( 'wpseo_opengraph_image', array( $this, 'opengraph_image' ) );
add_filter( 'wpseo_opengraph_url', array( $this, 'canonical_url' ) );
add_filter( 'wpseo_og_locale', array( $this, 'og_locale' ) );
/**
* Twitter.
*/
add_filter( 'wpseo_twitter_title', array( $this, 'seo_title' ) );
add_filter( 'wpseo_twitter_description', array( $this, 'meta_description' ) );
add_filter( 'wpseo_twitter_image', array( $this, 'twitter_image' ) );
add_filter( 'wpseo_twitter_card_type', array( $this, 'twitter_card_type' ) );
/**
* Schema.
*/
add_filter( 'wpseo_schema_graph', array( $this, 'schema_graph' ), 20, 2 );
add_filter( 'wpseo_schema_webpage', array( $this, 'schema_webpage' ) );
add_filter( 'wpseo_schema_article', array( $this, 'schema_article' ) );
add_filter( 'wpseo_schema_organization', array( $this, 'schema_organization' ) );
add_filter( 'wpseo_schema_person', array( $this, 'schema_person' ) );
add_filter( 'wpseo_schema_needs_breadcrumb', '__return_true' );
/**
* XML sitemap.
*/
add_filter( 'wpseo_sitemap_urlimages', array( $this, 'sitemap_post_images' ), 10, 2 );
add_filter( 'wpseo_sitemap_urlimages_term', array( $this, 'sitemap_term_images' ), 10, 2 );
add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', array( $this, 'exclude_posts_from_sitemap' ) );
add_filter( 'wpseo_sitemap_exclude_taxonomy', array( $this, 'exclude_taxonomy_from_sitemap' ), 10, 2 );
add_filter( 'wpseo_sitemap_exclude_post_type', array( $this, 'exclude_post_type_from_sitemap' ), 10, 2 );
/**
* Head cleanup / duplicate prevention.
*/
add_action( 'wp', array( $this, 'remove_duplicate_theme_meta' ), 20 );
/**
* Shortcodes.
*/
add_shortcode( 'rx_breadcrumbs', array( $this, 'breadcrumb_shortcode' ) );
add_shortcode( 'rx_seo_title', array( $this, 'seo_title_shortcode' ) );
add_shortcode( 'rx_meta_description', array( $this, 'meta_description_shortcode' ) );
}
/**
* Add theme supports.
*
* @return void
*/
public function add_theme_supports() {
add_theme_support( 'title-tag' );
add_theme_support( 'custom-logo' );
add_theme_support( 'post-thumbnails' );
/**
* Tell Yoast that theme supports breadcrumbs.
*/
add_theme_support( 'yoast-seo-breadcrumbs' );
}
/**
* Check if Yoast SEO is active.
*
* @return bool
*/
public function is_yoast_active() {
return defined( 'WPSEO_VERSION' ) || class_exists( 'WPSEO_Options' ) || function_exists( 'yoast_breadcrumb' );
}
/**
* Add body classes.
*
* @param array $classes Body classes.
* @return array
*/
public function body_classes( $classes ) {
$classes[] = $this->is_yoast_active() ? 'rx-yoast-active' : 'rx-yoast-inactive';
if ( is_singular() ) {
$classes[] = 'rx-seo-singular';
}
if ( is_singular( 'post' ) ) {
$classes[] = 'rx-seo-article';
}
if ( is_category() || is_tag() || is_tax() ) {
$classes[] = 'rx-seo-taxonomy-archive';
}
return array_unique( $classes );
}
/**
* Admin notice if Yoast is missing.
*
* @return void
*/
public function yoast_missing_notice() {
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
if ( $this->is_yoast_active() ) {
return;
}
$screen = get_current_screen();
if ( ! $screen || 'themes' !== $screen->base ) {
return;
}
echo '<div class="notice notice-info is-dismissible">';
echo '<p><strong>RX Theme SEO:</strong> For full SEO, breadcrumb, schema, Open Graph, and sitemap features, install and activate Yoast SEO.</p>';
echo '</div>';
}
/**
* Remove duplicate theme meta when Yoast controls SEO.
*
* @return void
*/
public function remove_duplicate_theme_meta() {
if ( ! $this->is_yoast_active() ) {
return;
}
remove_action( 'wp_head', 'rel_canonical' );
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
}
/**
* Render breadcrumbs.
*
* Use in template:
* RX_Theme_WordPress_SEO::instance()->breadcrumbs();
*
* @param bool $echo Echo output.
* @return string|null
*/
public function breadcrumbs( $echo = true ) {
if ( ! function_exists( 'yoast_breadcrumb' ) ) {
return null;
}
$output = yoast_breadcrumb(
'<nav class="rx-breadcrumbs" aria-label="' . esc_attr__( 'Breadcrumb', 'rx-theme' ) . '">',
'</nav>',
false
);
if ( $echo ) {
echo wp_kses_post( $output );
return null;
}
return $output;
}
/**
* Breadcrumb shortcode.
*
* @return string
*/
public function breadcrumb_shortcode() {
return (string) $this->breadcrumbs( false );
}
/**
* Customize breadcrumb links.
*
* @param array $links Breadcrumb links.
* @return array
*/
public function breadcrumb_links( $links ) {
if ( empty( $links ) || ! is_array( $links ) ) {
return $links;
}
/**
* Add Blog crumb for single posts.
*/
if ( is_singular( 'post' ) ) {
$blog_page_id = (int) get_option( 'page_for_posts' );
if ( $blog_page_id ) {
$blog_crumb = array(
'url' => get_permalink( $blog_page_id ),
'text' => get_the_title( $blog_page_id ),
);
array_splice( $links, 1, 0, array( $blog_crumb ) );
}
}
/**
* Add medical library crumb for health articles.
*/
if ( is_singular( 'post' ) && has_category( array( 'medical', 'health', 'disease', 'treatment' ) ) ) {
$medical_url = home_url( '/medical-library/' );
$medical_crumb = array(
'url' => $medical_url,
'text' => __( 'Medical Library', 'rx-theme' ),
);
array_splice( $links, 1, 0, array( $medical_crumb ) );
}
return $links;
}
/**
* Customize a single breadcrumb link HTML.
*
* @param string $link_output Link HTML.
* @param array $link Link data.
* @return string
*/
public function breadcrumb_single_link( $link_output, $link ) {
if ( empty( $link_output ) ) {
return $link_output;
}
$link_output = str_replace( '<a ', '<a class="rx-breadcrumb-link" ', $link_output );
$link_output = str_replace( '<span ', '<span class="rx-breadcrumb-current" ', $link_output );
return $link_output;
}
/**
* Breadcrumb wrapper element.
*
* @return string
*/
public function breadcrumb_wrapper() {
return 'nav';
}
/**
* Breadcrumb class.
*
* @return string
*/
public function breadcrumb_class() {
return 'rx-breadcrumbs yoast-breadcrumbs';
}
/**
* SEO title fallback / enhancement.
*
* @param string $title SEO title.
* @return string
*/
public function seo_title( $title ) {
if ( ! empty( $title ) ) {
return $title;
}
if ( is_singular() ) {
$title = single_post_title( '', false );
} elseif ( is_category() || is_tag() || is_tax() ) {
$title = single_term_title( '', false );
} elseif ( is_search() ) {
$title = sprintf(
/* translators: %s search query */
__( 'Search results for %s', 'rx-theme' ),
get_search_query()
);
} elseif ( is_404() ) {
$title = __( 'Page Not Found', 'rx-theme' );
} else {
$title = get_bloginfo( 'name' );
}
$site_name = get_bloginfo( 'name' );
if ( $site_name && false === stripos( $title, $site_name ) ) {
$title .= ' - ' . $site_name;
}
return wp_strip_all_tags( $title );
}
/**
* SEO title shortcode.
*
* @return string
*/
public function seo_title_shortcode() {
return esc_html( $this->seo_title( '' ) );
}
/**
* Meta description fallback.
*
* @param string $description Meta description.
* @return string
*/
public function meta_description( $description ) {
if ( ! empty( $description ) ) {
return $description;
}
if ( is_singular() ) {
$post_id = get_the_ID();
$manual = get_post_meta( $post_id, '_rx_meta_description', true );
if ( $manual ) {
return $this->trim_description( $manual );
}
$excerpt = get_the_excerpt( $post_id );
if ( $excerpt ) {
return $this->trim_description( $excerpt );
}
$content = get_post_field( 'post_content', $post_id );
return $this->trim_description( $content );
}
if ( is_category() || is_tag() || is_tax() ) {
$term = get_queried_object();
if ( ! empty( $term->description ) ) {
return $this->trim_description( $term->description );
}
}
if ( is_search() ) {
return sprintf(
/* translators: %s search query */
__( 'Find helpful medical and health information about %s on %s.', 'rx-theme' ),
get_search_query(),
get_bloginfo( 'name' )
);
}
return $this->trim_description( get_bloginfo( 'description' ) );
}
/**
* Meta description shortcode.
*
* @return string
*/
public function meta_description_shortcode() {
return esc_html( $this->meta_description( '' ) );
}
/**
* Trim description.
*
* @param string $text Text.
* @param int $limit Character limit.
* @return string
*/
private function trim_description( $text, $limit = 160 ) {
$text = wp_strip_all_tags( strip_shortcodes( (string) $text ) );
$text = preg_replace( '/\s+/', ' ', $text );
$text = trim( $text );
if ( mb_strlen( $text ) > $limit ) {
$text = mb_substr( $text, 0, $limit - 3 ) . '...';
}
return $text;
}
/**
* Canonical URL.
*
* @param string $canonical Canonical URL.
* @return string
*/
public function canonical_url( $canonical ) {
if ( is_singular() ) {
$custom = get_post_meta( get_the_ID(), '_rx_canonical_url', true );
if ( $custom && wp_http_validate_url( $custom ) ) {
return esc_url_raw( $custom );
}
}
if ( is_paged() ) {
return $canonical;
}
if ( is_search() ) {
return home_url( '/' );
}
return $canonical;
}
/**
* Robots rules.
*
* @param string $robots Robots string.
* @return string
*/
public function robots_rules( $robots ) {
if ( is_search() || is_404() ) {
return 'noindex,follow';
}
if ( is_author() && ! $this->author_has_posts() ) {
return 'noindex,follow';
}
if ( is_paged() && apply_filters( 'rx_theme_noindex_paged_archives', false ) ) {
return 'noindex,follow';
}
if ( is_singular() ) {
$noindex = get_post_meta( get_the_ID(), '_rx_noindex', true );
if ( '1' === (string) $noindex ) {
return 'noindex,follow';
}
}
return $robots;
}
/**
* Check author posts.
*
* @return bool
*/
private function author_has_posts() {
$author = get_queried_object();
if ( empty( $author->ID ) ) {
return false;
}
return count_user_posts( (int) $author->ID, 'post', true ) > 0;
}
/**
* Open Graph image.
*
* @param string $image Image URL.
* @return string
*/
public function opengraph_image( $image ) {
if ( ! empty( $image ) ) {
return $image;
}
return $this->get_social_image();
}
/**
* Twitter image.
*
* @param string $image Image URL.
* @return string
*/
public function twitter_image( $image ) {
if ( ! empty( $image ) ) {
return $image;
}
return $this->get_social_image();
}
/**
* Twitter card type.
*
* @param string $type Card type.
* @return string
*/
public function twitter_card_type( $type ) {
return 'summary_large_image';
}
/**
* OG locale.
*
* @param string $locale Locale.
* @return string
*/
public function og_locale( $locale ) {
$wp_locale = get_locale();
if ( $wp_locale ) {
return str_replace( '-', '_', $wp_locale );
}
return $locale;
}
/**
* Get social image fallback.
*
* @return string
*/
private function get_social_image() {
if ( is_singular() ) {
$post_id = get_the_ID();
$custom = get_post_meta( $post_id, '_rx_social_image', true );
if ( $custom && wp_http_validate_url( $custom ) ) {
return esc_url_raw( $custom );
}
if ( has_post_thumbnail( $post_id ) ) {
$image = wp_get_attachment_image_url( get_post_thumbnail_id( $post_id ), 'full' );
if ( $image ) {
return esc_url_raw( $image );
}
}
}
$custom_logo_id = get_theme_mod( 'custom_logo' );
if ( $custom_logo_id ) {
$logo = wp_get_attachment_image_url( $custom_logo_id, 'full' );
if ( $logo ) {
return esc_url_raw( $logo );
}
}
$fallback = get_template_directory_uri() . '/assets/images/social-default.jpg';
return esc_url_raw( apply_filters( 'rx_theme_default_social_image', $fallback ) );
}
/**
* Modify Yoast schema graph.
*
* @param array $graph Schema graph.
* @param mixed $context Yoast context.
* @return array
*/
public function schema_graph( $graph, $context ) {
if ( ! is_array( $graph ) ) {
return $graph;
}
$graph[] = $this->website_search_schema();
if ( is_singular( 'post' ) ) {
$graph[] = $this->medical_article_schema();
$graph[] = $this->speakable_schema();
}
if ( is_front_page() || is_home() ) {
$graph[] = $this->medical_organization_schema();
}
if ( is_author() ) {
$graph[] = $this->author_profile_schema();
}
$faq_schema = $this->faq_schema_from_post();
if ( $faq_schema ) {
$graph[] = $faq_schema;
}
$howto_schema = $this->howto_schema_from_post();
if ( $howto_schema ) {
$graph[] = $howto_schema;
}
return $graph;
}
/**
* WebPage schema.
*
* @param array $data Schema data.
* @return array
*/
public function schema_webpage( $data ) {
if ( ! is_array( $data ) ) {
return $data;
}
if ( is_singular() ) {
$data['dateModified'] = get_the_modified_date( DATE_W3C );
$data['datePublished'] = get_the_date( DATE_W3C );
}
if ( is_search() ) {
$data['@type'] = 'SearchResultsPage';
}
if ( is_404() ) {
$data['@type'] = 'WebPage';
$data['name'] = __( 'Page Not Found', 'rx-theme' );
}
return $data;
}
/**
* Article schema.
*
* @param array $data Article schema.
* @return array
*/
public function schema_article( $data ) {
if ( ! is_singular( 'post' ) || ! is_array( $data ) ) {
return $data;
}
$post_id = get_the_ID();
$is_medical = has_category( array( 'medical', 'health', 'disease', 'treatment', 'medicine' ), $post_id );
if ( $is_medical ) {
$data['@type'] = array( 'Article', 'MedicalWebPage', 'MedicalScholarlyArticle' );
}
$data['headline'] = get_the_title( $post_id );
$data['datePublished'] = get_the_date( DATE_W3C, $post_id );
$data['dateModified'] = get_the_modified_date( DATE_W3C, $post_id );
$data['wordCount'] = str_word_count( wp_strip_all_tags( get_post_field( 'post_content', $post_id ) ) );
$reviewed_by = get_post_meta( $post_id, '_rx_medical_reviewed_by', true );
if ( $reviewed_by ) {
$data['reviewedBy'] = array(
'@type' => 'Person',
'name' => sanitize_text_field( $reviewed_by ),
);
}
$specialty = get_post_meta( $post_id, '_rx_medical_specialty', true );
if ( $specialty ) {
$data['medicalSpecialty'] = sanitize_text_field( $specialty );
}
return $data;
}
/**
* Organization schema.
*
* @param array $data Organization schema.
* @return array
*/
public function schema_organization( $data ) {
if ( ! is_array( $data ) ) {
return $data;
}
$data['@type'] = array( 'Organization', 'MedicalOrganization' );
$data['name'] = get_bloginfo( 'name' );
$data['url'] = home_url( '/' );
$logo = $this->get_social_image();
if ( $logo ) {
$data['logo'] = array(
'@type' => 'ImageObject',
'url' => $logo,
);
}
$same_as = apply_filters(
'rx_theme_organization_same_as',
array()
);
if ( ! empty( $same_as ) && is_array( $same_as ) ) {
$data['sameAs'] = array_values( array_filter( array_map( 'esc_url_raw', $same_as ) ) );
}
return $data;
}
/**
* Person schema.
*
* @param array $data Person schema.
* @return array
*/
public function schema_person( $data ) {
if ( ! is_array( $data ) ) {
return $data;
}
if ( is_author() ) {
$author = get_queried_object();
if ( ! empty( $author->ID ) ) {
$data['name'] = get_the_author_meta( 'display_name', $author->ID );
$data['description'] = get_the_author_meta( 'description', $author->ID );
$data['url'] = get_author_posts_url( $author->ID );
}
}
return $data;
}
/**
* Website search schema.
*
* @return array
*/
private function website_search_schema() {
return array(
'@type' => 'WebSite',
'@id' => home_url( '/#website-search' ),
'url' => home_url( '/' ),
'name' => get_bloginfo( 'name' ),
'potentialAction' => array(
'@type' => 'SearchAction',
'target' => home_url( '/?s={search_term_string}' ),
'query-input' => 'required name=search_term_string',
),
);
}
/**
* Medical organization schema.
*
* @return array
*/
private function medical_organization_schema() {
return array(
'@type' => 'MedicalOrganization',
'@id' => home_url( '/#medical-organization' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
'logo' => array(
'@type' => 'ImageObject',
'url' => $this->get_social_image(),
),
);
}
/**
* Medical article schema.
*
* @return array
*/
private function medical_article_schema() {
$post_id = get_the_ID();
$schema = array(
'@type' => 'MedicalWebPage',
'@id' => get_permalink( $post_id ) . '#medical-webpage',
'url' => get_permalink( $post_id ),
'name' => get_the_title( $post_id ),
'description' => $this->meta_description( '' ),
'datePublished' => get_the_date( DATE_W3C, $post_id ),
'dateModified' => get_the_modified_date( DATE_W3C, $post_id ),
'lastReviewed' => get_the_modified_date( DATE_W3C, $post_id ),
'medicalAudience' => array(
'@type' => 'MedicalAudience',
'name' => 'Patient',
),
'about' => array(),
);
$condition = get_post_meta( $post_id, '_rx_medical_condition', true );
if ( $condition ) {
$schema['about'][] = array(
'@type' => 'MedicalCondition',
'name' => sanitize_text_field( $condition ),
);
}
$specialty = get_post_meta( $post_id, '_rx_medical_specialty', true );
if ( $specialty ) {
$schema['specialty'] = sanitize_text_field( $specialty );
}
return $schema;
}
/**
* Speakable schema.
*
* @return array
*/
private function speakable_schema() {
$post_id = get_the_ID();
return array(
'@type' => 'SpeakableSpecification',
'@id' => get_permalink( $post_id ) . '#speakable',
'cssSelector' => array(
'.entry-title',
'.entry-content p:first-of-type',
),
);
}
/**
* Author profile schema.
*
* @return array
*/
private function author_profile_schema() {
$author = get_queried_object();
if ( empty( $author->ID ) ) {
return array();
}
return array(
'@type' => 'ProfilePage',
'@id' => get_author_posts_url( $author->ID ) . '#profile',
'name' => get_the_author_meta( 'display_name', $author->ID ),
'description' => get_the_author_meta( 'description', $author->ID ),
'url' => get_author_posts_url( $author->ID ),
);
}
/**
* FAQ schema from post meta.
*
* Expected meta:
* _rx_faq_items = array(
* array( 'question' => '...', 'answer' => '...' )
* )
*
* @return array|null
*/
private function faq_schema_from_post() {
if ( ! is_singular() ) {
return null;
}
$post_id = get_the_ID();
$items = get_post_meta( $post_id, '_rx_faq_items', true );
if ( empty( $items ) || ! is_array( $items ) ) {
return null;
}
$main_entity = array();
foreach ( $items as $item ) {
if ( empty( $item['question'] ) || empty( $item['answer'] ) ) {
continue;
}
$main_entity[] = array(
'@type' => 'Question',
'name' => wp_strip_all_tags( $item['question'] ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => wp_kses_post( $item['answer'] ),
),
);
}
if ( empty( $main_entity ) ) {
return null;
}
return array(
'@type' => 'FAQPage',
'@id' => get_permalink( $post_id ) . '#faq',
'mainEntity' => $main_entity,
);
}
/**
* HowTo schema from post meta.
*
* Expected meta:
* _rx_howto_steps = array(
* array( 'name' => 'Step title', 'text' => 'Step detail' )
* )
*
* @return array|null
*/
private function howto_schema_from_post() {
if ( ! is_singular() ) {
return null;
}
$post_id = get_the_ID();
$steps = get_post_meta( $post_id, '_rx_howto_steps', true );
if ( empty( $steps ) || ! is_array( $steps ) ) {
return null;
}
$schema_steps = array();
foreach ( $steps as $step ) {
if ( empty( $step['name'] ) || empty( $step['text'] ) ) {
continue;
}
$schema_steps[] = array(
'@type' => 'HowToStep',
'name' => wp_strip_all_tags( $step['name'] ),
'text' => wp_strip_all_tags( $step['text'] ),
);
}
if ( empty( $schema_steps ) ) {
return null;
}
return array(
'@type' => 'HowTo',
'@id' => get_permalink( $post_id ) . '#howto',
'name' => get_the_title( $post_id ),
'step' => $schema_steps,
);
}
/**
* Add images to Yoast post sitemap.
*
* @param array $images Existing images.
* @param int $post_id Post ID.
* @return array
*/
public function sitemap_post_images( $images, $post_id ) {
$post_id = absint( $post_id );
if ( ! $post_id ) {
return $images;
}
if ( has_post_thumbnail( $post_id ) ) {
$image_url = wp_get_attachment_image_url( get_post_thumbnail_id( $post_id ), 'full' );
if ( $image_url ) {
$images[] = array(
'src' => esc_url_raw( $image_url ),
'title' => get_the_title( $post_id ),
'alt' => get_post_meta( get_post_thumbnail_id( $post_id ), '_wp_attachment_image_alt', true ),
);
}
}
$content = get_post_field( 'post_content', $post_id );
if ( $content ) {
preg_match_all( '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $content, $matches );
if ( ! empty( $matches[1] ) ) {
foreach ( $matches[1] as $src ) {
$images[] = array(
'src' => esc_url_raw( $src ),
);
}
}
}
return $this->unique_sitemap_images( $images );
}
/**
* Add images to Yoast term sitemap.
*
* @param array $images Existing images.
* @param object $term Term object.
* @return array
*/
public function sitemap_term_images( $images, $term ) {
if ( empty( $term->term_id ) ) {
return $images;
}
$image_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
if ( $image_id ) {
$image_url = wp_get_attachment_image_url( $image_id, 'full' );
if ( $image_url ) {
$images[] = array(
'src' => esc_url_raw( $image_url ),
'title' => single_term_title( '', false ),
);
}
}
$rx_term_image = get_term_meta( $term->term_id, '_rx_term_image', true );
if ( $rx_term_image && wp_http_validate_url( $rx_term_image ) ) {
$images[] = array(
'src' => esc_url_raw( $rx_term_image ),
'title' => single_term_title( '', false ),
);
}
return $this->unique_sitemap_images( $images );
}
/**
* Remove duplicate sitemap images.
*
* @param array $images Images.
* @return array
*/
private function unique_sitemap_images( $images ) {
if ( empty( $images ) || ! is_array( $images ) ) {
return array();
}
$seen = array();
$unique = array();
foreach ( $images as $image ) {
if ( empty( $image['src'] ) ) {
continue;
}
$key = md5( $image['src'] );
if ( isset( $seen[ $key ] ) ) {
continue;
}
$seen[ $key ] = true;
$unique[] = $image;
}
return $unique;
}
/**
* Exclude posts from sitemap by post meta.
*
* @param array $post_ids Post IDs.
* @return array
*/
public function exclude_posts_from_sitemap( $post_ids ) {
$query = new WP_Query(
array(
'post_type' => 'any',
'post_status' => 'publish',
'fields' => 'ids',
'posts_per_page' => 500,
'meta_query' => array(
array(
'key' => '_rx_exclude_from_sitemap',
'value' => '1',
),
),
'no_found_rows' => true,
)
);
if ( ! empty( $query->posts ) ) {
$post_ids = array_merge( $post_ids, $query->posts );
}
return array_unique( array_map( 'absint', $post_ids ) );
}
/**
* Exclude taxonomies from sitemap.
*
* @param bool $exclude Whether to exclude.
* @param string $taxonomy Taxonomy name.
* @return bool
*/
public function exclude_taxonomy_from_sitemap( $exclude, $taxonomy ) {
$excluded = apply_filters(
'rx_theme_excluded_sitemap_taxonomies',
array( 'post_format' )
);
if ( in_array( $taxonomy, $excluded, true ) ) {
return true;
}
return $exclude;
}
/**
* Exclude post types from sitemap.
*
* @param bool $exclude Whether to exclude.
* @param string $post_type Post type.
* @return bool
*/
public function exclude_post_type_from_sitemap( $exclude, $post_type ) {
$excluded = apply_filters(
'rx_theme_excluded_sitemap_post_types',
array( 'attachment' )
);
if ( in_array( $post_type, $excluded, true ) ) {
return true;
}
return $exclude;
}
}
endif;
/**
* Start integration.
*/
add_action(
'after_setup_theme',
function() {
RX_Theme_WordPress_SEO::instance();
},
1
);
/**
* Template helper function.
*
* Use:
* rx_theme_breadcrumbs();
*
* @return void
*/
if ( ! function_exists( 'rx_theme_breadcrumbs' ) ) {
function rx_theme_breadcrumbs() {
RX_Theme_WordPress_SEO::instance()->breadcrumbs( true );
}
}
/**
* Template helper: get SEO title.
*
* @return string
*/
if ( ! function_exists( 'rx_theme_get_seo_title' ) ) {
function rx_theme_get_seo_title() {
return RX_Theme_WordPress_SEO::instance()->seo_title( '' );
}
}
/**
* Template helper: get meta description.
*
* @return string
*/
if ( ! function_exists( 'rx_theme_get_meta_description' ) ) {
function rx_theme_get_meta_description() {
return RX_Theme_WordPress_SEO::instance()->meta_description( '' );
}
}
To show breadcrumbs in your theme template, add this where you want breadcrumb output, for example in header.php, single.php, or page.php:
<?php
if ( function_exists( 'rx_theme_breadcrumbs' ) ) {
rx_theme_breadcrumbs();
}
?>
You can also use this shortcode inside post/page content:
[rx_breadcrumbs]
This is a strong “integration file.” For even bigger RX SEO power, the next best companion file would be:
inc/integrations/schema-medical.php for custom disease, drug, diagnostic test, FAQ, treatment, and author-review schema.