Skip to main content

Course Progress

Loading...

Learning Objectives

  • Understand term metadata in WordPress
  • Add custom fields to taxonomy terms
  • Use term meta API functions
  • Create admin forms for term metadata
  • Save and retrieve term meta data
  • Display term metadata in templates
  • Handle different field types
  • Implement best practices for term meta

Understanding Term Metadata

Term metadata allows you to store additional information for taxonomy terms beyond the default name, slug, and description. Since WordPress 4.4, the term meta API provides a standardized way to add custom fields to categories, tags, and custom taxonomies.

💡
Key Concept
Term metadata works similarly to post metadata. You can store any type of data associated with a term, such as images, colors, icons, custom text, or settings.

Term Meta API Functions

Function Description Usage
add_term_meta() Add metadata to a term add_term_meta($term_id, $key, $value, $unique)
get_term_meta() Retrieve term metadata get_term_meta($term_id, $key, $single)
update_term_meta() Update term metadata update_term_meta($term_id, $key, $value, $prev_value)
delete_term_meta() Delete term metadata delete_term_meta($term_id, $key, $value)
get_term_meta_by_id() Get meta by meta ID get_term_meta_by_id($mid)
update_term_meta_by_id() Update by meta ID update_term_meta_by_id($mid, $key, $value)
delete_term_meta_by_id() Delete by meta ID delete_term_meta_by_id($mid)

Basic Term Meta Usage

Adding and Retrieving Term Meta

<?php
// Add term meta
$term_id = 15;
add_term_meta( $term_id, 'custom_color', '#667eea', true );
add_term_meta( $term_id, 'featured_image_id', 123, true );
add_term_meta( $term_id, 'display_order', 10, true );

// Get term meta
$color = get_term_meta( $term_id, 'custom_color', true );
$image_id = get_term_meta( $term_id, 'featured_image_id', true );
$order = get_term_meta( $term_id, 'display_order', true );

// Update term meta
update_term_meta( $term_id, 'custom_color', '#764ba2' );
update_term_meta( $term_id, 'display_order', 5 );

// Delete term meta
delete_term_meta( $term_id, 'temporary_flag' );

// Get all meta for a term
$all_meta = get_term_meta( $term_id );

// Check if meta exists
if ( metadata_exists( 'term', $term_id, 'custom_color' ) ) {
    // Meta key exists
}

Adding Term Meta Fields to Admin

Complete Term Meta Implementation

<?php
/**
 * Add custom fields to category admin
 */
class MyTheme_Term_Meta {
    
    public function __construct() {
        // Category meta
        add_action( 'category_add_form_fields', array( $this, 'add_category_fields' ) );
        add_action( 'category_edit_form_fields', array( $this, 'edit_category_fields' ) );
        add_action( 'created_category', array( $this, 'save_category_fields' ) );
        add_action( 'edited_category', array( $this, 'save_category_fields' ) );
        
        // Custom taxonomy meta
        add_action( 'product_category_add_form_fields', array( $this, 'add_taxonomy_fields' ) );
        add_action( 'product_category_edit_form_fields', array( $this, 'edit_taxonomy_fields' ) );
        add_action( 'created_product_category', array( $this, 'save_taxonomy_fields' ) );
        add_action( 'edited_product_category', array( $this, 'save_taxonomy_fields' ) );
        
        // Add admin scripts
        add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
    }
    
