Taxonomies in WordPress

What are taxonomies in WordPress? For those who don't know, and for those who think they know everything about taxonomies, it will be useful to read this article. I'll take a detailed look at what's underneath that strange word, what it means in WordPress, and how taxonomies work. I think everyone will find something useful in this article.

Read also how posts in WordPress are arranged.
Read also how meta-fields in WordPress works.

About taxonomies

The word "Taxonomy" comes to us, as always, from the Greek: taxis - arrangement, nomos - law, principle. I.e. Taxonomy is the principle of arrangement of something. For WordPress, it's the principle of arrangement posts and meybe other entities.

Figuratively, taxonomies can be compared to folders on a computer: where files are stacked. You go into a folder, you see a list of files. In WordPress is similar: go to a taxonomy (category), you see a list of posts in it.

The content structure in WordPress is very simple: content consists of posts and taxonomies that link those posts - basically, that's it! Content also includes comments and files, but both are part of the posts. There are also users, but that's sort of a separate entity, not content. So it turns out that taxonomies only collect posts (link it to each other).

It's worth noting that in WordPress, "Taxonomy" is just a name, i.e. there is no taxonomy as such - there is only a record of its existence in php code. And something real about a taxonomy is its elements (terms). For example, take category taxonomy - it's just a name - a record in a PHP variable, and the real data of the taxonomy are the created categories - its elements. For example, if you do not create any category, then conventionally we can say that there is no taxonomy (it is empty) - it is not written anywhere in the database, but exists only in PHP variables, where the name and properties (options) of taxonomy, and such a variable is created on the fly during page generation. Posts are linked to taxonomy elements, not to taxonomy itself. Since the posts are linked not with taxonomy, but with its elements, then all further work with taxonomy is work with its elements.

Taxonomy elements are called terms.

Types of Taxonomies

I mentioned above that when you create a taxonomy, you set properties for it. One of the most important properties is the type of taxonomy. So, taxonomies are divided into two types:

  1. Hierarchical (tree-like) - for example categories.

  2. Flat (Linear) - for example tags.

Distinctions. Elements of tree-like taxes can be parent and child, i.e. some elements are as if nested in others. And the elements of flat taxes are always by themselves, i.e. they all are on the same level, and therefore do not depend on each other.

Schematically it looks like this:

Basic WordPress Taxonomies

By default, there are five taxonomies in WordPress:

  • category - Post Categories.

  • post_tag - Post Tags.

  • post_format - hidden taxonomy. The terms of this taxonomy are post formats.

  • nav_menu - hidden taxonomy. The terms of this taxonomy are created navigation menus, the posts are attached to them (menu links). If an custom link is created in the menu, its data is placed in the wp_posts table with nav_menu_item post_type, and the post is attached to the nav_menu term. All link options (URL, anchor, etc.) are stored in the post's meta-fields. The same thing happens if an element of another taxonomy is placed in that nav_menu taxonomy, such as a category - a post is also created in the wp_posts table, and the data is placed in the post's meta-fields. This system is clumsy and not very practical, but very convenient, independent, well extensible and, most importantly, very simple.

  • link_category - sections for links, which are disabled in recent versions of WordPress. Read more about enabling links.

Creating your own Taxonomies

Taxonomy is created with register_taxonomy() function or corresponding plugin, for example Custom Post Type UI. At that, as I've already mentioned, nothing is added to the database, only taxonomy description and its properties are created in global PHP variable and in Rewrite rules. As soon as at least one taxonomy element (term) has been created, a record of the new term appears in the database, and you can already attach posts to it.

When you create a taxonomy you can specify a variety of properties (options), for example:

  • Type — hierarchical or flat.

  • Post type for which the taxonomy is created — Then when you edit a post in admin panel a taxonomy block will appear where you can add a post to taxonomy (link a post to a tax term). For example, such block is a block of categories when editing a post.

  • What the taxonomy URL will look like — This "stuff" is called a Rewrite Rule (human-readable URI).

  • You can create hidden taxonomy, then it won't be visible anywhere, especially in the admin panel, but you can use it in some unconventional way to group entries or do something else. So, for example, gallery plugins link galleries or individual images.

  • other options... For a complete list see the description of function register_taxonomy().

Why do I need to create custom taxonomies?

If you use Categories everywhere, your code will pretty quickly turn into mush. As a result, extending the functionality of the site will become increasingly difficult, and the site speed will be slower and slower.

