Dissecting WordPress Themes Part 6: Tag Hierarchy

In the prior article (Part 5) we began our study of the archive hierarchy with categories. We found that the base of the hierarchy, archive.php, is common to all archives, including categories, tags, authors, dates, custom taxonomies, and custom post types. Creating an archive.php template file will override the index.php template, which is common to all request types. We also found that category archives specifically can be formatted by overriding archive.php with category.php and that, if needed, individual categories can be formatted using templates that include either the ID or slug of the targeted category. We also looked at the parent property that can be employed for categories to create a “tree” of categories and wrapped up by displaying the category tree list in the sidebar of our “trace” theme.

In this article, we’ll continue with another of the archive requests, tags. It turns out that tags work almost identically to categories, with the exception that tags cannot be arranged into a tree structure with parent/child relationships. Tags have the same template hierarchy levels as categories. You can override archive.php with tag.php and also format individual tag archives with either tag-[id].php or tag-[slug].php.

Create and Assign Tags

Let’s begin by creating a few tags and assigning them to our posts. First, log in to the Administration Screen and navigate to the Tags page by selecting Posts→Tags from the left menu. We’ll create three new tags named Tag 1, Tag 2, and Tag 3. We’ll give each a slug and description as shown in the table below. For each tag, fill in the appropriate fields on the Tags page and click the Add New Tag button.

Tag Name Slug Description
Tag 1 tag-1 This is the description of Tag 1.
Tag 2 tag-2 This is the description of Tag 2.
Tag 3 tag-3 This is the description of Tag 3.

When complete, the Tags page should like the screenshot below.

Tags page with new tags

The Tags page shows the tags we created along with their descriptions and slugs.

Next, we’ll assign these new tags to our posts from the prior articles. Recall that we now have 10 posts and three “Books” in the database. You can assign tags using the Posts page’s (Posts→All Posts from the menu) Quick Edit function. Just hover your mouse over each of the post titles and click the Quick Edit link. Type in the tag names separated by commas as shown in the table below. Be sure to click the Update button on the Quick Edit dialog for each post. You can leave Posts 7-10 untagged.

Post Title Tags
Post 1 Tag 1
Post 2 Tag 2
Post 3 Tag 3
Post 4 Tag 1, Tag 2
Post 5 Tag 2, Tag 3
Post 6 Tag 1, Tag 2, Tag 3

To show that our custom post type also supports tags, add the following tags to the Books as shown below. Books can be edited using the Books→Books link from the left menu.

Book Title Tags
Book 1 Tag 1
Book 2 Tag 2
Book 3 Tag 3

Display Assigned Tags

Now that we’ve created tags and assigned them to our posts, let’s take a look at the site. Navigate to the site’s home page by clicking the Visit Site link in the top toolbar (hover over the site’s name to see the link). Since we are not requesting any particular post, we see the index.php template display as shown below. Recall from the last article that we added the Category column to the table of posts. Click on the Older posts link to see the first three posts.

Index.php template showing categories

The index.php display as we left it at the end of the last article showing assigned categories.

Now, we’ll add a column to display the assigned tags after the Category column. Edit the index.php file to add a Tags column as shown below. We just need to add a column to the header row and call the get_the_tag_list() function to retrieve the tag links assigned to each post. Here’s the updated index.php code:

<?php
get_header(); 
echo "<p>In index.php</p>\n";

if (have_posts()) {   // if there are posts
	echo "<table>\n";
	echo "<tr><th>ID</th><th>Title</th><th>Format</th><th>Category</th>",
			"<th>Tags</th></tr>\n";
	while (have_posts()) {   // start the loop
		the_post();
		echo "<tr>";
		echo "<td>", get_the_ID(), "</td>";
		echo "<td><a href='", get_permalink(), "'>",
				get_the_title(), "</a></td>";
		$postFormat = get_post_format();
		echo "<td>", $postFormat ? $postFormat : 'standard', "</td>";
		echo "<td>", get_the_category_list(', '), "</td>";
		echo "<td>", get_the_tag_list('', ', '), "</td>";
		echo "</tr>\n";
		}
	echo "</table>\n";
	next_posts_link("Older posts");
	echo "<br/>";
	previous_posts_link("Newer posts");
} else {
	echo "<p>no posts to display</p>\n";
}
    
