iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Taxonomies

Taxonomies organise posts. WordPress ships category and tag for built-in posts; register_taxonomy lets you create your own (Industries, Genres, Locations) for custom post types — with hierarchy, REST exposure, URL slugs, and editing UI.

register, hierarchical, REST, UI

EXAMPLE
<?php
// 1) Register a custom taxonomy
add_action( 'init', function () {
    register_taxonomy(
        'industry',                                           // slug; URL: /industry/finance
        array( 'post', 'business' ),                          // post types it applies to
        array(
            'label'               => 'Industries',
            'labels'              => array(
                'name'                       => 'Industries',
                'singular_name'              => 'Industry',
                'menu_name'                  => 'Industries',
                'all_items'                  => 'All Industries',
                'parent_item'                => 'Parent Industry',
                'parent_item_colon'          => 'Parent Industry:',
                'new_item_name'              => 'New Industry',
                'add_new_item'               => 'Add Industry',
                'edit_item'                  => 'Edit Industry',
                'update_item'                => 'Update Industry',
                'view_item'                  => 'View Industry',
                'search_items'               => 'Search Industries',
                'add_or_remove_items'        => 'Add or remove industries',
                'choose_from_most_used'      => 'Choose from the most used',
            ),
            'hierarchical'        => true,                    // like categories (tree)
            'public'              => true,
            'publicly_queryable'  => true,
            'show_ui'             => true,
            'show_admin_column'   => true,                    // column in list table
            'show_in_nav_menus'   => true,
            'show_tagcloud'       => true,
            'show_in_rest'        => true,                    // exposes via /wp-json/wp/v2/industry
            'rest_base'           => 'industries',
            'rewrite'             => array( 'slug' => 'industry', 'with_front' => false ),
            'capabilities'        => array(
                'manage_terms'    => 'manage_categories',
                'edit_terms'      => 'manage_categories',
                'delete_terms'    => 'manage_categories',
                'assign_terms'    => 'edit_posts',
            ),
        )
    );
} );

// 2) Flat (tag-like) taxonomy
register_taxonomy( 'mood', 'product', array(
    'label'        => 'Moods',
    'hierarchical' => false,                                  // tag-style
    'public'       => true,
    'show_in_rest' => true,
    'rewrite'      => array( 'slug' => 'mood' ),
) );

// 3) Attach an existing taxonomy to another post type
register_taxonomy_for_object_type( 'industry', 'product' );

// 4) Create / update terms
$term = wp_insert_term( 'Finance', 'industry', array(
    'slug'        => 'finance',
    'description' => 'Finance-related content',
    'parent'      => 0,
) );
if ( is_wp_error( $term ) ) error_log( $term->get_error_message() );

// Children
wp_insert_term( 'Banking', 'industry', array( 'parent' => $term['term_id'] ) );
wp_insert_term( 'Insurance', 'industry', array( 'parent' => $term['term_id'] ) );

// 5) Assign terms to a post
wp_set_object_terms( $post_id, array( 'finance', 'banking' ), 'industry' );
wp_set_object_terms( $post_id, array( 'finance' ), 'industry', true );        // append, not replace

// 6) Query posts by term
$query = new WP_Query( array(
    'post_type' => 'post',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'industry',
            'field'    => 'slug',
            'terms'    => array( 'finance', 'banking' ),
            'operator' => 'IN',
        ),
        array(
            'taxonomy' => 'mood',
            'field'    => 'slug',
            'terms'    => array( 'serious' ),
            'operator' => 'NOT IN',
        ),
    ),
) );

// 7) Render terms in templates
<?php
$terms = get_the_terms( get_the_ID(), 'industry' );
if ( $terms && ! is_wp_error( $terms ) ) :
?>
    <ul class="industries">
    <?php foreach ( $terms as $term ) : ?>
        <li><a href="<?php echo esc_url( get_term_link( $term ) ); ?>"><?php echo esc_html( $term->name ); ?></a></li>
    <?php endforeach; ?>
    </ul>
<?php endif; ?>

// 8) Term metadata
add_term_meta( $term_id, 'featured_image_id', $attachment_id, true );
$image_id = get_term_meta( $term_id, 'featured_image_id', true );

// 9) UI hooks — fields on the add/edit term screen
add_action( 'industry_add_form_fields', function () {
?>
    <div class="form-field term-featured-image-wrap">
        <label for="featured_image_id">Featured image ID</label>
        <input type="text" id="featured_image_id" name="featured_image_id" />
    </div>
<?php
} );

add_action( 'edited_industry', function ( $term_id ) {
    if ( isset( $_POST['featured_image_id'] ) ) {
        update_term_meta( $term_id, 'featured_image_id', absint( $_POST['featured_image_id'] ) );
    }
} );

// 10) Archive templates — template hierarchy
// taxonomy-{taxonomy}-{slug}.php     → taxonomy-industry-finance.php
// taxonomy-{taxonomy}.php             → taxonomy-industry.php
// taxonomy.php
// archive.php
// index.php

// 11) REST API
// GET /wp-json/wp/v2/industries
// GET /wp-json/wp/v2/industries/{id}
// POST /wp-json/wp/v2/industries        (needs auth + manage_categories)
// Filter posts: /wp-json/wp/v2/posts?industry=42

// 12) Block editor support — show in the right sidebar
// 'show_in_rest' => true is required for block editor to fetch the taxonomy.
// Use core/post-terms block in templates: <!-- wp:post-terms {"term":"industry"} /-->

// 13) Querying terms (not posts)
$terms = get_terms( array(
    'taxonomy'   => 'industry',
    'hide_empty' => false,
    'parent'     => 0,                                        // top-level only
    'orderby'    => 'name',
    'order'      => 'ASC',
) );

// 14) Migration / cleanup
wp_delete_term( $term_id, 'industry' );
// Use Tools → Import/Export, or WP-CLI: 'wp term migrate' for bulk operations.
// wp term list industry
// wp term create industry 'Finance' --slug=finance
// wp term update industry 'Banking' --parent=42

// 15) Common bugs
// • Forgetting to flush rewrites after registering with rewrite slug → 404 on /industry/finance
//   Visit Settings → Permalinks once, or call flush_rewrite_rules() on plugin activation
// • Taxonomy registered but NOT on init hook — too early; WP not ready
// • show_in_rest = false — block editor doesn't show the taxonomy panel
// • Hierarchical taxonomy used for tag-like data — UX confusing; pick the right model
// • register_taxonomy slug conflicts with built-in (post_tag, category) — pick a unique name
// • wp_set_object_terms with strings — WordPress creates terms on the fly; can lead to typos as new terms
// • Querying terms with hide_empty:true (default) misses unused — set false when listing all
// • Filtering REST endpoint with the wrong param key — slug != ID; check the resource shape

Why it matters

Custom taxonomies are how you organise custom post types — register them on init, mark show_in_rest so the block editor + API can use them, and pair with a template (taxonomy-{name}.php) for archive rendering. Add term meta + custom admin fields for richer editing without third-party plugins.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
register_taxonomy('genre', 'book', [
    'public' => true,
    'hierarchical' => true,
]);
Try it Yourself »

Discussion

Loading…