Suppose we have a real estate site with a houses post type. We need to distribute the houses by location, price category, number of rooms, etc. All this can be done by using standard categories, and it is very simple: we create category: cheap houses, houses in New York, homes with 3 rooms, etc. With the increasing number of houses and categories for them, looking for the right category will be harder, and with the increasing functions of the site will be more and more difficult to manage all this - as a result will be confusion and trivial errors in the filling and in the code.

In this case, the new taxonomies will simplify things many times over. If we have separate taxonomies for each group of properties, for example: sale_price, number_of_bedrooms, location, then we will have separate blocks when editing a house, in which it is more convenient to navigate and choose where and what to place. And, it will be possible to create separate queries for each property type. All of this will have separate names, which will make it easier to write, understand and speed up queries.

This is what a query might look like when you need to get cheap houses in New York:

$houses = get_posts( array(
	 'posts_per_page' => 8,
	 'orderby'        => 'rand',
	 'post_type'      => 'houses',
	 'sale_price'     => 'cheap',
	 'location'       => 'New York'
) );

But if you do the same with categories, the code will be larger, less clear and less faster.

Creating new taxonomy is often convenient and profitable, in the long run.

Structure: Taxonomy Database Tables

In the WordPress database, four tables are responsible for taxonomies. Let's break down each one.

wp_terms

Contains taxonomy elements (terms) and basic information about them. This is the main table of taxonomy elements. Depends on the wp_term_taxonomy table - they always go together.

wp_terms Table

term_id
Unique term ID (table row ID).
name
The name of the term, ex: "Author functions".
slug
The label (slug) of the term, ex: "author-functions".
term_group
Deprecated field, no longer used.

wp_term_taxonomy

Contains additional data about the taxonomy element, in particular important data - which taxonomy the term belongs to (taxonomy field) and which post it is associated with (term_taxonomy_id field).

wp_term_taxonomy table

term_taxonomy_id
ID of the row in this table. Corresponds to the term_taxonomy_id field from wp_term_relationships table.
term_id
term ID. Corresponds to the term_id field from wp_terms table.
taxonomy
The name of the taxonomy the term belongs to.
description
A description for the term.
parent
Contains the ID of the parent term (hierarchical taxes).
count
Contains the number of entries (posts) in the term.

If you install WP 4.2 or higher, the database will have one entry in wp_term_taxonomy for each term in wp_terms. This means that the values of the term_taxonomy_id and term_id fields will always be the same.

Before WP 4.4, there could be multiple entries for the same term. This happened when a term was created, but another taxonomy already had a term with the same name and slug. In this case a new term in wp_terms was not created, but instead a new entry was created in wp_term_taxonomy table, which meant that that term belonged to another taxonomy. It was for such 'multi-terms' that additional fields were needed: description, parent and count to make them different for one term and its different taxonomies.

Since WP version 4.2, all "merged" terms have been separated and now each term has its own unique ID, and even when the name and slug of terms from different taxonomies match, the same entries are created in the wp_terms table. This approach is much clearer and easier, but WordPress developers could not think of doing it that way right away, which is a pity.

Today the old logic is still supported and it means that all requests are built using wp_term_taxonomy table.

Technically wp_term_taxonomy table is not needed since version 4.2, because the logic of data storage has changed and the new logic fields taxonomy, description, parent and count can be placed in the wp_terms table and rewrite all related engine code and all plugins. But this is not easy, given the full backward compatibility of WordPress (hello to those who do not like to update WP). So for now it's impossible to enjoy the new logic to the fullest.

When you updated to WP 4.2 the merged terms were split and now the split data is in the get_option('_split_terms') option. See wp_get_split_terms() function to know more about this.

wp_term_relationships

Contains relations between terms and posts. The table shows which post belongs to which term. But the term ID here is linked to a post through the term_taxonomy_id field, - why so unclear relationship is used is explained above: in the table wp_term_taxonomy.

This table contains only three fields:

wp_term_relationships table

object_id
Contains post ID (ID field value from wp_posts table). If link support enabled it will contain the link ID from wp_links table.
term_taxonomy_id
Contains the value of the same field from the wp_term_taxonomy table.
term_order

Contains the order in which the terms were specified when attaching them to the post. For example, when editing a post we gave it 2 categories and 3 tags, here is the order in which we see them (they were passed in the POST query), these values will be written here: 1, 2 for categories, and 1, 2, 3 for tags.

By default this feature is disabled for all built-in taxonomies (field contains 0). To enable it, you must specify the sort parameter when registering a taxonomy, see register_taxonomy().