get_sidebar();
get_footer();
?>

We are passing two parameters to get_the_tag_list(). The first specifies that there is no output before the list, while the second specifies that we want a comma and space between tags. You could also pass a third parameter for a string to come after the list, but we left that at its default of null. Refresh the home page to see the output below.

Index.php template with tag display

Revised index.php template with a new column to display assigned tags.

One thing to note here is that there is no Untagged tag that corresponds to the Uncategorized category. If there are no tags assigned to a post, the get_the_tag_list() call just returns a null string. Clicking the Older posts link, we see the second page of results for the first three posts.

Older posts with assigned tags

The Older posts link displays the first three posts with their assigned tags.

Hover your mouse over the tags on this page and notice the URLs that are generated. They are of the form http://localhost/lab1/tag/[slug]/. The tag component of the URL indicates that we are requesting a tag archive while the [slug] component indicates the specific tag requested. Click on one of the tags in the list, say Tag 1. The first thing to note is that the archive.php template executes to fulfill the request. Recall that we added archive.php in the last article. Because we a requesting a tag archive, archive.php overrides index.php. To prove this to yourself you can temporarily rename archive.php and refresh the page. You should see index.php execute as the fallback.

Let’s add the tag list to archive.php as we did with index.php. The new code follows.

<?php
get_header(); 
echo "<p>In archive.php</p>\n";

if (have_posts()) {   // if there are posts
	echo "<table>\n";
	echo "<tr><th>ID</th><th>Title</th><th>Format</th><th>Category</th>",
			"<th>Tags</th></tr>\n";
	while (have_posts()) {   // start the loop
		the_post();
		echo "<tr>";
		echo "<td>", get_the_ID(), "</td>";
		echo "<td><a href='", get_permalink(), "'>",
				get_the_title(), "</a></td>";
		$postFormat = get_post_format();
		echo "<td>", $postFormat ? $postFormat : 'standard', "</td>";
		echo "<td>", get_the_category_list(', '), "</td>";
		echo "<td>", get_the_tag_list('', ', '), "</td>";
		echo "</tr>\n";
		}
	echo "</table>\n";
	next_posts_link("Older posts");
	echo "<br/>";
	previous_posts_link("Newer posts");
} else {
	echo "<p>no posts to display</p>\n";
}
    
get_sidebar();
get_footer();
?>

Also note from the snapshot above that our Book 1 post is not included in the list even though it was assigned to Tag 1. This is due to the code we put in functions.php to merge the posts from our Books custom post type with our other posts. This code currently checks that we are requesting either the home page or a category page as shown below. However, we are now requesting a tag archive page so the test fails and the posts and Books are not merged.

<?php
add_action('after_setup_theme', 'myThemeSetup');
function myThemeSetup() {
	// add post formats support
	add_theme_support('post-formats',
		array('aside', 'audio', 'chat', 'gallery',
			'image', 'link', 'quote', 'status', 'video'));
}

add_action('init', 'myPostTypes');
function myPostTypes() {
	register_post_type ('my_book',
		array ('labels' => array (
				'name' => 'Books',
				'singular_name' => 'Book'),
			'public' => true,
			'has_archive' => true,
			'rewrite' => array ('slug' => 'books'),
			'supports' => array ('title', 'editor', 'author', 'excerpt',
				'revisions', 'comments', 'post-formats'),
			'taxonomies' => array ('category', 'post_tag')
			));
}

add_action('pre_get_posts', 'addMyPostTypesToQuery');
function addMyPostTypesToQuery($query) {
	if ( (is_home() || is_category()) && $query->is_main_query() ) {
		$query->set('post_type', array('post', 'my_book'));
	}
}
?>

While we could just add another condition to also merge posts on tag archives with the is_tag() call, let’s go ahead and merge the posts on all queries by removing the page type part of the condition altogether. This results in the code below:

add_action('pre_get_posts', 'addMyPostTypesToQuery');
function addMyPostTypesToQuery($query) {
	if ( $query->is_main_query() ) {
		$query->set('post_type', array('post', 'my_book'));
	}
}

Now, refresh the tag archive page for Tag 1 to see the results of these two changes. We can now verify that we are looking at just posts assigned to Tag 1. We also now see Book 1 included in the list.

Archive.php with tag display

The archive.php template now displays assigned tags for all posts and Books tagged with Tag 1.

Click on Tag 2 and Tag 3 to see that they also use archive.php and display posts assigned to their respective tags. Note that since there is no Untagged tag, there is no way to request a list of all posts that have no tag assigned as there is with categories.

Exercise the Hierarchy

Now we’ll run through the tag hierarchy as we did with categories in the last article in the series. Copy the archive.php file to tag.php. Update the trace message to In tag.php and add messages to display the tag name and description. Also, remove the Category and Tags columns from the table just to show some formatting changes. Here is the resulting code:

<?php
get_header(); 
echo "<p>In tag.php</p>\n";
echo "<p>Browsing tag ", single_tag_title(), "</p>\n";
echo tag_description(), "\n";
	
if (have_posts()) {   // if there are posts
	echo "<table>\n";
	echo "<tr><th>ID</th><th>Title</th><th>Format</th></tr>\n";
	while (have_posts()) {   // start the loop
		the_post();
		echo "<tr>";
		echo "<td>", get_the_ID(), "</td>";
		echo "<td><a href='", get_permalink(), "'>",
				get_the_title(), "</a></td>";
		$postFormat = get_post_format();
		echo "<td>", $postFormat ? $postFormat : 'standard', "</td>";
		echo "</tr>\n";
		}
	echo "</table>\n";
	next_posts_link("Older posts");
	echo "<br/>";
	previous_posts_link("Newer posts");
} else {
	echo "<p>no posts to display</p>\n";
}
    
get_sidebar();
get_footer();
?>

We’re calling single_tag_title() to display the tag name and tag_description() to get the tag’s description. Select one of the tag links, say Tag 1, to see that the tag.php template file has overridden archive.php and that the tag name and description for the chosen tag are displayed. From the home page click on Tag 2 and Tag 3 to see that they also cause tag.php to execute.

Tag.php overrides archive.php

The tag.php template file overrides archive.php for tag archive requests.

The next level in the hierarchy uses the tag’s ID to provide a mechanism for tag-specific formatting. In order to determine the ID for a tag, return to the Administration Screen, select Posts→Tags from the menu, and then Edit for one of the tags, say Tag 1. In the URL for the Edit Tag page, look for the tag_ID property in the query string. The value for that property is the tag’s ID. In the screenshot below, the ID for Tag 1 on my system is 17. The corresponding value on your system will likely be different. Note the actual ID for the next step.

Edit Tag page shows tag ID

On the Edit Tag page you can find the ID for the tag by inspecting the URL query string to find the tag_ID property value.

Copy the tag.php template file to tag-[id].php where [id] is the ID for Tag 1 on your system. In my case, I copied tag.php to tag-17.php. Change the trace message to In tag-[id].php, replacing [id] with your ID value. The code for my tag-17.php is shown below.

<?php
get_header(); 
echo "<p>In tag-17.php</p>\n";
echo "<p>Browsing tag ", single_tag_title(), "</p>\n";
echo tag_description(), "\n";
	
if (have_posts()) {   // if there are posts
	echo "<table>\n";
	echo "<tr><th>ID</th><th>Title</th><th>Format</th></tr>\n";
	while (have_posts()) {   // start the loop
		the_post();
		echo "<tr>";
		echo "<td>", get_the_ID(), "</td>";
		echo "<td><a href='", get_permalink(), "'>",
				get_the_title(), "</a></td>";
		$postFormat = get_post_format();
		echo "<td>", $postFormat ? $postFormat : 'standard', "</td>";
		echo "</tr>\n";
		}
	echo "</table>\n";
	next_posts_link("Older posts");
	echo "<br/>";
	previous_posts_link("Newer posts");
} else {
	echo "<p>no posts to display</p>\n";
}
    
