I’ll build this as a production-style WordPress taxonomy file for your RX theme: registration, admin columns, SEO fields, REST support, rewrite rules, helpers, breadcrumbs support, archive tuning, and safe fallbacks.
Below is an advanced production-level inc/taxonomies/medical-category.php file for your RX theme. It registers a powerful Medical Category taxonomy with admin fields, SEO metadata, archive behavior, REST API support, term ordering, featured categories, custom admin columns, schema-friendly data, and helper functions.
Create this file:
rx-theme/
└── inc/
└── taxonomies/
└── medical-category.php
Then paste this full code:
<?php
/**
* RX Theme - Medical Category Taxonomy
*
* File: inc/taxonomies/medical-category.php
*
* Features:
* - Registers medical_category taxonomy
* - Supports multiple post types
* - Hierarchical category structure
* - Custom rewrite slug
* - REST API enabled
* - Admin term fields:
* - SEO title
* - SEO description
* - Short description
* - Featured image URL
* - Icon class
* - Color
* - Display order
* - Featured category
* - Noindex option
* - Schema type
* - Admin columns
* - Frontend body classes
* - Archive title/description improvements
* - Query ordering support
* - Helper functions
* - Shortcodes
* - JSON-LD schema helper
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'RX_Medical_Category_Taxonomy' ) ) :
final class RX_Medical_Category_Taxonomy {
/**
* Taxonomy key.
*/
const TAXONOMY = 'medical_category';
/**
* Option/meta keys.
*/
const META_SEO_TITLE = 'rx_medical_cat_seo_title';
const META_SEO_DESCRIPTION = 'rx_medical_cat_seo_description';
const META_SHORT_DESCRIPTION = 'rx_medical_cat_short_description';
const META_IMAGE = 'rx_medical_cat_image';
const META_ICON = 'rx_medical_cat_icon';
const META_COLOR = 'rx_medical_cat_color';
const META_ORDER = 'rx_medical_cat_order';
const META_FEATURED = 'rx_medical_cat_featured';
const META_NOINDEX = 'rx_medical_cat_noindex';
const META_SCHEMA_TYPE = 'rx_medical_cat_schema_type';
/**
* Initialize hooks.
*/
public static function init() {
add_action( 'init', array( __CLASS__, 'register_taxonomy' ), 5 );
add_action( self::TAXONOMY . '_add_form_fields', array( __CLASS__, 'add_term_fields' ) );
add_action( self::TAXONOMY . '_edit_form_fields', array( __CLASS__, 'edit_term_fields' ), 10, 2 );
add_action( 'created_' . self::TAXONOMY, array( __CLASS__, 'save_term_fields' ) );
add_action( 'edited_' . self::TAXONOMY, array( __CLASS__, 'save_term_fields' ) );
add_filter( 'manage_edit-' . self::TAXONOMY . '_columns', array( __CLASS__, 'admin_columns' ) );
add_filter( 'manage_' . self::TAXONOMY . '_custom_column', array( __CLASS__, 'admin_column_content' ), 10, 3 );
add_filter( 'manage_edit-' . self::TAXONOMY . '_sortable_columns', array( __CLASS__, 'sortable_columns' ) );
add_action( 'pre_get_posts', array( __CLASS__, 'archive_query' ) );
add_filter( 'body_class', array( __CLASS__, 'body_classes' ) );
add_filter( 'get_the_archive_title', array( __CLASS__, 'archive_title' ) );
add_filter( 'get_the_archive_description', array( __CLASS__, 'archive_description' ) );
add_action( 'wp_head', array( __CLASS__, 'frontend_meta_tags' ), 2 );
add_action( 'wp_head', array( __CLASS__, 'json_ld_schema' ), 30 );
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_fields' ) );
add_shortcode( 'rx_medical_categories', array( __CLASS__, 'shortcode_medical_categories' ) );
add_shortcode( 'rx_medical_category_children', array( __CLASS__, 'shortcode_medical_category_children' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_assets' ) );
}
/**
* Register taxonomy.
*/
public static function register_taxonomy() {
$post_types = self::get_supported_post_types();
$labels = array(
'name' => esc_html__( 'Medical Categories', 'rx-theme' ),
'singular_name' => esc_html__( 'Medical Category', 'rx-theme' ),
'search_items' => esc_html__( 'Search Medical Categories', 'rx-theme' ),
'popular_items' => esc_html__( 'Popular Medical Categories', 'rx-theme' ),
'all_items' => esc_html__( 'All Medical Categories', 'rx-theme' ),
'parent_item' => esc_html__( 'Parent Medical Category', 'rx-theme' ),
'parent_item_colon' => esc_html__( 'Parent Medical Category:', 'rx-theme' ),
'edit_item' => esc_html__( 'Edit Medical Category', 'rx-theme' ),
'view_item' => esc_html__( 'View Medical Category', 'rx-theme' ),
'update_item' => esc_html__( 'Update Medical Category', 'rx-theme' ),
'add_new_item' => esc_html__( 'Add New Medical Category', 'rx-theme' ),
'new_item_name' => esc_html__( 'New Medical Category Name', 'rx-theme' ),
'separate_items_with_commas' => esc_html__( 'Separate medical categories with commas', 'rx-theme' ),
'add_or_remove_items' => esc_html__( 'Add or remove medical categories', 'rx-theme' ),
'choose_from_most_used' => esc_html__( 'Choose from the most used medical categories', 'rx-theme' ),
'not_found' => esc_html__( 'No medical categories found.', 'rx-theme' ),
'no_terms' => esc_html__( 'No medical categories', 'rx-theme' ),
'filter_by_item' => esc_html__( 'Filter by medical category', 'rx-theme' ),
'items_list_navigation' => esc_html__( 'Medical categories list navigation', 'rx-theme' ),
'items_list' => esc_html__( 'Medical categories list', 'rx-theme' ),
'back_to_items' => esc_html__( 'Back to medical categories', 'rx-theme' ),
'menu_name' => esc_html__( 'Medical Categories', 'rx-theme' ),
'name_admin_bar' => esc_html__( 'Medical Category', 'rx-theme' ),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'hierarchical' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => false,
'show_in_quick_edit' => true,
'show_admin_column' => true,
'show_in_rest' => true,
'rest_base' => 'medical-categories',
'rest_controller_class' => 'WP_REST_Terms_Controller',
'query_var' => self::TAXONOMY,
'update_count_callback' => '_update_post_term_count',
'rewrite' => array(
'slug' => 'medical-category',
'with_front' => false,
'hierarchical' => true,
'ep_mask' => EP_NONE,
),
'capabilities' => array(
'manage_terms' => 'manage_categories',
'edit_terms' => 'manage_categories',
'delete_terms' => 'manage_categories',
'assign_terms' => 'edit_posts',
),
'default_term' => array(
'name' => esc_html__( 'General Medicine', 'rx-theme' ),
'slug' => 'general-medicine',
'description' => esc_html__( 'General medical articles and health education topics.', 'rx-theme' ),
),
);
register_taxonomy( self::TAXONOMY, $post_types, $args );
}
/**
* Supported post types.
*/
public static function get_supported_post_types() {
$post_types = array( 'post' );
$possible_custom_post_types = array(
'medical',
'condition',
'disease',
'drug',
'treatment',
'diagnosis',
'faq',
'health_article',
'rx_article',
);
foreach ( $possible_custom_post_types as $post_type ) {
if ( post_type_exists( $post_type ) ) {
$post_types[] = $post_type;
}
}
/**
* Filter RX medical category supported post types.
*/
return apply_filters( 'rx_medical_category_post_types', array_unique( $post_types ) );
}
/**
* Add fields on create screen.
*/
public static function add_term_fields() {
wp_nonce_field( 'rx_save_medical_category_meta', 'rx_medical_category_nonce' );
?>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_SEO_TITLE ); ?>">
<?php esc_html_e( 'SEO Title', 'rx-theme' ); ?>
</label>
<input type="text" name="<?php echo esc_attr( self::META_SEO_TITLE ); ?>" id="<?php echo esc_attr( self::META_SEO_TITLE ); ?>" value="" maxlength="70">
<p><?php esc_html_e( 'Recommended length: 50–60 characters.', 'rx-theme' ); ?></p>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>">
<?php esc_html_e( 'SEO Description', 'rx-theme' ); ?>
</label>
<textarea name="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>" id="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>" rows="4" maxlength="180"></textarea>
<p><?php esc_html_e( 'Recommended length: 140–160 characters.', 'rx-theme' ); ?></p>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>">
<?php esc_html_e( 'Short Description', 'rx-theme' ); ?>
</label>
<textarea name="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>" id="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>" rows="4"></textarea>
<p><?php esc_html_e( 'Small intro text for cards, blocks, or archive header.', 'rx-theme' ); ?></p>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_IMAGE ); ?>">
<?php esc_html_e( 'Featured Image URL', 'rx-theme' ); ?>
</label>
<input type="url" name="<?php echo esc_attr( self::META_IMAGE ); ?>" id="<?php echo esc_attr( self::META_IMAGE ); ?>" value="">
<p><?php esc_html_e( 'Paste an image URL for this medical category.', 'rx-theme' ); ?></p>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_ICON ); ?>">
<?php esc_html_e( 'Icon Class', 'rx-theme' ); ?>
</label>
<input type="text" name="<?php echo esc_attr( self::META_ICON ); ?>" id="<?php echo esc_attr( self::META_ICON ); ?>" value="" placeholder="dashicons-heart">
<p><?php esc_html_e( 'Example: dashicons-heart, dashicons-plus-alt, rx-icon-lungs.', 'rx-theme' ); ?></p>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_COLOR ); ?>">
<?php esc_html_e( 'Color', 'rx-theme' ); ?>
</label>
<input type="text" class="rx-color-field" name="<?php echo esc_attr( self::META_COLOR ); ?>" id="<?php echo esc_attr( self::META_COLOR ); ?>" value="#0ea5e9">
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_ORDER ); ?>">
<?php esc_html_e( 'Display Order', 'rx-theme' ); ?>
</label>
<input type="number" name="<?php echo esc_attr( self::META_ORDER ); ?>" id="<?php echo esc_attr( self::META_ORDER ); ?>" value="0">
</div>
<div class="form-field rx-term-field">
<label>
<input type="checkbox" name="<?php echo esc_attr( self::META_FEATURED ); ?>" value="1">
<?php esc_html_e( 'Featured Medical Category', 'rx-theme' ); ?>
</label>
</div>
<div class="form-field rx-term-field">
<label>
<input type="checkbox" name="<?php echo esc_attr( self::META_NOINDEX ); ?>" value="1">
<?php esc_html_e( 'Noindex this category archive', 'rx-theme' ); ?>
</label>
</div>
<div class="form-field rx-term-field">
<label for="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>">
<?php esc_html_e( 'Schema Type', 'rx-theme' ); ?>
</label>
<select name="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>" id="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>">
<option value="MedicalWebPage"><?php esc_html_e( 'MedicalWebPage', 'rx-theme' ); ?></option>
<option value="CollectionPage"><?php esc_html_e( 'CollectionPage', 'rx-theme' ); ?></option>
<option value="MedicalCondition"><?php esc_html_e( 'MedicalCondition', 'rx-theme' ); ?></option>
<option value="MedicalTherapy"><?php esc_html_e( 'MedicalTherapy', 'rx-theme' ); ?></option>
</select>
</div>
<?php
}
/**
* Edit fields.
*/
public static function edit_term_fields( $term ) {
wp_nonce_field( 'rx_save_medical_category_meta', 'rx_medical_category_nonce' );
$seo_title = self::get_term_meta( $term->term_id, self::META_SEO_TITLE );
$seo_description = self::get_term_meta( $term->term_id, self::META_SEO_DESCRIPTION );
$short_description = self::get_term_meta( $term->term_id, self::META_SHORT_DESCRIPTION );
$image = self::get_term_meta( $term->term_id, self::META_IMAGE );
$icon = self::get_term_meta( $term->term_id, self::META_ICON );
$color = self::get_term_meta( $term->term_id, self::META_COLOR, '#0ea5e9' );
$order = self::get_term_meta( $term->term_id, self::META_ORDER, 0 );
$featured = self::get_term_meta( $term->term_id, self::META_FEATURED );
$noindex = self::get_term_meta( $term->term_id, self::META_NOINDEX );
$schema_type = self::get_term_meta( $term->term_id, self::META_SCHEMA_TYPE, 'MedicalWebPage' );
?>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_SEO_TITLE ); ?>">
<?php esc_html_e( 'SEO Title', 'rx-theme' ); ?>
</label>
</th>
<td>
<input type="text" name="<?php echo esc_attr( self::META_SEO_TITLE ); ?>" id="<?php echo esc_attr( self::META_SEO_TITLE ); ?>" value="<?php echo esc_attr( $seo_title ); ?>" maxlength="70">
<p class="description"><?php esc_html_e( 'Recommended length: 50–60 characters.', 'rx-theme' ); ?></p>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>">
<?php esc_html_e( 'SEO Description', 'rx-theme' ); ?>
</label>
</th>
<td>
<textarea name="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>" id="<?php echo esc_attr( self::META_SEO_DESCRIPTION ); ?>" rows="4" maxlength="180"><?php echo esc_textarea( $seo_description ); ?></textarea>
<p class="description"><?php esc_html_e( 'Recommended length: 140–160 characters.', 'rx-theme' ); ?></p>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>">
<?php esc_html_e( 'Short Description', 'rx-theme' ); ?>
</label>
</th>
<td>
<textarea name="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>" id="<?php echo esc_attr( self::META_SHORT_DESCRIPTION ); ?>" rows="4"><?php echo esc_textarea( $short_description ); ?></textarea>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_IMAGE ); ?>">
<?php esc_html_e( 'Featured Image URL', 'rx-theme' ); ?>
</label>
</th>
<td>
<input type="url" name="<?php echo esc_attr( self::META_IMAGE ); ?>" id="<?php echo esc_attr( self::META_IMAGE ); ?>" value="<?php echo esc_url( $image ); ?>">
<?php if ( $image ) : ?>
<p>
<img src="<?php echo esc_url( $image ); ?>" alt="" style="max-width:160px;height:auto;border:1px solid #ddd;padding:4px;background:#fff;">
</p>
<?php endif; ?>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_ICON ); ?>">
<?php esc_html_e( 'Icon Class', 'rx-theme' ); ?>
</label>
</th>
<td>
<input type="text" name="<?php echo esc_attr( self::META_ICON ); ?>" id="<?php echo esc_attr( self::META_ICON ); ?>" value="<?php echo esc_attr( $icon ); ?>" placeholder="dashicons-heart">
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_COLOR ); ?>">
<?php esc_html_e( 'Color', 'rx-theme' ); ?>
</label>
</th>
<td>
<input type="text" class="rx-color-field" name="<?php echo esc_attr( self::META_COLOR ); ?>" id="<?php echo esc_attr( self::META_COLOR ); ?>" value="<?php echo esc_attr( $color ); ?>">
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_ORDER ); ?>">
<?php esc_html_e( 'Display Order', 'rx-theme' ); ?>
</label>
</th>
<td>
<input type="number" name="<?php echo esc_attr( self::META_ORDER ); ?>" id="<?php echo esc_attr( self::META_ORDER ); ?>" value="<?php echo esc_attr( (int) $order ); ?>">
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row"><?php esc_html_e( 'Featured', 'rx-theme' ); ?></th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::META_FEATURED ); ?>" value="1" <?php checked( $featured, '1' ); ?>>
<?php esc_html_e( 'Featured Medical Category', 'rx-theme' ); ?>
</label>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row"><?php esc_html_e( 'Robots', 'rx-theme' ); ?></th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::META_NOINDEX ); ?>" value="1" <?php checked( $noindex, '1' ); ?>>
<?php esc_html_e( 'Noindex this category archive', 'rx-theme' ); ?>
</label>
</td>
</tr>
<tr class="form-field rx-term-field">
<th scope="row">
<label for="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>">
<?php esc_html_e( 'Schema Type', 'rx-theme' ); ?>
</label>
</th>
<td>
<select name="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>" id="<?php echo esc_attr( self::META_SCHEMA_TYPE ); ?>">
<?php
$types = array(
'MedicalWebPage',
'CollectionPage',
'MedicalCondition',
'MedicalTherapy',
);
foreach ( $types as $type ) :
?>
<option value="<?php echo esc_attr( $type ); ?>" <?php selected( $schema_type, $type ); ?>>
<?php echo esc_html( $type ); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<?php
}
/**
* Save term fields.
*/
public static function save_term_fields( $term_id ) {
if ( ! isset( $_POST['rx_medical_category_nonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['rx_medical_category_nonce'] ) ), 'rx_save_medical_category_meta' ) ) {
return;
}
if ( ! current_user_can( 'manage_categories' ) ) {
return;
}
$text_fields = array(
self::META_SEO_TITLE,
self::META_ICON,
self::META_SCHEMA_TYPE,
);
foreach ( $text_fields as $field ) {
$value = isset( $_POST[ $field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) : '';
update_term_meta( $term_id, $field, $value );
}
$textarea_fields = array(
self::META_SEO_DESCRIPTION,
self::META_SHORT_DESCRIPTION,
);
foreach ( $textarea_fields as $field ) {
$value = isset( $_POST[ $field ] ) ? sanitize_textarea_field( wp_unslash( $_POST[ $field ] ) ) : '';
update_term_meta( $term_id, $field, $value );
}
$image = isset( $_POST[ self::META_IMAGE ] ) ? esc_url_raw( wp_unslash( $_POST[ self::META_IMAGE ] ) ) : '';
update_term_meta( $term_id, self::META_IMAGE, $image );
$color = isset( $_POST[ self::META_COLOR ] ) ? sanitize_hex_color( wp_unslash( $_POST[ self::META_COLOR ] ) ) : '';
update_term_meta( $term_id, self::META_COLOR, $color ? $color : '#0ea5e9' );
$order = isset( $_POST[ self::META_ORDER ] ) ? absint( $_POST[ self::META_ORDER ] ) : 0;
update_term_meta( $term_id, self::META_ORDER, $order );
$featured = isset( $_POST[ self::META_FEATURED ] ) ? '1' : '0';
update_term_meta( $term_id, self::META_FEATURED, $featured );
$noindex = isset( $_POST[ self::META_NOINDEX ] ) ? '1' : '0';
update_term_meta( $term_id, self::META_NOINDEX, $noindex );
}
/**
* Admin columns.
*/
public static function admin_columns( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $label ) {
$new_columns[ $key ] = $label;
if ( 'name' === $key ) {
$new_columns['rx_image'] = esc_html__( 'Image', 'rx-theme' );
$new_columns['rx_icon'] = esc_html__( 'Icon', 'rx-theme' );
$new_columns['rx_color'] = esc_html__( 'Color', 'rx-theme' );
$new_columns['rx_featured'] = esc_html__( 'Featured', 'rx-theme' );
$new_columns['rx_order'] = esc_html__( 'Order', 'rx-theme' );
$new_columns['rx_noindex'] = esc_html__( 'Noindex', 'rx-theme' );
}
}
return $new_columns;
}
/**
* Admin column content.
*/
public static function admin_column_content( $content, $column_name, $term_id ) {
switch ( $column_name ) {
case 'rx_image':
$image = self::get_term_meta( $term_id, self::META_IMAGE );
if ( $image ) {
$content = '<img src="' . esc_url( $image ) . '" alt="" style="width:48px;height:48px;object-fit:cover;border-radius:6px;">';
} else {
$content = '—';
}
break;
case 'rx_icon':
$icon = self::get_term_meta( $term_id, self::META_ICON );
$content = $icon ? '<span class="' . esc_attr( $icon ) . '"></span> <code>' . esc_html( $icon ) . '</code>' : '—';
break;
case 'rx_color':
$color = self::get_term_meta( $term_id, self::META_COLOR, '#0ea5e9' );
$content = '<span style="display:inline-block;width:22px;height:22px;border-radius:50%;background:' . esc_attr( $color ) . ';border:1px solid #ccc;"></span> <code>' . esc_html( $color ) . '</code>';
break;
case 'rx_featured':
$featured = self::get_term_meta( $term_id, self::META_FEATURED );
$content = '1' === $featured ? esc_html__( 'Yes', 'rx-theme' ) : esc_html__( 'No', 'rx-theme' );
break;
case 'rx_order':
$content = absint( self::get_term_meta( $term_id, self::META_ORDER, 0 ) );
break;
case 'rx_noindex':
$noindex = self::get_term_meta( $term_id, self::META_NOINDEX );
$content = '1' === $noindex ? esc_html__( 'Yes', 'rx-theme' ) : esc_html__( 'No', 'rx-theme' );
break;
}
return $content;
}
/**
* Sortable columns.
*/
public static function sortable_columns( $columns ) {
$columns['rx_order'] = 'rx_order';
return $columns;
}
/**
* Improve taxonomy archive query.
*/
public static function archive_query( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_tax( self::TAXONOMY ) ) {
$query->set( 'posts_per_page', apply_filters( 'rx_medical_category_posts_per_page', 12 ) );
$query->set( 'ignore_sticky_posts', true );
}
}
/**
* Body classes.
*/
public static function body_classes( $classes ) {
if ( is_tax( self::TAXONOMY ) ) {
$term = get_queried_object();
if ( $term && ! is_wp_error( $term ) ) {
$classes[] = 'rx-medical-category-archive';
$classes[] = 'rx-medical-category-' . sanitize_html_class( $term->slug );
$featured = self::get_term_meta( $term->term_id, self::META_FEATURED );
if ( '1' === $featured ) {
$classes[] = 'rx-medical-category-featured';
}
}
}
return $classes;
}
/**
* Archive title.
*/
public static function archive_title( $title ) {
if ( is_tax( self::TAXONOMY ) ) {
$term = get_queried_object();
if ( $term && ! is_wp_error( $term ) ) {
$title = single_term_title( '', false );
}
}
return $title;
}
/**
* Archive description.
*/
public static function archive_description( $description ) {
if ( is_tax( self::TAXONOMY ) ) {
$term = get_queried_object();
if ( $term && ! is_wp_error( $term ) ) {
$short_description = self::get_term_meta( $term->term_id, self::META_SHORT_DESCRIPTION );
if ( $short_description ) {
$description = wpautop( esc_html( $short_description ) );
}
}
}
return $description;
}
/**
* Frontend meta tags.
*/
public static function frontend_meta_tags() {
if ( ! is_tax( self::TAXONOMY ) ) {
return;
}
$term = get_queried_object();
if ( ! $term || is_wp_error( $term ) ) {
return;
}
$seo_title = self::get_term_meta( $term->term_id, self::META_SEO_TITLE );
$seo_description = self::get_term_meta( $term->term_id, self::META_SEO_DESCRIPTION );
$noindex = self::get_term_meta( $term->term_id, self::META_NOINDEX );
$image = self::get_term_meta( $term->term_id, self::META_IMAGE );
if ( '1' === $noindex ) {
echo "\n" . '<meta name="robots" content="noindex,follow">' . "\n";
}
if ( $seo_title ) {
echo '<meta property="og:title" content="' . esc_attr( $seo_title ) . '">' . "\n";
echo '<meta name="twitter:title" content="' . esc_attr( $seo_title ) . '">' . "\n";
}
if ( $seo_description ) {
echo '<meta name="description" content="' . esc_attr( $seo_description ) . '">' . "\n";
echo '<meta property="og:description" content="' . esc_attr( $seo_description ) . '">' . "\n";
echo '<meta name="twitter:description" content="' . esc_attr( $seo_description ) . '">' . "\n";
}
if ( $image ) {
echo '<meta property="og:image" content="' . esc_url( $image ) . '">' . "\n";
echo '<meta name="twitter:image" content="' . esc_url( $image ) . '">' . "\n";
}
echo '<meta property="og:type" content="website">' . "\n";
echo '<meta property="og:url" content="' . esc_url( get_term_link( $term ) ) . '">' . "\n";
}
/**
* JSON-LD schema.
*/
public static function json_ld_schema() {
if ( ! is_tax( self::TAXONOMY ) ) {
return;
}
$term = get_queried_object();
if ( ! $term || is_wp_error( $term ) ) {
return;
}
$schema_type = self::get_term_meta( $term->term_id, self::META_SCHEMA_TYPE, 'MedicalWebPage' );
$seo_description = self::get_term_meta( $term->term_id, self::META_SEO_DESCRIPTION );
$image = self::get_term_meta( $term->term_id, self::META_IMAGE );
$url = get_term_link( $term );
if ( is_wp_error( $url ) ) {
return;
}
$data = array(
'@context' => 'https://schema.org',
'@type' => $schema_type ? $schema_type : 'MedicalWebPage',
'name' => single_term_title( '', false ),
'description' => $seo_description ? $seo_description : wp_strip_all_tags( term_description( $term ) ),
'url' => esc_url_raw( $url ),
'isPartOf' => array(
'@type' => 'WebSite',
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
),
);
if ( $image ) {
$data['image'] = esc_url_raw( $image );
}
echo "\n" . '<script type="application/ld+json">' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>' . "\n";
}
/**
* Register REST fields.
*/
public static function register_rest_fields() {
register_rest_field(
self::TAXONOMY,
'rx_meta',
array(
'get_callback' => function( $term ) {
$term_id = isset( $term['id'] ) ? absint( $term['id'] ) : 0;
return array(
'seo_title' => self::get_term_meta( $term_id, self::META_SEO_TITLE ),
'seo_description' => self::get_term_meta( $term_id, self::META_SEO_DESCRIPTION ),
'short_description' => self::get_term_meta( $term_id, self::META_SHORT_DESCRIPTION ),
'image' => self::get_term_meta( $term_id, self::META_IMAGE ),
'icon' => self::get_term_meta( $term_id, self::META_ICON ),
'color' => self::get_term_meta( $term_id, self::META_COLOR, '#0ea5e9' ),
'order' => absint( self::get_term_meta( $term_id, self::META_ORDER, 0 ) ),
'featured' => '1' === self::get_term_meta( $term_id, self::META_FEATURED ),
'noindex' => '1' === self::get_term_meta( $term_id, self::META_NOINDEX ),
'schema_type' => self::get_term_meta( $term_id, self::META_SCHEMA_TYPE, 'MedicalWebPage' ),
);
},
'schema' => array(
'description' => esc_html__( 'RX Medical Category custom metadata.', 'rx-theme' ),
'type' => 'object',
'context' => array( 'view', 'edit' ),
),
)
);
}
/**
* Admin assets.
*/
public static function admin_assets( $hook ) {
$screen = get_current_screen();
if ( ! $screen || self::TAXONOMY !== $screen->taxonomy ) {
return;
}
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
$script = "
jQuery(document).ready(function($){
$('.rx-color-field').wpColorPicker();
});
";
wp_add_inline_script( 'wp-color-picker', $script );
}
/**
* Shortcode: [rx_medical_categories]
*/
public static function shortcode_medical_categories( $atts ) {
$atts = shortcode_atts(
array(
'parent' => '',
'hide_empty' => 'false',
'featured' => '',
'number' => 0,
'columns' => 3,
'show_count' => 'true',
'show_image' => 'true',
'show_icon' => 'true',
'show_desc' => 'true',
'orderby' => 'meta_value_num',
'order' => 'ASC',
),
$atts,
'rx_medical_categories'
);
$args = array(
'taxonomy' => self::TAXONOMY,
'hide_empty' => filter_var( $atts['hide_empty'], FILTER_VALIDATE_BOOLEAN ),
'number' => absint( $atts['number'] ),
'orderby' => sanitize_key( $atts['orderby'] ),
'order' => 'DESC' === strtoupper( $atts['order'] ) ? 'DESC' : 'ASC',
);
if ( '' !== $atts['parent'] ) {
$args['parent'] = absint( $atts['parent'] );
}
if ( 'true' === $atts['featured'] || 'false' === $atts['featured'] ) {
$args['meta_query'] = array(
array(
'key' => self::META_FEATURED,
'value' => 'true' === $atts['featured'] ? '1' : '0',
),
);
}
if ( 'meta_value_num' === $args['orderby'] ) {
$args['meta_key'] = self::META_ORDER;
}
$terms = get_terms( $args );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return '';
}
$columns = max( 1, min( 6, absint( $atts['columns'] ) ) );
$show_count = filter_var( $atts['show_count'], FILTER_VALIDATE_BOOLEAN );
$show_image = filter_var( $atts['show_image'], FILTER_VALIDATE_BOOLEAN );
$show_icon = filter_var( $atts['show_icon'], FILTER_VALIDATE_BOOLEAN );
$show_desc = filter_var( $atts['show_desc'], FILTER_VALIDATE_BOOLEAN );
ob_start();
?>
<div class="rx-medical-category-grid rx-medical-category-grid-<?php echo esc_attr( $columns ); ?>">
<?php foreach ( $terms as $term ) : ?>
<?php
$link = get_term_link( $term );
$image = self::get_term_meta( $term->term_id, self::META_IMAGE );
$icon = self::get_term_meta( $term->term_id, self::META_ICON );
$color = self::get_term_meta( $term->term_id, self::META_COLOR, '#0ea5e9' );
$description = self::get_term_meta( $term->term_id, self::META_SHORT_DESCRIPTION );
if ( is_wp_error( $link ) ) {
continue;
}
?>
<article class="rx-medical-category-card" style="--rx-category-color: <?php echo esc_attr( $color ); ?>;">
<a href="<?php echo esc_url( $link ); ?>" class="rx-medical-category-card-link">
<?php if ( $show_image && $image ) : ?>
<span class="rx-medical-category-card-image">
<img src="<?php echo esc_url( $image ); ?>" alt="<?php echo esc_attr( $term->name ); ?>" loading="lazy">
</span>
<?php endif; ?>
<span class="rx-medical-category-card-content">
<span class="rx-medical-category-card-title">
<?php if ( $show_icon && $icon ) : ?>
<span class="<?php echo esc_attr( $icon ); ?>" aria-hidden="true"></span>
<?php endif; ?>
<?php echo esc_html( $term->name ); ?>
</span>
<?php if ( $show_desc && $description ) : ?>
<span class="rx-medical-category-card-description">
<?php echo esc_html( wp_trim_words( $description, 22 ) ); ?>
</span>
<?php endif; ?>
<?php if ( $show_count ) : ?>
<span class="rx-medical-category-card-count">
<?php
printf(
esc_html( _n( '%s article', '%s articles', $term->count, 'rx-theme' ) ),
esc_html( number_format_i18n( $term->count ) )
);
?>
</span>
<?php endif; ?>
</span>
</a>
</article>
<?php endforeach; ?>
</div>
<?php
return ob_get_clean();
}
/**
* Shortcode: [rx_medical_category_children]
*/
public static function shortcode_medical_category_children( $atts ) {
$atts = shortcode_atts(
array(
'term_id' => '',
'hide_empty' => 'false',
'columns' => 3,
),
$atts,
'rx_medical_category_children'
);
$term_id = absint( $atts['term_id'] );
if ( ! $term_id && is_tax( self::TAXONOMY ) ) {
$term = get_queried_object();
if ( $term && ! is_wp_error( $term ) ) {
$term_id = absint( $term->term_id );
}
}
if ( ! $term_id ) {
return '';
}
return self::shortcode_medical_categories(
array(
'parent' => $term_id,
'hide_empty' => $atts['hide_empty'],
'columns' => $atts['columns'],
)
);
}
/**
* Get term meta safely.
*/
public static function get_term_meta( $term_id, $key, $default = '' ) {
$value = get_term_meta( absint( $term_id ), $key, true );
if ( '' === $value || null === $value ) {
return $default;
}
return $value;
}
/**
* Get featured medical categories.
*/
public static function get_featured_categories( $limit = 6 ) {
$args = array(
'taxonomy' => self::TAXONOMY,
'hide_empty' => false,
'number' => absint( $limit ),
'meta_key' => self::META_ORDER,
'orderby' => 'meta_value_num',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => self::META_FEATURED,
'value' => '1',
),
),
);
return get_terms( $args );
}
/**
* Get category image.
*/
public static function get_category_image( $term_id ) {
return self::get_term_meta( $term_id, self::META_IMAGE );
}
/**
* Get category color.
*/
public static function get_category_color( $term_id ) {
return self::get_term_meta( $term_id, self::META_COLOR, '#0ea5e9' );
}
/**
* Get category icon.
*/
public static function get_category_icon( $term_id ) {
return self::get_term_meta( $term_id, self::META_ICON );
}
}
RX_Medical_Category_Taxonomy::init();
endif;
/**
* Helper: Get medical category image.
*/
function rx_get_medical_category_image( $term_id ) {
if ( class_exists( 'RX_Medical_Category_Taxonomy' ) ) {
return RX_Medical_Category_Taxonomy::get_category_image( $term_id );
}
return '';
}
/**
* Helper: Get medical category color.
*/
function rx_get_medical_category_color( $term_id ) {
if ( class_exists( 'RX_Medical_Category_Taxonomy' ) ) {
return RX_Medical_Category_Taxonomy::get_category_color( $term_id );
}
return '#0ea5e9';
}
/**
* Helper: Get medical category icon.
*/
function rx_get_medical_category_icon( $term_id ) {
if ( class_exists( 'RX_Medical_Category_Taxonomy' ) ) {
return RX_Medical_Category_Taxonomy::get_category_icon( $term_id );
}
return '';
}
/**
* Helper: Get featured medical categories.
*/
function rx_get_featured_medical_categories( $limit = 6 ) {
if ( class_exists( 'RX_Medical_Category_Taxonomy' ) ) {
return RX_Medical_Category_Taxonomy::get_featured_categories( $limit );
}
return array();
}
Add this line in your theme functions.php:
require_once get_template_directory() . '/inc/taxonomies/medical-category.php';
After adding it, go to:
WordPress Dashboard → Settings → Permalinks → Save Changes
Use shortcode anywhere:
[rx_medical_categories]
Featured only:
[rx_medical_categories featured="true" columns="4"]
Child categories on a medical category archive:
[rx_medical_category_children]