wp_termmeta

Contains terms metadata. This table complements the wp_terms table.

In wp_termmeta it is customary to store any additional data about a term, for example, these can be SEO fields: title, description, or anything else.

The structure of the table is the same as the other metadata tables: wp_postmeta, wp_commentmeta, wp_usermeta. The logic of storing, caching and retrieving WP metadata is the same for all metadata types: posts, taxes, comments, users - reed special article about metadata in WordPress.

By default, this table is not used and is always empty. It is used when extending basic WP features, usually by plugins.

wp_termmeta table

meta_id
The ID of the meta field. Normally not used anywhere (used in WP core).
term_id
The ID of a term from the wp_terms table.
meta_key
The key of the meta field.
meta_value
The value of the meta field. Always a string, i.e. numbers are stored as strings and arrays in serialized form.

This table appears in WP version 4.4.

Linking all Tables

To create your own SQL queries when basic functions fail or don't do as well as you'd like, you need to know how tables are linked.

Relation of all taxonomy tables. See. full database table schema

Let's look at a SQL query that will connect all the tables with a JOIN. The query below will return all posts of type post and the data to which term each post is attached:

SELECT t.name, t.term_id, tt.taxonomy, p.ID, p.post_title, p.post_date
FROM wp_terms t
	INNER JOIN wp_term_taxonomy tt ON (t.term_id = tt.term_id)
	INNER JOIN wp_term_relationships tr ON (tt.term_taxonomy_id = tr.term_taxonomy_id)
	INNER JOIN wp_posts p ON (tr.object_id = p.ID)
WHERE p.post_type = 'post'

We get it:

By creating such queries and combining tables using JOIN, you will quickly and well understand the dependencies between taxonomy tables.

WordPress Taxonomy Functions

For a complete list, see this link, and here are some popular functions:

edit_term_link() Displays/retrieves edit term link (html tag A), link to edit the specified taxonomy element.
get_edit_term_link() Retrieves the URL for editing a given term.
get_taxonomies() Gets the list of registered taxonomies. You can restrict the list to the desired parameters.
get_taxonomy() Gets an object containing settings (data) of the specified taxonomy.
get_term() Get all Term data from database by Term ID.
get_term_by() Get all Term data from database by Term field and data.
get_term_children() Merge all term children into a single array of their IDs.
get_term_field() Get sanitized Term field.
get_term_link() Gets the URL for the archive page of a term (taxonomy item). The same as the link to the category page.
get_terms() Retrieve the terms (items) of a given taxonomy or list of taxonomies by the given parameters.
is_taxonomy_hierarchical() Whether the taxonomy object is hierarchical.
register_taxonomy() Creates a new custom WordPress taxonomy. Allows you to change an existing taxonomy.
register_taxonomy_for_object_type() Add an already registered taxonomy to an object type.
sanitize_term() Sanitize all fields of the taxonomy element (term) using the sanitize_term_field() function.
sanitize_term_field() Prepares (cleanse) term field value for use it in a text or elsewhere (depends on cleaning context).
single_term_title() Display or retrieve title for the current taxonomy (category, tag, etc.). Uses on term archive pages.
taxonomy_exists() Checks that the taxonomy name exists.
term_description() Retrieve term description.
term_exists() Check if a given taxonomy element exists. Returns either term ID or a term data if the element (term) exists.
term_is_ancestor_of() Check if a term is an ancestor of another term.
the_terms() Display the terms in a list.
unregister_taxonomy() Unregisters a taxonomy.
unregister_taxonomy_for_object_type() Remove an already registered taxonomy from an object type.
wp_count_terms() Count how many terms are in Taxonomy.
wp_delete_term() Removes a term from the database.
wp_get_term_taxonomy_parent_id() Returns the term's parent's term_ID.
wp_insert_term() Adds a new taxonomy element (term, category) into a database.
wp_update_term() Updates the term (taxonomy element) using the specified data.
get_term_meta() Retrieves metadata for a term.
add_term_meta() Adds metadata to a term.
update_term_meta() Updates term metadata.
delete_term_meta() Removes metadata matching criteria from a term.
has_term_meta() Gets all metadata of the specified taxonomy element (term).
register_term_meta() Registers a meta key for terms.

-

Taxonomies are a pretty powerful tool in WordPress, with relatively simple table logic. Once you understand how taxonomies work, you can create more complex sites.

Also, understanding how taxonomies work and how other site content (for example posts) link to them will help you understand where, how, and which taxonomy function is best to use.