get_sidebar();
get_footer();
?>

Save the file and click Visit Site. Select  the Tag 1 link for one of the posts, say Book 1. You should see a screen similar to that below. Check Tag 2 and Tag 3 archives to verify that they still use the tag.php template.

Tag-[id].php overrides tag.php

The tag-[id].php template overrides the tag.php template for a specific tag archive.

Finally, as with categories, you can select a tag-specific template file using the slug. For Tag 1, this template file is tag-tag-1.php. Copy tag.php to tag-tag-1.php and change the trace message to In tag-tag-1.php.

<?php
get_header(); 
echo "<p>In tag-tag-1.php</p>\n";
echo "<p>Browsing tag ", single_tag_title(), "</p>\n";
echo tag_description(), "\n";
	
if (have_posts()) {   // if there are posts
	echo "<table>\n";
	echo "<tr><th>ID</th><th>Title</th><th>Format</th></tr>\n";
	while (have_posts()) {   // start the loop
		the_post();
		echo "<tr>";
		echo "<td>", get_the_ID(), "</td>";
		echo "<td><a href='", get_permalink(), "'>",
				get_the_title(), "</a></td>";
		$postFormat = get_post_format();
		echo "<td>", $postFormat ? $postFormat : 'standard', "</td>";
		echo "</tr>\n";
		}
	echo "</table>\n";
	next_posts_link("Older posts");
	echo "<br/>";
	previous_posts_link("Newer posts");
} else {
	echo "<p>no posts to display</p>\n";
}
    
get_sidebar();
get_footer();
?>

Refresh the browser to verify that tag-tag-1.php overrides tag-[id].php.

Tag-[slug].php overrides tag-[id].php

The tag-[slug].php template overrides tag-[id].php when both are present.

As I showed in this section, the tag archive hierarchy is identical to the category archive hierarchy that we explored in the last article. Beginning from archive.php (which it shares with the other archive types), you can use tag.php for special tag archive formatting. You then have two options for tag-specific formatting: tag-[id].php and tag-[slug].php. You wouldn’t likely use both for the same tag as I did in this demonstration, but you can choose either identification method for a particular tag.

Extending our on-going template hierarchy diagram with the tag hierarchy, we can see that category and tag archives are very similar.

Template Hierarchy with Tag Archives

The tag archive page hierarchy is rooted on the archive.php template file, which applies to all archives. For tag archives, the tag.php template can be used. Additionally, you can format individual tags using the tag-[id].php and tag-[slug].php templates. Click for full-size image.

Display Tag Cloud in the Footer

In the prior article I showed how easy it is to display an independent list of category links in the sidebar. In this article we’ll use a similar function to display a tag cloud. Just for a change, we’ll display it in the footer rather than in the sidebar. Open the footer.php file and add the wp_tag_cloud() call as shown below. wp_tag_cloud() has many options to control the format of its output. Here, we’ll just use all of the defaults for illustration.

<?php
echo "<p>In footer.php</p>\n";
wp_tag_cloud();
echo "</body>\n";
echo "</html>\n";
?>

Navigate to the home page of the site. You should see the simple tag cloud as shown below. The tags are shown at different sizes based on their usage in your posts. Clicking on the links that represent each tag will navigate to the archive page for the tag selected.

Tag cloud display

The tag cloud shows the relative usage of tags with different sizes: the more popular the tag, the larger its display.

In this article we explored the tag archive hierarchy. Along with the category archive hierarchy, archive.php can be used to override index.php. We’ll see in later articles that archive.php is used for several other archive types as well. If you need to format tag archives differently than other archives, you can override archive.php with tag.php. Additionally, tag-specific formatting can be accomplished with tag-[id].php and tag-[slug].php.

In the next article of the series, we’ll examine the author archive hierarchy.

References

Speak Your Mind

*