Skip to content

WordPress Allowing Categories To Have Genres

WordPress Allowing Categories To Have Genres

With WordPress being so widely used in the Web, it is no wonder that WordPress has helped people develop all kinds of sites – book reviews, food reviews, cooking, sports, … While WordPress is widely used, and is the mainstream Content Management System (CMS) among many users, it does not always offer an elegant solution to a problem. WordPress is not designed to have a taxonomy assigned to another taxonomy (e.g categories being grouped by genres) This is just how WordPress is designed. I won’t go through its pros and cons, but will instead show you how to go round this problem. Using Category Meta (WordPress 4.4+) In WordPress 4.4, term meta data was introduced and this allows us to add custom fields and values to posts, categories, and pages. Although the ability to edit these fields is not directly available through the WordPress user interface, it can however be modified through the use of codes. Here are the 4 main functions introduced to handling term meta data.
  • add_term_meta():  adds the meta data
  • update_term_meta(): updates existing meta data
  • delete_term_meta(): deletes meta data
  • get_term_meta(): retrieves the meta data
By their function names, they are pretty self-explanatory. The only thing we should take note is, that the meta field specified can be a unique field – meaning only a single entry of it can exist, or it can have multiple entries of it. For example, in our case, we need to have multiple entries of genre because, you obviously don’t only have one type of genre. So let’s dive right in to how to implement genres for categories. Firstly, lets specify a global array as our list of genres available. Oh yes, for the codes below, you can just put them all into your functions.php file
$genres_list = [
    "romance" => "Romance",
    "action" => "Action",
    "horror" => "Horror",
Note that the array key will be used as the value that we will pass to WordPress, while the array value is what we will display to the user (hence why the initial is capitalized). So, romance will be stored into WordPress, while Romance will be displayed to the user. Now that we have specified our available genres, next we want to modify the Add Category page to allow the user to add genres to their category when creating one. To do so, we have to use the hook category_add_form_fields.
add_action( 'category_add_form_fields', 'add_genre_field', 10, 2 );
function add_genre_field($taxonomy) {
    global $genres_list;
    ?><div class="form-field term-group">
        <p><?php _e('Genre(s)', 'niraeth_plugin'); ?></p>
        <?php foreach ($genres_list as $genre_key => $genre) : ?>
            <input type="checkbox" id="cb_<?php echo $genre_key; ?>" name="genres_list[]" value="<?php echo $genre_key; ?>"> <?php echo $genre; ?>
        <?php endforeach; ?>

Once you save this code into your functions.php file, go ahead and try to create a new category. You will see the page is now something like this However, although the page shows that genres can be added, it doesn’t actually add genres to the category yet, because we have yet to actually save the specified genres from Add Category page into WordPress database. To do so, we need to hook created_category.
add_action( 'created_category', 'save_genre_meta', 10, 2 );

function save_genre_meta( $term_id, $tt_id ){
    if( isset( $_POST['genres_list'] ) && !empty($_POST['genres_list']) ){
        foreach( $_POST['genres_list'] as $genre )
            $genre = sanitize_title($genre);
        	add_term_meta( $term_id, 'genre', $genre, false );
Now, the genre values specified will be saved into WordPress database. Go ahead and create a new category, so we can use it to edit later on. Remember how I mentioned earlier that a meta field can contain multiple entries? Lets take a look at the parameters of add_term_meta function to see how we can do so.
  • $term_id: the id of the term,
  • $meta_key: the meta key,
  • $meta_value: the value,
  • $unique: whether the key is unique, or can contain multiple entries; defaults to false.
So the thing to note here, is to make sure add_term_meta fourth parameter $unique is false. Let’s move on to the updating or editing of genres of a category. To do so, we need to hook category_edit_form_fields.
add_action( 'category_edit_form_fields', 'edit_genre_field', 10, 2 );

function edit_genre_field( $term, $taxonomy ){

    global $genres_list;

    // get current group
    $retrieved_genre_keys = get_term_meta( $term->term_id, 'genre', false );
    ?><tr class="form-field term-group-wrap">
        <th scope="row"><label for="genre"><?php _e( 'Genre', 'niraeth_plugin' ); ?></label></th> <br/>
                foreach( $genres_list as $genre_key => $genre ) : 
                    if( in_array($genre_key, $retrieved_genre_keys) ):
                        $checked = "checked";
                        $checked = "";
                <input type="checkbox" id="cb_<?php echo $genre_key; ?>" name="genres_list[]" value="<?php echo $genre_key; ?>" <?php echo $checked;?> > <?php echo $genre; ?>
            <?php endforeach; ?>

Here, to display what genres has already been specified, we need to call the function get_term_meta. The function arguments are as follow :
  • $term_id: the id of the term,
  • $key: the key of the meta data,
  • $single: whether to return a single result; defaults to false which would return an array.
Remember, like the $unique field in add_term_meta, we want to specify the third parameter, $single, as false here when calling get_term_meta so that we can retrieve all our genre entries. Now, lets add the code to save edited genres to the WordPress database.
add_action( 'edited_category', 'update_genre_meta', 10, 2 );

function update_genre_meta( $term_id, $tt_id ){
    if( isset( $_POST['genres_list'] ) && !empty($_POST['genres_list']) ){        	
        delete_term_meta( $term_id, 'genre' ); // delete all meta belonging to 'genre'

        foreach( $_POST['genres_list'] as $genre )
            $genre = sanitize_title($genre);
        	add_term_meta( $term_id, 'genre', $genre, false );
For our editing, we are not actually going to use the update_term_metafunction as it does not actually help us here. Lets look at its parameters :
  • $term_id: term id.
  • $meta_key: metadata key.
  • $meta_value:  metadata value.
  • $prev_value:  previous value to check before removing. Default value: ”
So as you can see, in order to use the update_term_meta function, we need to know the meta’s previous value which we definitely won’t. This is because our code design doesn’t have a state such as true or false, or,  yes or no. So if our previous value was false, we can now change it to true. You see the problem here? We don’t have a previous value to use ! So we can solve this by simply removing all the old genres specified using delete_term_meta, and then adding the genres specified with add_term_meta, just like how we did for our Add Category page. Save the code, and try to edit a category, you will see that it displays the current specified genres. It should look like this. A Try to update it, and it should work fine now. If it works, update with at least two genre, e.g Romance and Action, to see how multiple genres will be displayed to the user later on. Finally, let’s add a column for the user to see what genres each category have in the Categories page.
add_filter('manage_edit-category_columns', 'add_genre_column' );

function add_genre_column( $columns ){
    $columns['genre'] = __( 'Genre', 'niraeth_plugin' );
    return $columns;
Now there should be a Genre column which you can see, just that the cell is empty. Lets add the code to display the genres into the cell.
add_filter('manage_category_custom_column', 'add_genre_column_content', 10, 3 );

function add_genre_column_content( $content, $column_name, $term_id ){
    global $genres_list;

    if( $column_name !== 'genre' ){
        return $content;

    $term_id = absint( $term_id );
    $retrieved_genre_keys = get_term_meta( $term_id, 'genre', false );
    $retrieved_genres = [];
    foreach($retrieved_genre_keys as $retrieved_genre_key)
        $retrieved_genres[] = $genres_list[$retrieved_genre_key];
    // Convert $retrieved_genres to readable string
    $genres_string = implode(",", $retrieved_genres);
    if( !empty( $retrieved_genres ) ){
        $content .= esc_attr( $genres_string );

    return $content;
Now the user can see what genres each category have. Finally, lets help the user by adding code to sort the column.
add_filter( 'manage_edit-category_sortable_columns', 'add_genre_column_sortable' );

function add_genre_column_sortable( $sortable ){
    $sortable[ 'genre' ] = 'genre';
    return $sortable;
Great, now you should see something like this in your category page. Finally, in case you wish to find which categories contains a specific genre, e.g Romance, I have made a function for you to do so.
function get_categories_by_genre($genre_key)
    $genre_key = strtolower($genre_key);
    // Now let's test it out, to see if we can retrieve it using code.
    $args = array(
        'hide_empty' => false, // also retrieve terms which are not used yet
        'meta_query' => array(
               'key'       => 'genre',
               'value'     => $genre_key,
               'compare'   => 'LIKE'
    $terms = get_terms( 'category', $args );
    return $terms;
And to display the categories with their genres,
function display_categories_with_genre($categories)
    if ( ! empty( $categories ) && ! is_wp_error( $categories ) ){
        echo '<ul>';
        foreach ( $categories as $category ) {
            echo '<li> Category ' . $category->name . ' genres are ' . implode(",",get_term_meta( $category->term_id, 'genre', false )) . '</li>';
        echo '</ul>';
To use them both together,
$categories = get_categories_by_genre('romance');
And you should see something like this when you view a post or page. That’s it ! Now your WordPress categories have genres available.    
Enjoyed the content ? Share it with your friends !
Published inDevelopmentProgramming

Be First to Comment

Leave a Reply

Your email address will not be published.