medical-category.php

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 = '&mdash;';
				}
				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>' : '&mdash;';
				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]

Leave a Reply

Your email address will not be published. Required fields are marked *