    /**
     * Add fields to Add New Term screen
     */
    public function add_taxonomy_fields() {
        ?>
        <div class="form-field">
            <label for="term_color"><?php _e( 'Color', 'mytheme' ); ?></label>
            <input type="text" name="term_color" id="term_color" class="color-picker" value="">
            <p class="description"><?php _e( 'Select a color for this category', 'mytheme' ); ?></p>
        </div>
        
        <div class="form-field">
            <label for="term_image"><?php _e( 'Category Image', 'mytheme' ); ?></label>
            <input type="hidden" name="term_image_id" id="term_image_id" value="">
            <div id="term_image_wrapper"></div>
            <button type="button" class="button upload_image_button"><?php _e( 'Upload/Add Image', 'mytheme' ); ?></button>
            <button type="button" class="button remove_image_button" style="display:none;"><?php _e( 'Remove Image', 'mytheme' ); ?></button>
            <p class="description"><?php _e( 'Featured image for this category', 'mytheme' ); ?></p>
        </div>
        
        <div class="form-field">
            <label for="term_icon"><?php _e( 'Icon Class', 'mytheme' ); ?></label>
            <input type="text" name="term_icon" id="term_icon" value="">
            <p class="description"><?php _e( 'Enter icon class (e.g., fa-shopping-cart)', 'mytheme' ); ?></p>
        </div>
        
        <div class="form-field">
            <label for="term_featured">
                <input type="checkbox" name="term_featured" id="term_featured" value="1">
                <?php _e( 'Featured Category', 'mytheme' ); ?>
            </label>
            <p class="description"><?php _e( 'Check to mark as featured', 'mytheme' ); ?></p>
        </div>
        
        <div class="form-field">
            <label for="term_order"><?php _e( 'Display Order', 'mytheme' ); ?></label>
            <input type="number" name="term_order" id="term_order" value="0">
            <p class="description"><?php _e( 'Order for displaying (lower numbers first)', 'mytheme' ); ?></p>
        </div>
        <?php
    }
    
    /**
     * Add fields to Edit Term screen
     */
    public function edit_taxonomy_fields( $term ) {
        // Get existing values
        $color = get_term_meta( $term->term_id, 'term_color', true );
        $image_id = get_term_meta( $term->term_id, 'term_image_id', true );
        $icon = get_term_meta( $term->term_id, 'term_icon', true );
        $featured = get_term_meta( $term->term_id, 'term_featured', true );
        $order = get_term_meta( $term->term_id, 'term_order', true );
        ?>
        
        <tr class="form-field">
            <th scope="row"><label for="term_color"><?php _e( 'Color', 'mytheme' ); ?></label></th>
            <td>
                <input type="text" name="term_color" id="term_color" class="color-picker" value="<?php echo esc_attr( $color ); ?>">
                <p class="description"><?php _e( 'Select a color for this category', 'mytheme' ); ?></p>
            </td>
        </tr>
        
        <tr class="form-field">
            <th scope="row"><label for="term_image"><?php _e( 'Category Image', 'mytheme' ); ?></label></th>
            <td>
                <input type="hidden" name="term_image_id" id="term_image_id" value="<?php echo esc_attr( $image_id ); ?>">
                <div id="term_image_wrapper">
                    <?php if ( $image_id ) : ?>
                        <?php echo wp_get_attachment_image( $image_id, 'thumbnail' ); ?>
                    <?php endif; ?>
                </div>
                <button type="button" class="button upload_image_button"><?php _e( 'Upload/Add Image', 'mytheme' ); ?></button>
                <button type="button" class="button remove_image_button" <?php echo $image_id ? '' : 'style="display:none;"'; ?>><?php _e( 'Remove Image', 'mytheme' ); ?></button>
                <p class="description"><?php _e( 'Featured image for this category', 'mytheme' ); ?></p>
            </td>
        </tr>
        
        <tr class="form-field">
            <th scope="row"><label for="term_icon"><?php _e( 'Icon Class', 'mytheme' ); ?></label></th>
            <td>
                <input type="text" name="term_icon" id="term_icon" value="<?php echo esc_attr( $icon ); ?>">
                <p class="description"><?php _e( 'Enter icon class (e.g., fa-shopping-cart)', 'mytheme' ); ?></p>
            </td>
        </tr>
        
        <tr class="form-field">
            <th scope="row"><label for="term_featured"><?php _e( 'Featured', 'mytheme' ); ?></label></th>
            <td>
                <label>
                    <input type="checkbox" name="term_featured" id="term_featured" value="1" <?php checked( $featured, '1' ); ?>>
                    <?php _e( 'Featured Category', 'mytheme' ); ?>
                </label>
                <p class="description"><?php _e( 'Check to mark as featured', 'mytheme' ); ?></p>
            </td>
        </tr>
        
        <tr class="form-field">
            <th scope="row"><label for="term_order"><?php _e( 'Display Order', 'mytheme' ); ?></label></th>
            <td>
                <input type="number" name="term_order" id="term_order" value="<?php echo esc_attr( $order ); ?>">
                <p class="description"><?php _e( 'Order for displaying (lower numbers first)', 'mytheme' ); ?></p>
            </td>
        </tr>
        <?php
    }
    
