How (and Why) to Convert WordPress Tags from ‘Flat’ to ‘Hierarchical’

The following is a guest post by Thomas McGee (@ThomasEMcGee) from Writely Designed. Thomas has found that having the ability to do parent/child tags in WordPress can be useful. But, you'll have to do some work if you want to do it, since WordPress doesn't offer this by default.

WordPress tags are pivotal to the organization methodology of the platform. Some sites get by with using categories only, but for most, categories and tags are like bread and butter—an inseparable team that helps make content manageable, tidy, and easy to find for readers.

Whether you're a WordPress newbie or experienced professional, it's likely that you've experienced the difference between the two WordPress organizational tools. Categories are "hierarchical", meaning they are able to hold parent items and sub-items. Also, categories maintain a running checkbox "list" of all the categories you've previously created and used.

Tags, on the other hand, are "flat" and do not contain any parent or child items—everything is on the same "level" so to speak. Rather than the "checklist" format afforded to the category, tags display a simple text box where you're able to enter comma-separated items. In a fashion similar to categories, you're able to view "most used tags" in a "tag cloud" format. The trouble with this, if you're interested in having fine-tuned control over the organization of your tags, is that this can become messy in the blink of an eye.

Categories are typically used for broad or generic topics, whereas tags get a little more specific. If we were to use the example of a news website, it might use a category for "Technology" and a tag for something like "Apple."

Explanations aside, in this article I'll explore how (and why) to convert standard "flat" WordPress tags into a hierarchical, category-like format.

Why Would You Want 'Hierarchical' Tags?

"I already have categories which operate like categories, why would I want my tags to operate like categories as well?"

I would've asked the same question had I not tried it out for myself in one of the WordPress themes I was building. However, in the time I've experimented with the concept, I've found only advantages. Advantages, that is, if organization and maintainability are important to your (or your user's) WordPress structure.

One of the useful traits of tags is that they are a means separate from categories for organizing content. As I touched on above, categories work great for the most generalized content and tags, the specific.

While tags are excellent for quickly rattling off topics relevant to the article, it's easy for tags to become cluttered, messy, and inconsistent. Multiply this by ten or a hundred if you have a wide variety of contributors with the ability to add tags themselves.

An aid to the problem is to convert tags over to a hierarchical format. The methodology remains the same; that is, categories stick to general topics and tags specific, but with the added benefit of more structure for the upkeep of tags.

"But I like being able to just type out tags in succession."

While a hierarchical tag meta box will not allow you to do the normal comma-separated entry method, you're still able to type out titles in rapid succession. This is possible because WordPress is smart enough to know when a category (or tag, in our case) already exists, and will check that item rather than creating a duplicate. The result is the ability for you or your user to type out tags in succession or click the checkboxes to assign the relevant tags to the post.

Type to add new parent tag

There is one caveat with this approach, however. For the items to "check" as you enter them, they must be parent tags, not children. If you type out a child tag name, it will simply create it as a new parent tag.

"I already have a ton of tags on my WordPress site, won't this break permalinks?"

If this method is implemented properly, no. One of the nice things about this concept is that you can try it out for a while, then switch back to the old-school style of tags without losing any previous tag structures. If you decide to go back and forth a couple of times, it's possible you'll lose some hierarchical data, but that's an unlikely scenario.

Proper implementation of hierarchical tags maintains permalink structure.

As noted previously, the largest benefit of hierarchizing your tags is the added organizational control from within the admin. When it comes to the front-end user navigation and use of tagged articles, there won't be a visible difference.

On the back-end, however, you can begin to build an understandable, readable, and manageable library of tags. It's extremely helpful for your own use, and even more so if you're running a site that accepts guest contributors. This structured system is a massive aid in the prevention of redundant tags. If you ever decide to scroll through your active tags and see entries like "responsive," "Responsive Design," "RWD," "responsive web design," and so on, you know exactly what I mean. With the hierarchical tag approach, you could create a "Techniques" parent tag with a "Responsive Web Design" child tag and writers would always know where to look when writing on that particular topic.

If you're familiar with the Evernote approach to organizing tags, you may notice that the methodology of hierarchical tags very much follows the same idea.

Evernote handles tags with a hierarchical methodology

The benefit this affords to Evernote users is the ability to build an endless library of topics that follow a logical pattern. This helps make items easier to find when scanning or browsing, while retaining the ability to search for a specific tag.

How to Convert WordPress' Built-in Flat Tags to the Hierarchical Equivalent

If you're sold on the idea of hierarchical tags, or just want to give it a try, the method for making the change involves overriding WordPress' built-in tags with an adapted version of your own.

The first step is to maintain the taxonomy's rewrite to prevent permalinks from getting disrupted:

// Maintain the built-in rewrite functionality of WordPress tags

global $wp_rewrite;

$rewrite =  array(
  'hierarchical'              => false, // Maintains tag permalink structure
  'slug'                      => get_option('tag_base') ? get_option('tag_base') : 'tag',
  'with_front'                => ! get_option('tag_base') || $wp_rewrite->using_index_permalinks(),
  'ep_mask'                   => EP_TAGS,
);

Next, we need to specify the labels shown for the taxonomy. In this case, we're maintaining the tag's original name:

// Redefine tag labels (or leave them the same)

$labels = array(
  'name'                       => _x( 'Tags', 'Taxonomy General Name', 'hierarchical_tags' ),
  'singular_name'              => _x( 'Tag', 'Taxonomy Singular Name', 'hierarchical_tags' ),
  'menu_name'                  => __( 'Taxonomy', 'hierarchical_tags' ),
  'all_items'                  => __( 'All Tags', 'hierarchical_tags' ),
  'parent_item'                => __( 'Parent Tag', 'hierarchical_tags' ),
  'parent_item_colon'          => __( 'Parent Tag:', 'hierarchical_tags' ),
  'new_item_name'              => __( 'New Tag Name', 'hierarchical_tags' ),
  'add_new_item'               => __( 'Add New Tag', 'hierarchical_tags' ),
  'edit_item'                  => __( 'Edit Tag', 'hierarchical_tags' ),
  'update_item'                => __( 'Update Tag', 'hierarchical_tags' ),
  'view_item'                  => __( 'View Tag', 'hierarchical_tags' ),
  'separate_items_with_commas' => __( 'Separate tags with commas', 'hierarchical_tags' ),
  'add_or_remove_items'        => __( 'Add or remove tags', 'hierarchical_tags' ),
  'choose_from_most_used'      => __( 'Choose from the most used', 'hierarchical_tags' ),
  'popular_items'              => __( 'Popular Tags', 'hierarchical_tags' ),
  'search_items'               => __( 'Search Tags', 'hierarchical_tags' ),
  'not_found'                  => __( 'Not Found', 'hierarchical_tags' ),
);

Now that we've worked to maintain the tag's basic structure, we can change the taxonomy structure from flat to hierarchical:

// Override structure of built-in WordPress tags

register_taxonomy( 'post_tag', 'post', array(
  'hierarchical'              => true, // Was false, now set to true
  'query_var'                 => 'tag',
  'labels'                    => $labels,
  'rewrite'                   => $rewrite,
  'public'                    => true,
  'show_ui'                   => true,
  'show_admin_column'         => true,
  '_builtin'                  => true,
) );

If you would like to change the name of WordPress' built-in tags to something else, you can do so without disturbing permalinks by modifying everything within the $labels array.

// Let's change Tags to Topics

$labels = array(
  'name'                  => _x( 'Topics', 'taxonomy general name' ),
  'singular_name'         => _x( 'Topic', 'taxonomy singular name' ),
  'search_items'          => __( 'Search Topics' ),
  'all_items'             => __( 'All Topics' ),
  'parent_item'           => __( 'Parent Topic' ),
  'parent_item_colon'     => __( 'Parent Topic:' ),
  'edit_item'             => __( 'Edit Topic' ),
  'update_item'           => __( 'Update Topic' ),
  'add_new_item'          => __( 'Add New Topic' ),
  'new_item_name'         => __( 'New Topic Name' ),
  'not_found'             => __( 'No topics found.' ),
  'menu_name'             => __( 'Topics' ),
);

Putting Everything Together

Once we've created all the pieces we need to modify the tag taxonomy, we can tie everything together with a function tied to the init WordPress action hook.

If you're working on a WordPress theme (or child-theme) you can simply add this to your functions.php file or functionality plugin:

