Custom post type and taxonomy hierarchy in WordPress

For this blog post I’m going off the assumption you have a fairly good understanding of WordPress custom post types and taxonomies.

Let’s say you’re building a website for a company that provides a list of services. You could create a custom post type for services using the code below.

$args = array(
      'public' => true,
      'label'  => 'Services'
    register_post_type( 'service', $args );

Next thing you would want to do is create a standard WP page from where you will pull a list of all your custom post types and display them in a nice format for the user to make his/her selection to read up about that specific service by going to the service single page. You’ll likely create a page with the slug ‘/services/’ for this page.

By default the slug for your services custom post posts would be ‘/service/xxx’, which means when you’re on your ‘/services/’ page and you click on a single service your URL would change to ‘/service/xxx’ and for SEO that’s really bad.

A possible solution to this could be to rewrite your custom post type slug. You could do this as follows:

$args = array(
      'public' => true,
      'label'  => 'Services'
      'rewrite' => array('slug' => 'services'),
    register_post_type( 'service', $args );

Now both slugs are the same, but you face yet another problem. If you click back to the base page on your page breadcrumbs for example, you won’t ever see your standard WP page you created, you’ll see your custom post archive page. The ‘/services/’ base page is an archive page, not a WP page.

That you fix by doing this:

 $args = array(
      'public' => true,
      'label'  => 'Services'
      'rewrite' => array('slug' => 'services'),
      'has_archive' => FALSE,
    register_post_type( 'service', $args );

You disable the archive page.


Now. Let’s say you have different types of services and you want to categorize those services. You would create a custom taxonomy like this:

			'label' => __( 'Services Categories' )

The problem once again is in that your slug structure would be ‘/service-categories/’ for your custom taxonomy. You solve this once again by rewriting the URL:

			'label' => __( 'Services Categories' ),
			'rewrite' => array( 'slug' => 'services' ),
			'hierarchical' => true,

Let me expand on my services example. Let’s say our company does plumbing and gas installations. We could create a taxonomy for ‘plumbing’. If I create a services custom post for ‘unblocking drains’ and tag it as ‘plumbing’. I would expect my URL for that page to be ‘/services/plumbing/unblocking-drains/’, but WordPress does not give you that by default. Your URL would just be ‘/services/unblocking-drains/’. To get your taxonomy in your URL structure you have to rewrite it once again. You’ll do that with a filter and a function:

add_filter('post_type_link', 'my_awesome_post_link', 1, 2);

function my_awesome_post_link($permalink, $id){
//I'm going to let you write this for yourself
//My code is very specific to the project I did this for and won't work for you.
//Essentially what you need is to create the URL structure to include your taxonomies in the URL

//for example:
//return home_url().'/services/plumbing/unblocking-drains/';

This function will then create your link and everywhere you call


it will return the full path including your taxonomy structure.

But this isn’t all, now that you’ve created the URL structure you need to tell WP how to interpret it. You need to create a rewrite rule for it.

add_filter('generate_rewrite_rules', 'my_rewrite_rules');
function my_rewrite_rules($wp_rewrite) {
//I'm going to let you write this for yourself
//My code is very specific to the project I did this for and won't work for you.
//You need to create a rewrite rule for each of your custom post types including the full URL structure

//for example:

$custom_rules = array();

//for each of your custom posts:
$custom_rules['^services/plumbing/unblocking-drains$'] = 'index.php?service=unblocking-drains';

$wp_rewrite->rules = $custom_rules + $wp_rewrite->rules;
return $wp_rewrite->rules;

What this does is when you give WordPress this URL ‘/services/plumbing/unblocking-drains/’ it converts it to ‘index.php?services=unblocking-drains’ which WordPress is able to understand and return the correct content.

This is what I did in a nutshell.

I (re-)learned a couple of valuable lessons:

  • Unit testing
    • Whenever you start something that seems like it’s going to be small, just a few functions, it always ends up being more. A few hours later you sit with a class with a bunch of methods. You’re going to wish you started writing unit tests in the beginning.
  • Test data
    • You’re going to need real-world test data. Lorem ipsum doesn’t mean anything. Sometimes having real-world test data shows you certain edge-cases you would not have thought of when using generic data.

WordPress automatic meta description generation

Sometimes you’re just too lazy to create proper meta descriptions for a post or page and having a good(ish) meta description to display in your SERP is rather important for SEO.

I recently had to write a plugin to generate meta descriptions for all post types. The theory is, when you request the page, I have an action that runs on the

add_action('wp_head', 'xxx');

action. It then checks if you have a meta description for that page (assuming you have Yoast SEO installed), and if not, generates one based on a few parameters and appends it to your


section in your markup.

I’ll give you a base to start off with, this is by no means a complete and final version that you should use on your site. There is a lot more validation that needs to be done, but it’s a start. For now, you can put this in your functions.php file.

The bit of code below will check if you have a Yoast SEO meta description, if not, it will take the first sentence it finds in your content and use that as a meta description. Meta descriptions are 160 characters in length, anything over that will be truncated by Google.

add_action('wp_head', 'my_generate_custom_meta');

function my_generate_custom_meta() {
	global $wp_query;
	$yoast_meta_key = '_yoast_wpseo_metadesc';
	$post_id = get_the_ID();
	$yoast_meta_desc = get_post_meta($post_id, $yoast_meta_key, true);

	if (empty($yoast_meta_desc)) {
		$meta_desc = '';
		$title = strip_tags($wp_query->queried_object->post_title);
		$content = strip_tags($wp_query->queried_object->post_content);

		if (!empty($title) && !empty($content)) {
			$first_sentence = substr($content, 0, strpos($content, '.'));
			$meta_desc = $title . '. ' . $first_sentence;
			update_post_meta($post_id, $yoast_meta_key, $meta_desc);
			echo sprintf('', $meta_desc);

WordPress Custom Post Type names restricted to max 20 characters

Ever spotted this error:

: register_post_type was called
. Post type names must be between 1 and 20 characters in length. Please see
Debugging in WordPress
for more information. (This message was added in version 4.2.) in
on line

Thrown here:


	if ( empty( $post_type ) || strlen( $post_type ) > 20 ) {
		_doing_it_wrong( __FUNCTION__, __( 'Post type names must be between 1 and 20 characters in length.' ), '4.2' );
		return new WP_Error( 'post_type_length_invalid', __( 'Post type names must be between 1 and 20 characters in length.' ) );

So It would appear that due to legacy issues, your custom post type cannot exceed 20 characters.

What are these “legacy issues” I speak of? The post_type field in the wp_posts table in the database where the type of post (your custom post type’s name) is saved, is a VARCHAR(20)!

Screen Shot 07-06-16 at 08.58 AM

I have no words.

WordPress plugins you need on your site

I try and do a lot of regular optimizing on my other blog. Over the years I’ve found a few WordPress plugins I cannot go without. I thought I’d share them with you.



CloudFlare and Incapsula are the two best CDN‘s around, and both have a free version. I use both, on different sites, for various reasons. Many articles have been written on the differences between the two, so I’m not going to spend much time on the subject. I’ll leave it up to you to decide which is best for you.


If there is one plugin you cannot go without, it would be this one. You can speed up your blog/website so much by using W3TC. The options for caching content are endless and once again, many articles about this plugin have already been written such as this one from Tuts+

SEO Friendly Images

If you’re anything like me, super lazy and you don’t add ALT text to your images, then you’re going to need this plugin. We all know how important it is for SEO to have ALT text on all your images. This plugin conveniently adds it for you, using the name of the post, or whatever other option you have enabled in the settings area.

Widget Logic

This is another one of my favourites. Sometimes you only need a certain plugin to appear on a certain post/category. With this plugin you can do that. You do need a bit of brains to figure out the ‘logic’ part, but other than that, this plugin works great.

WordPress SEO

Your blog cannot go without this plugin. Besides all the normal SEO stuff like providing you a place to add meta tags for Google Webmaster Tools or adding meta tags for a category/page, this plugin also generates your XML sitemap.


Another one for the lazy folk. If you upload images that aren’t web optimized, this plugin will do that for you. In the free version, you can only optimize images that are less than 1Mb, but if you upgrade to PRO, you can optimize images up to 5Mb.

Total Security

Total Security saved my butt a few times already. When you’re done with a clean install of WordPress, there are a few things you can do to harden WordPress against attackers. Total Security scans your WordPress installation and gives you a nice report of all the things you can do to improve security. In addition to this, they also compare your WordPress core files against a known safe version and shows you any differences, meaning modifications to your core code that could have been caused by exploits in some of your installed plugins.

Get all posts for custom taxonomy in WordPress

I’ve been doing a lot of behind-the-scenes stuff over at UltimateVelocity and I want to share one thing that caught me out quite a bit recently.

When getting all the posts associated with a custom taxonomy, you should use this codex example.

$args = array(
	 'posts_per_page' => 8,
	 'orderby' => 'rand',
	 'post_type' => 'albums',
	 'genre' => 'jazz',
	 'post_status' => 'publish'
$show_albums = get_posts( $args );

If you want to return all the posts, simply replace

'posts_per_page' => 8


'posts_per_page' => -1

Also, by default, the function only returns 5 posts, if if you omit to specify ‘posts_per_page’, you will see just 5 posts returned and you will be puzzled as to why.

The WordPress SEO hack

How does it work?Screen Shot 10-23-14 at 06.36 AM

What happens is that a malicious script is uploaded to your web server and is included somewhere in a WordPress header file. When someone clicks on search results from a search engine, and you are directed to your website, the malicious script picks up the referrer was a search engine and 301 redirects the user to somewhere else of their choosing. The 301 redirect tells Google that the URL has changed and Google automatically changes the old URL to the new one on their index.

How can I test if I’m compromised?

You can run this query in Google [keyword]. The keyword is optional, if your site has a lot of indexed pages, it might be difficult to scroll through all the results looking for suspicious URL’s. You can just run through the usual list of “viagra”, “penis enlargement”, etc. to test if Google indexed

How do I fix it?

Look for any suspicious files in your web server public root. I was lucky enough to immediately spot a file called “wp-cofnig.php”.

The contents of this file, looked like this:

Screen Shot 10-23-14 at 06.43 AM

What I can suggest is that you run this command on your web server:

$ grep -irl “eval(” /path/to/your/wordpress/install/

This will search through all the WordPress files for “eval(“. Chances are that you could get a couple of legitimate results, but you’ll be able to spot the ones that should not be there if you have some experience.

Once you removed the malicious script, and a user clicks on the fake URL in the search results, they should no longer be redirected away from your site, but will rather now land your site and your 404 error page should be displayed.

How do I remove those fake links?

There are a few ways you can do this: