Adding Custom Taxonomies To WordPress – Without Plugins

How To Add custom taxonomies to WordPress and set the graphical editor for the description of those custom taxonomies in the admin area

custom taxonomies on wordpress no plugin
Reading Time: 7 minutes

There’s plenty of articles around that allow you to add custom taxonomies to WordPress – many of which talk about the use of plugins to do to it. A plugin is necessary if you’re not comfortable writing your own code – but I tend to a lot of stuff myself where possible and custom taxonomies are no different.

What Is A WordPress Taxonomy?

A WordPress taxonomy is, simply put, a way of grouping WordPress posts. WordPress comes with 2 taxonomies already – they are Categories and Tags. It’s a way of grouping posts together into logical collections.

Why Would You Want Additional WordPress Taxonomies?

I’m going to spend the rest of this post looking at a real world example. It’s based on WooCommerce, but the method is exactly the same for normal WordPress installations.

A Real World Example

In my example, I am running an eCommerce store at https://www.reptilesrock.net – which is set up to provide reptile related merchandise. Since it’s an eCommerce store, I want to group the products (which are essentially just a custom WordPress post type) in various different ways.

To begin with, I want to group my products based on the product type itself. For example; Mug, T-Shirt, Duvet Cover.

For this, the existing taxonomy of category works well. So I just created a bunch of categories to satisfy this grouping.

But I also want to group each product depending on which Reptile it relates to. I could use tags since that’s available still, but I actually may want to use tags for something else later. So now I have no available options for grouping the products together.

For example, a product can be in the Mug category, but also the Iguana ‘group’ too. This is important because a reptile lover may know they want to buy something with an Iguana on it, but not know what actual product they want. So I can create a menu that displays all posts that are in the Iguana collection. Or they may know that want to buy a Mug, but aren’t yet sure which reptile they want.

Incidentally, I used the excellent WOOF Products Filter plugin to actually do more filtering – but each product needs to be in different taxonomies to make this work.

Creating the Taxonomy

I do my work that relates to my site overall in the mu-plugins directory. I don’t need to activate anything and the code runs before much of WordPress is initiated so I know I can set things up before anything else relies on it. But you could write this one as a plugin instead if you prefer.

I also do all my work in separate PHP classes. I do this because it helps encapsulate my work from everything else in WordPress and is generally a pretty good way to do things. I’ve never looked at Namespaces (I left full time development over 10 years ago but still play with WordPress. Which means if I can do it – so can you :))

So, to start from the beginning with how I do it then – from mywordpresshome/wp-content/mu-plugins;

If mu-plugins doesn’t exist;

cd [mywordpresshome]/wp-content
mkdir mu-plugins
cd mu-plugins

Then you’ll need to create and edit a new file. I use Visual Studio Code under WSL to do my WordPress development. You can find out how to do that here. I’m going to assume you’re using the same because it’s a good way to do it – but you can replace my VSCode specific instructions with whichever IDE or editor you use.

code .

Then create a new file using File->New File – I called mine rr_core.php – you can call it whatever you like so long as it ends in .php as this tells WordPress to automatically load and execute your code.

Once that’s created you can enter your own code, which will be executed every time a WordPress page, or post is loaded. I’ve prepared a snippet below that we’ll dig deeper into to help you create your taxonomies;

<?php
/*
    Plugin Name: ReptilesRock Core Additional Features
    Description: Adds additional bespoke features to ReptilesRock.net - in particular alternative fulfilment locations. To achieve this, set up multiple products with different fulfilment locations. Link them together and mark any that are alternatives only as hidden
    Version: 0.1
    License: Proprietary
 (only useful to me!)
    Author: Steve Brown
    Author URI: https://www.most-useful.com
*/
class RRock {
  function __construct() {
    add_action( 'init', array($this, 'add_RR_taxonomies'));
  }

  function add_RR_taxonomies() {
        $design_labels = array(
            'name'                       => 'Designs',
            'singular_name'              => 'Design',
            'menu_name'                  => 'Designs',
            'all_items'                  => 'All Designs',
            'parent_item'                => 'Parent Design',
            'parent_item_colon'          => 'Parent Design:',
            'new_item_name'              => 'New Design Name',
            'add_new_item'               => 'Add New Design',
            'edit_item'                  => 'Edit Design',
            'update_item'                => 'Update Design',
            'separate_items_with_commas' => 'Separate Designs with commas',
            'search_items'               => 'Search Designs',
            'add_or_remove_items'        => 'Add or remove Designs',
            'choose_from_most_used'      => 'Choose from the most used Designs',
        );
        $args = array(
            'labels'                     => $design_labels,
            'hierarchical'               => true,
            'public'                     => true,
            'show_ui'                    => true,
            'show_admin_column'          => true,
            'show_in_nav_menus'          => true,
            'show_tagcloud'              => true,
            'rewrite'			=> true,
        );    
        register_taxonomy( 'show-by-design', 'product', $args );

        $reptile_labels = array(
            'name'                       => 'Reptiles',
            'singular_name'              => 'Reptile',
            'menu_name'                  => 'Reptiles',
            'all_items'                  => 'All Reptiles',
            'parent_item'                => 'Parent Reptile',
            'parent_item_colon'          => 'Parent Reptile:',
            'new_item_name'              => 'New Reptile Name',
            'add_new_item'               => 'Add New Reptile',
            'edit_item'                  => 'Edit Reptile',
            'update_item'                => 'Update Reptile',
            'separate_items_with_commas' => 'Separate Reptiles with commas',
            'search_items'               => 'Search Reptiles',
            'add_or_remove_items'        => 'Add or remove Reptiles',
            'choose_from_most_used'      => 'Choose from the most used Reptiles',
        );
        $args = array(
            'labels'                     => $reptile_labels,
            'hierarchical'               => true,
            'public'                     => true,
            'show_ui'                    => true,
            'show_admin_column'          => true,
            'show_in_nav_menus'          => true,
            'show_tagcloud'              => true,
            'rewrite'			=> true,
        );    
        register_taxonomy( 'show-by-reptile', 'product', $args );
    }
}
new RRock();