    /**
     * Save term meta fields
     */
    public function save_taxonomy_fields( $term_id ) {
        // Save color
        if ( isset( $_POST['term_color'] ) ) {
            update_term_meta( $term_id, 'term_color', sanitize_hex_color( $_POST['term_color'] ) );
        }
        
        // Save image
        if ( isset( $_POST['term_image_id'] ) ) {
            update_term_meta( $term_id, 'term_image_id', absint( $_POST['term_image_id'] ) );
        }
        
        // Save icon
        if ( isset( $_POST['term_icon'] ) ) {
            update_term_meta( $term_id, 'term_icon', sanitize_text_field( $_POST['term_icon'] ) );
        }
        
        // Save featured
        $featured = isset( $_POST['term_featured'] ) ? '1' : '0';
        update_term_meta( $term_id, 'term_featured', $featured );
        
        // Save order
        if ( isset( $_POST['term_order'] ) ) {
            update_term_meta( $term_id, 'term_order', intval( $_POST['term_order'] ) );
        }
    }
    
    /**
     * Enqueue admin scripts
     */
    public function admin_scripts( $hook ) {
        if ( $hook != 'edit-tags.php' && $hook != 'term.php' ) {
            return;
        }
        
        // Color picker
        wp_enqueue_style( 'wp-color-picker' );
        wp_enqueue_script( 'wp-color-picker' );
        
        // Media uploader
        wp_enqueue_media();
        
        // Custom script
        wp_enqueue_script( 
            'mytheme-term-meta', 
            get_template_directory_uri() . '/js/term-meta.js', 
            array( 'jquery', 'wp-color-picker' ), 
            '1.0.0', 
            true 
        );
    }
}

// Initialize
new MyTheme_Term_Meta();

JavaScript for Media Upload

term-meta.js

jQuery(document).ready(function($) {
    
    // Color picker
    $('.color-picker').wpColorPicker();
    
    // Media upload
    var mediaUploader;
    
    $('.upload_image_button').on('click', function(e) {
        e.preventDefault();
        
        var button = $(this);
        var imageIdInput = button.siblings('#term_image_id');
        var imageWrapper = button.siblings('#term_image_wrapper');
        var removeButton = button.siblings('.remove_image_button');
        
        // If uploader exists, open it
        if (mediaUploader) {
            mediaUploader.open();
            return;
        }
        
        // Create media uploader
        mediaUploader = wp.media({
            title: 'Choose Image',
            button: {
                text: 'Use this image'
            },
            multiple: false
        });
        
        // When image selected
        mediaUploader.on('select', function() {
            var attachment = mediaUploader.state().get('selection').first().toJSON();
            imageIdInput.val(attachment.id);
            imageWrapper.html('<img src="' + attachment.sizes.thumbnail.url + '">');
            removeButton.show();
        });
        
        mediaUploader.open();
    });
    
    // Remove image
    $('.remove_image_button').on('click', function(e) {
        e.preventDefault();
        
        var button = $(this);
        var imageIdInput = button.siblings('#term_image_id');
        var imageWrapper = button.siblings('#term_image_wrapper');
        
        imageIdInput.val('');
        imageWrapper.html('');
        button.hide();
    });
});

Displaying Term Meta in Templates

Using Term Meta in Templates

<?php
// On taxonomy archive page
if ( is_tax() ) {
    $term = get_queried_object();
    $term_id = $term->term_id;
    
    // Get term meta
    $color = get_term_meta( $term_id, 'term_color', true );
    $image_id = get_term_meta( $term_id, 'term_image_id', true );
    $icon = get_term_meta( $term_id, 'term_icon', true );
    $featured = get_term_meta( $term_id, 'term_featured', true );
    
    // Display term header with custom styling
    ?>
    <header class="term-header" style="background-color: <?php echo esc_attr( $color ); ?>">
        <?php if ( $image_id ) : ?>
            <div class="term-image">
                <?php echo wp_get_attachment_image( $image_id, 'large' ); ?>
            </div>
        <?php endif; ?>
        
        <h1 class="term-title">
            <?php if ( $icon ) : ?>
                <i class="<?php echo esc_attr( $icon ); ?>"></i>
            <?php endif; ?>
            <?php single_term_title(); ?>
        </h1>
        
        <?php if ( $featured ) : ?>
            <span class="featured-badge"><?php _e( 'Featured', 'mytheme' ); ?></span>
        <?php endif; ?>
        
        <?php if ( term_description() ) : ?>
            <div class="term-description">
                <?php echo term_description(); ?>
            </div>
        <?php endif; ?>
    </header>
    <?php
}

