🏷️ Custom Taxonomies
Create custom ways to organize and categorize content
Master hierarchical and non-hierarchical taxonomies in WordPress
Learning Objectives
- Understand WordPress taxonomies
- Register custom taxonomies with register_taxonomy()
- Create hierarchical (category-like) taxonomies
- Build non-hierarchical (tag-like) taxonomies
- Associate taxonomies with post types
- Configure taxonomy labels and arguments
- Query posts by taxonomy terms
- Display taxonomies in templates
Understanding Taxonomies
Taxonomies are ways to group and organize content in WordPress. They create relationships between different pieces of content, making it easier for users to find related information.
Key Concept
Taxonomy Components
- Taxonomy: The classification system itself (e.g., "Categories")
- Terms: Individual items within a taxonomy (e.g., "Technology", "Sports")
- Term Relationships: Connections between posts and terms
Built-in vs Custom Taxonomies
| Built-in Taxonomies | Type | Used For |
|---|---|---|
| category | Hierarchical | Posts - Main topics |
| post_tag | Non-hierarchical | Posts - Keywords |
| nav_menu | Non-hierarchical | Navigation menus |
| link_category | Non-hierarchical | Links (deprecated) |
| post_format | Non-hierarchical | Post formats |
Registering Custom Taxonomies
Basic Taxonomy Registration
<?php
/**
* Register a simple custom taxonomy
*/
function mytheme_register_product_category() {
$args = array(
'hierarchical' => true,
'label' => 'Product Categories',
'public' => true,
'show_in_rest' => true,
);
register_taxonomy( 'product_category', 'product', $args );
}
add_action( 'init', 'mytheme_register_product_category' );
Complete Hierarchical Taxonomy (Category-like)
<?php
/**
* Register hierarchical taxonomy for products
*/
function mytheme_register_product_category_taxonomy() {
$labels = array(
'name' => _x( 'Product Categories', 'Taxonomy General Name', 'mytheme' ),
'singular_name' => _x( 'Product Category', 'Taxonomy Singular Name', 'mytheme' ),
'menu_name' => __( 'Categories', 'mytheme' ),
'all_items' => __( 'All Categories', 'mytheme' ),
'parent_item' => __( 'Parent Category', 'mytheme' ),
'parent_item_colon' => __( 'Parent Category:', 'mytheme' ),
'new_item_name' => __( 'New Category Name', 'mytheme' ),
'add_new_item' => __( 'Add New Category', 'mytheme' ),
'edit_item' => __( 'Edit Category', 'mytheme' ),
'update_item' => __( 'Update Category', 'mytheme' ),
'view_item' => __( 'View Category', 'mytheme' ),
'separate_items_with_commas' => __( 'Separate categories with commas', 'mytheme' ),
'add_or_remove_items' => __( 'Add or remove categories', 'mytheme' ),
'choose_from_most_used' => __( 'Choose from the most used', 'mytheme' ),
'popular_items' => __( 'Popular Categories', 'mytheme' ),
'search_items' => __( 'Search Categories', 'mytheme' ),
'not_found' => __( 'Not Found', 'mytheme' ),
'no_terms' => __( 'No categories', 'mytheme' ),
'items_list' => __( 'Categories list', 'mytheme' ),
'items_list_navigation' => __( 'Categories list navigation', 'mytheme' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
'show_in_rest' => true,
'rest_base' => 'product-categories',
'rest_controller_class' => 'WP_REST_Terms_Controller',
'rewrite' => array(
'slug' => 'products/category',
'with_front' => false,
'hierarchical' => true,
),
'query_var' => true,
'default_term' => array(
'name' => 'Uncategorized',
'slug' => 'uncategorized',
),
);
register_taxonomy( 'product_category', array( 'product' ), $args );
}
add_action( 'init', 'mytheme_register_product_category_taxonomy', 0 );
Non-Hierarchical Taxonomy (Tag-like)
<?php
/**
* Register non-hierarchical taxonomy for products
*/
function mytheme_register_product_tag_taxonomy() {
$labels = array(
'name' => _x( 'Product Tags', 'Taxonomy General Name', 'mytheme' ),
'singular_name' => _x( 'Product Tag', 'Taxonomy Singular Name', 'mytheme' ),
'menu_name' => __( 'Tags', 'mytheme' ),
'all_items' => __( 'All Tags', 'mytheme' ),
'new_item_name' => __( 'New Tag Name', 'mytheme' ),
'add_new_item' => __( 'Add New Tag', 'mytheme' ),
'edit_item' => __( 'Edit Tag', 'mytheme' ),
'update_item' => __( 'Update Tag', 'mytheme' ),
'view_item' => __( 'View Tag', 'mytheme' ),
'separate_items_with_commas' => __( 'Separate tags with commas', 'mytheme' ),
'add_or_remove_items' => __( 'Add or remove tags', 'mytheme' ),
'choose_from_most_used' => __( 'Choose from the most used tags', 'mytheme' ),
'popular_items' => __( 'Popular Tags', 'mytheme' ),
'search_items' => __( 'Search Tags', 'mytheme' ),
'not_found' => __( 'No tags found', 'mytheme' ),
'no_terms' => __( 'No tags', 'mytheme' ),
'items_list' => __( 'Tags list', 'mytheme' ),
'items_list_navigation' => __( 'Tags list navigation', 'mytheme' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
'show_in_rest' => true,
'rewrite' => array(
'slug' => 'product-tag',
'with_front' => false,
),
'update_count_callback' => '_update_post_term_count',
'query_var' => true,
'capabilities' => array(
'manage_terms' => 'manage_categories',
'edit_terms' => 'manage_categories',
'delete_terms' => 'manage_categories',
'assign_terms' => 'edit_posts',
),
);
register_taxonomy( 'product_tag', array( 'product' ), $args );
}
add_action( 'init', 'mytheme_register_product_tag_taxonomy', 0 );
register_taxonomy() Arguments
| Argument | Description | Default |
|---|---|---|
labels |
Array of labels for the taxonomy | array() |
description |
Short descriptive summary | '' |
public |
Whether taxonomy is public | true |
publicly_queryable |
Whether queries can be performed | value of public |
hierarchical |
Is taxonomy hierarchical | false |
show_ui |
Generate default UI in admin | value of public |
show_in_menu |
Show in admin menu | value of show_ui |
show_in_nav_menus |
Available for selection in menus | value of public |
show_in_rest |
Include in REST API | false |
rest_base |
REST API base slug | taxonomy name |
show_tagcloud |
Available for tag cloud widget | value of show_ui |
show_in_quick_edit |
Show in quick/bulk edit panel | value of show_ui |
show_admin_column |
Display column in post lists | false |
meta_box_cb |
Callback for meta box display | null |
meta_box_sanitize_cb |
Callback for sanitizing meta box | null |
capabilities |
Array of capabilities | array() |
rewrite |
Permalink rewrite rules | true |
query_var |
Query var for this taxonomy | taxonomy name |
update_count_callback |
Callback to update term count | '' |
default_term |
Default term settings | null |
sort |
Whether to sort terms | null |
Associating Taxonomies with Multiple Post Types
Shared Taxonomy Across Post Types
<?php
/**
* Register taxonomy for multiple post types
*/
function mytheme_register_shared_taxonomy() {
$labels = array(
'name' => _x( 'Topics', 'Taxonomy General Name', 'mytheme' ),
'singular_name' => _x( 'Topic', 'Taxonomy Singular Name', 'mytheme' ),
'menu_name' => __( 'Topics', 'mytheme' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
'public' => true,
'show_admin_column' => true,
'show_in_rest' => true,
'rewrite' => array( 'slug' => 'topic' ),
);
// Associate with multiple post types
$post_types = array( 'post', 'product', 'portfolio' );
register_taxonomy( 'topic', $post_types, $args );
}
add_action( 'init', 'mytheme_register_shared_taxonomy' );
// Add existing taxonomy to additional post type
function mytheme_add_categories_to_pages() {
register_taxonomy_for_object_type( 'category', 'page' );
register_taxonomy_for_object_type( 'post_tag', 'page' );
}
add_action( 'init', 'mytheme_add_categories_to_pages' );
Common Taxonomy Examples
Brand
Product brands/manufacturers
Color
Product color variations
Size
Product sizes (S, M, L, XL)
Location
Geographic locations
Genre
Movie/book genres
Department
Company departments
Skill Level
Beginner, Intermediate, Advanced
Year
Year of production/release
Features
Product features/capabilities
Displaying Taxonomies in Templates
Display Terms for a Post
<?php
// Get terms for current post
$terms = get_the_terms( get_the_ID(), 'product_category' );
if ( $terms && ! is_wp_error( $terms ) ) :
?>
<div class="product-categories">
<h3><?php _e( 'Categories:', 'mytheme' ); ?></h3>
<ul>
<?php foreach ( $terms as $term ) : ?>
<li>
<a href="<?php echo esc_url( get_term_link( $term ) ); ?>">
<?php echo esc_html( $term->name ); ?>
</a>
<span class="term-count">(<?php echo $term->count; ?>)</span>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php
// Display terms as comma-separated list
$term_list = get_the_term_list(
get_the_ID(),
'product_category',
'<div class="categories">Categories: ',
', ',
'</div>'
);
if ( $term_list && ! is_wp_error( $term_list ) ) {
echo $term_list;
}
List All Terms in a Taxonomy
<?php
// Get all terms in taxonomy
$terms = get_terms( array(
'taxonomy' => 'product_category',
'hide_empty' => false,
'orderby' => 'name',
'order' => 'ASC',
'parent' => 0, // Only top-level terms
) );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) :
?>
<ul class="category-list">
<?php foreach ( $terms as $term ) : ?>
<li>
<a href="<?php echo esc_url( get_term_link( $term ) ); ?>">
<?php echo esc_html( $term->name ); ?>
</a>
<?php
// Get child terms
$children = get_terms( array(
'taxonomy' => 'product_category',
'hide_empty' => false,
'parent' => $term->term_id,
) );
if ( ! empty( $children ) ) :
?>
<ul class="sub-categories">
<?php foreach ( $children as $child ) : ?>
<li>
<a href="<?php echo esc_url( get_term_link( $child ) ); ?>">
<?php echo esc_html( $child->name ); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
Querying Posts by Taxonomy
WP_Query with Taxonomy Parameters
<?php
// Query posts from specific term
$args = array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'product_category',
'field' => 'slug',
'terms' => 'electronics',
),
),
);
$query = new WP_Query( $args );
// Query with multiple terms (OR relationship)
$args = array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'product_category',
'field' => 'term_id',
'terms' => array( 12, 15, 18 ),
'operator' => 'IN',
),
),
);
// Query with multiple taxonomies (AND relationship)
$args = array(
'post_type' => 'product',
'tax_query' => array(
'relation' => 'AND',
array(
'taxonomy' => 'product_category',
'field' => 'slug',
'terms' => 'electronics',
),
array(
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => 'featured',
),
),
);
// Exclude specific terms
$args = array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'product_category',
'field' => 'term_id',
'terms' => array( 10, 20 ),
'operator' => 'NOT IN',
),
),
);
// Complex tax query
$args = array(
'post_type' => 'product',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'product_category',
'field' => 'slug',
'terms' => array( 'electronics' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'product_tag',
'field' => 'slug',
'terms' => array( 'featured' ),
),
array(
'taxonomy' => 'product_brand',
'field' => 'slug',
'terms' => array( 'apple', 'samsung' ),
),
),
),
);
Useful Taxonomy Functions
Working with Terms
<?php
// Check if term exists
if ( term_exists( 'electronics', 'product_category' ) ) {
// Term exists
}
// Get term by different fields
$term = get_term_by( 'slug', 'electronics', 'product_category' );
$term = get_term_by( 'name', 'Electronics', 'product_category' );
$term = get_term_by( 'id', 15, 'product_category' );
// Get term data
$term_id = $term->term_id;
$term_name = $term->name;
$term_slug = $term->slug;
$term_description = $term->description;
$term_count = $term->count;
$term_parent = $term->parent;
// Get term meta
$term_color = get_term_meta( $term_id, 'color', true );
// Get term link
$term_link = get_term_link( $term );
// Check if on term archive
if ( is_tax( 'product_category' ) ) {
// On product_category archive
}
if ( is_tax( 'product_category', 'electronics' ) ) {
// On specific term archive
}
// Get current term on archive
$current_term = get_queried_object();
// Add term to post
wp_set_object_terms( $post_id, array( 'electronics', 'computers' ), 'product_category' );
// Remove all terms from post
wp_remove_object_terms( $post_id, array( 'electronics' ), 'product_category' );
// Get term children
$children = get_term_children( $term_id, 'product_category' );
// Get term ancestors
$ancestors = get_ancestors( $term_id, 'product_category', 'taxonomy' );
// Count posts in term
$count = $term->count;
// Get posts in term
$posts = get_posts( array(
'post_type' => 'product',
'tax_query' => array(
array(
'taxonomy' => 'product_category',
'field' => 'term_id',
'terms' => $term_id,
),
),
) );
Best Practices
Taxonomy Best Practices
- Choose appropriate structure: Hierarchical for categories, flat for tags
- Use descriptive names: Make taxonomy purpose clear
- Avoid conflicts: Prefix taxonomy names uniquely
- Plan URL structure: Consider SEO when setting rewrites
- Enable REST API: For Gutenberg block editor support
- Set default terms: Provide fallback for uncategorized content
- Limit depth: Don't create overly complex hierarchies
- Consider performance: Too many taxonomies can slow queries
Taxonomy names must not exceed 32 characters and cannot contain capital letters or spaces. Always use lowercase with underscores.
Practice Exercise
Create Custom Taxonomies