Let’s dissect (the code, not the reptiles) that a little bit.

At the top we have some comments. These give WordPress some information it can use within the user interface if you want to look at the Must Use Plugins section of your administration screens.

Below that, we have a PHP class definition (which can be used to create PHP objects). A class is a blueprint that enables a programming language to create different type of objects. This is called Object Oriented Programming (OOP) and PHP can be used as an OO Language or a Procedural language depending on what you’re writing. Much of WordPress is written procedurally, but some is Object Oriented. So you can combine the two paradigms easily.

A class consists of different methods (sort of like functions) which can be called. The beauty of using an OO approach like this (one of the many benefits) is that I can call any of my methods whatever I like. There’ll never be a clash with anything else in WordPress because my method is always associated with the RRock class.

The first function is called __construct – which will automatically be run when a new RRock object is created. We’ll create a new RRock object later but for now, let’s look at what this method tells PHP to do when a RRock is created.

When the RRock object is first created (also called instantiated) – it calls one WordPress function. That is the add_action function, which is a WordPress specific function that allows us to hook into WordPress functionality to add our own logic.

In this case we’re hooking into the ‘init’ hook. WordPress calls various hooks at different times throughout the generation of a page. The init hook is called quite early in the page building process. It’s beyond the scope of this document to go too deep into the different WordPress and WooCommerce hooks. But init is called after plugins are loaded and the system is pretty ready to go.

The second part of the add_action command tells WordPress which of our methods to call when the init hook is triggered. In our case we want it to call our add_RR_taxonomies method. We have to put it in that format ( ie, array($this, ‘add_RR_taxonomies’) ) because we’ve used a class method to add our logic rather than the procedural method. $this refers to ‘this’ object – ie an object of class RRock. This is how we can call our methods whatever we like and know that won’t clash with other parts of WordPress because we’re literally saying this add_RR_taxonomies method – not any other.

So, we’ve told WordPress that when it’s starting up, it needs to create the taxonomies for us and register them for use. It does this every time a WordPress page is loaded – which is why plugins can slow your website down, because if they’re not efficiently written they’ll go through a lot of processor power every single time a WordPress post or page is visited.

Our add_RR_taxonomies method then creates 2 different taxonomies. I chose to use hierarchical taxonomies (category style) rather than non-hierarchical (tag style) just because I prefer the user interface for category style on the product screens.

Setting Up The TinyMCE Editor For Custom Taxonomies

When I first created my new taxonomies, I noticed that categories (the original taxonomy that comes pre-installed with WordPress) has a nice graphical editor for the category. I want to use fairly large category descriptions within my WooCommerce store, as I use them to populate the product description by default.

The new taxonomy only comes with a normal text entry box. Not very helpful. Personally, I think this part of the tutorial should become obsolete because code should be added to WordPress Core to allow the text box to be replaced with an editor just by passing a parameter to the taxonomy registration. But there may be reasons why that’s not done – and at some point I may try to find out 🙂

But for now – here’s how to add a graphical editor to the taxonomy description text entry box when editing custom taxonomies;

function __construct() {

  [..]
  add_action( 'show-by-design_edit_form_fields', array($this, 'edit_taxonomy_form_fields') );
  [..]
}

[..] indicates additional code lines and is used to illustrate that your constructor may have other method calls before and/or after this line.

You’ll notice that the beginning of the action is called show-by-design which is the name of one of the taxonomies I registered above (with the register_taxonomy( ‘show-by-design‘, ‘product’, $args ) command). If you called your taxonomy something else (and you probably should) change the action here to match.

Then we need to create a method on our RRock class called edit_taxonomy_form_fields – you can call it whatever you like, but it must match the add_action definition above.

Then your edit_taxonomy_form_fields method will look something like this;

function edit_taxonomy_form_fields( $term ) {
  // The $term is the taxonomy term that we're wanting to edit - so you can use
  // This method for more than one taxonomy if you choose by checking which $term it's called
?>
  <tr valign="top">
     <th scope="row">Description</th>
        <td>
           <?php wp_editor(html_entity_decode($term->description), 'description', array('media_buttons' => true, 'textarea_rows' => 10)); ?>
           <script>
              jQuery(window).ready(function(){
                jQuery('label[for=description]').parent().parent().remove();
              });
            </script>
        </td>
      </tr>
<?php

The above code tells WordPress to output the ‘wp_editor’ (which is a function that generates the necessary HTML code to produce the graphical editor box) and then a small piece of script which removes the original textarea box.

Other Instructions Exist – But Don’t Work With WooCommerce 4.7

There’s various instructions on forums for how to replace the standard text box on the custom taxonomy edit screen – but although much simpler, they don’t actually work in the current WooCommerce version (and possibly earlier) since the hooks have been deprecated.

The method above does work on WooCommerce 4.7

Conclusion

If you’ve enjoyed this post and it’s been helpful please leave a comment below or feel free to share it on Twitter or elsewhere. If it didn’t make any sense or left you bewildered or confused please do ask below and I’ll try to help!

Featured image from mcmurryjulie of Pixabay. Permission or attribution not required (but attribution given anyway!)