// Display category grid with images
$categories = get_terms( array(
    'taxonomy'   => 'product_category',
    'hide_empty' => false,
    'meta_key'   => 'term_order',
    'orderby'    => 'meta_value_num',
    'order'      => 'ASC',
) );

if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) :
    ?>
    <div class="category-grid">
        <?php foreach ( $categories as $category ) :
            $cat_color = get_term_meta( $category->term_id, 'term_color', true );
            $cat_image = get_term_meta( $category->term_id, 'term_image_id', true );
            $cat_icon = get_term_meta( $category->term_id, 'term_icon', true );
            $cat_featured = get_term_meta( $category->term_id, 'term_featured', true );
            ?>
            
            <div class="category-card <?php echo $cat_featured ? 'featured' : ''; ?>">
                <a href="<?php echo esc_url( get_term_link( $category ) ); ?>">
                    <?php if ( $cat_image ) : ?>
                        <div class="category-image">
                            <?php echo wp_get_attachment_image( $cat_image, 'medium' ); ?>
                        </div>
                    <?php elseif ( $cat_icon ) : ?>
                        <div class="category-icon" style="background-color: <?php echo esc_attr( $cat_color ); ?>">
                            <i class="<?php echo esc_attr( $cat_icon ); ?>"></i>
                        </div>
                    <?php endif; ?>
                    
                    <h3 class="category-title">
                        <?php echo esc_html( $category->name ); ?>
                    </h3>
                    
                    <span class="category-count">
                        <?php printf( _n( '%s Product', '%s Products', $category->count, 'mytheme' ), $category->count ); ?>
                    </span>
                </a>
            </div>
        <?php endforeach; ?>
    </div>
<?php endif; ?>

Common Field Types for Term Meta

Text Input

<input type="text" name="meta_key" value="<?php echo esc_attr( $value ); ?>">

Textarea

<textarea name="meta_key" rows="5"><?php echo esc_textarea( $value ); ?></textarea>

Select Dropdown

<select name="meta_key">
    <option value="option1" <?php selected( $value, 'option1' ); ?>>Option 1</option>
    <option value="option2" <?php selected( $value, 'option2' ); ?>>Option 2</option>
</select>

Checkbox

<input type="checkbox" name="meta_key" value="1" <?php checked( $value, '1' ); ?>>

Radio Buttons

<label><input type="radio" name="meta_key" value="option1" <?php checked( $value, 'option1' ); ?>> Option 1</label>
<label><input type="radio" name="meta_key" value="option2" <?php checked( $value, 'option2' ); ?>> Option 2</label>

Practical Use Cases

E-commerce Categories

  • Category banners
  • Promotional text
  • Sale percentage
  • Featured products count

Portfolio Categories

  • Project type icon
  • Color scheme
  • Client logo
  • Industry sector

Blog Categories

  • Author assignment
  • Category header image
  • Custom CSS class
  • Content guidelines

Location Taxonomies

  • Map coordinates
  • Address details
  • Contact information
  • Operating hours

Best Practices

Term Meta Best Practices

  • Sanitize input: Always sanitize data before saving
  • Escape output: Escape data when displaying
  • Use unique keys: Prefix meta keys to avoid conflicts
  • Check permissions: Verify user can edit terms
  • Provide defaults: Handle missing meta gracefully
  • Cache queries: Use transients for expensive operations
  • Document fields: Add descriptions to help users
  • Consider performance: Don't add too many fields
Use WordPress color picker and media uploader for better user experience. They provide familiar interfaces that users already know from other parts of WordPress.
Term meta is not included when exporting/importing WordPress content by default. Consider creating custom export/import functionality for important term metadata.

Practice Exercise

💻
Implement Term Metadata System

Create a complete term metadata implementation:

  1. Add color picker field to categories
  2. Implement image upload for terms
  3. Create icon selector field
  4. Add featured checkbox
  5. Implement display order field
  6. Save all fields properly
  7. Create JavaScript for media upload
  8. Display term meta on archive pages
  9. Create category grid with images
  10. Style based on term colors

Additional Resources