function wd_hierarchical_tags_register() {

  // Maintain the built-in rewrite functionality of WordPress tags

  global $wp_rewrite;

  $rewrite =  array(
    'hierarchical'              => false, // Maintains tag permalink structure
    'slug'                      => get_option('tag_base') ? get_option('tag_base') : 'tag',
    'with_front'                => ! get_option('tag_base') || $wp_rewrite->using_index_permalinks(),
    'ep_mask'                   => EP_TAGS,
  );

  // Redefine tag labels (or leave them the same)

  $labels = array(
    'name'                       => _x( 'Tags', 'Taxonomy General Name', 'hierarchical_tags' ),
    'singular_name'              => _x( 'Tag', 'Taxonomy Singular Name', 'hierarchical_tags' ),
    'menu_name'                  => __( 'Taxonomy', 'hierarchical_tags' ),
    'all_items'                  => __( 'All Tags', 'hierarchical_tags' ),
    'parent_item'                => __( 'Parent Tag', 'hierarchical_tags' ),
    'parent_item_colon'          => __( 'Parent Tag:', 'hierarchical_tags' ),
    'new_item_name'              => __( 'New Tag Name', 'hierarchical_tags' ),
    'add_new_item'               => __( 'Add New Tag', 'hierarchical_tags' ),
    'edit_item'                  => __( 'Edit Tag', 'hierarchical_tags' ),
    'update_item'                => __( 'Update Tag', 'hierarchical_tags' ),
    'view_item'                  => __( 'View Tag', 'hierarchical_tags' ),
    'separate_items_with_commas' => __( 'Separate tags with commas', 'hierarchical_tags' ),
    'add_or_remove_items'        => __( 'Add or remove tags', 'hierarchical_tags' ),
    'choose_from_most_used'      => __( 'Choose from the most used', 'hierarchical_tags' ),
    'popular_items'              => __( 'Popular Tags', 'hierarchical_tags' ),
    'search_items'               => __( 'Search Tags', 'hierarchical_tags' ),
    'not_found'                  => __( 'Not Found', 'hierarchical_tags' ),
  );

  // Override structure of built-in WordPress tags

  register_taxonomy( 'post_tag', 'post', array(
    'hierarchical'              => true, // Was false, now set to true
    'query_var'                 => 'tag',
    'labels'                    => $labels,
    'rewrite'                   => $rewrite,
    'public'                    => true,
    'show_ui'                   => true,
    'show_admin_column'         => true,
    '_builtin'                  => true,
  ) );

}
add_action('init', 'wd_hierarchical_tags_register');

Once this is implemented, you should see the hierarchical tag structure within your site's admin.

Add a New Taxonomy to WordPress

Let's say you like the whole organizational approach displayed with hierarchical tags. However, the idea of modifying built-in tags simply won't work for your configuration. An alternate solution is to create a completely new taxonomy and use it above and beyond categories and tags.

Following many of the concepts explored above, you could add a hierarchical "Topics" taxonomy with the settings of your choosing.

// Register Custom Taxonomy
function wd_topics_taxonomy() {

  $labels = array(
    'name'                       => _x( 'Topics', 'Taxonomy General Name', 'hierarchical_tags' ),
    'singular_name'              => _x( 'Topic', 'Taxonomy Singular Name', 'hierarchical_tags' ),
    'menu_name'                  => __( 'Taxonomy', 'hierarchical_tags' ),
    'all_items'                  => __( 'All Items', 'hierarchical_tags' ),
    'parent_item'                => __( 'Parent Item', 'hierarchical_tags' ),
    'parent_item_colon'          => __( 'Parent Item:', 'hierarchical_tags' ),
    'new_item_name'              => __( 'New Item Name', 'hierarchical_tags' ),
    'add_new_item'               => __( 'Add New Item', 'hierarchical_tags' ),
    'edit_item'                  => __( 'Edit Item', 'hierarchical_tags' ),
    'update_item'                => __( 'Update Item', 'hierarchical_tags' ),
    'view_item'                  => __( 'View Item', 'hierarchical_tags' ),
    'separate_items_with_commas' => __( 'Separate items with commas', 'hierarchical_tags' ),
    'add_or_remove_items'        => __( 'Add or remove items', 'hierarchical_tags' ),
    'choose_from_most_used'      => __( 'Choose from the most used', 'hierarchical_tags' ),
    'popular_items'              => __( 'Popular Items', 'hierarchical_tags' ),
    'search_items'               => __( 'Search Items', 'hierarchical_tags' ),
    'not_found'                  => __( 'Not Found', 'hierarchical_tags' ),
  );
  $args = array(
    'labels'                     => $labels,
    'hierarchical'               => true,
    'public'                     => true,
    'show_ui'                    => true,
    'show_admin_column'          => true,
    'show_in_nav_menus'          => true,
    'show_tagcloud'              => true,
  );
  register_taxonomy( 'topics', array( 'post' ), $args );

}

// Hook into the 'init' action
add_action( 'init', 'wd_topics_taxonomy', 0 );

The main challenge with this method is that upon creating your new taxonomy, you would then need to integrate it's front-end visibility into your theme or child theme, whereas most themes already have the accommodation for tags built-in.

Additional Resources

If you find you need to push the abilities beyond the example shown here, these additional resources may come in handy.

Download as a Plugin

If you're more for the plugin approach rather than the hands-on customization angle, you can download the code example given in this article as a WordPress plugin.

Conclusion

WordPress continues as arguably the most powerful, customizable, and extensible CMS in existence. With everything covered here, hopefully, this will jumpstart some ideas into how you can optimize the organizational abilities of WordPress and push the platform even further.