There are a number of introductions to WordPress Custom Post Types (CPT) to be found on the web that show how to create them, both with admin plugins and manually with code. However, you’ll notice whenever they show how to create a CPT in code that there is often a lot of repeated boilerplate code for setting up labels and often used defaults. The Extended CPTs library by John Blackbourn (aka johnbillion) aims to reduce the need for writing the same code over and over again by wrapping CPT creation with functions that provide often used defaults, generated labels, and other conveniences.
In this article I review the Extended CPTs library from the viewpoint of someone downloading for the very first time and trying to use it on a very simple WordPress plugin project.
Near the end of this article I’ll try and re-create the plugin using just the standard WordPress API to get a feel for how much the Extended CPTs library helps or hinders custom post type development.
When you look at the source code for Extended CPTs you’ll notice that all the functionality is contained within a single file called
This is both good and bad.
It’s good because you have just one file to copy into your plugin’s source folder and to
require_once. As this is a library rather than a plugin, you need to include the source for Extended CPTs in your own plugin, so a single file makes this much more manageable.
However, it’s bad for the very same reason. Because the
extended-cpts.php file has header comments that are recognised by WordPress as plugin descriptors, you need to be careful where you put the file in your source and distributed plugin. If
extended-cpts.php is placed in the top level of your source alongside your own plugin’s entry file you’ll notice that “Extended CPTs” will be listed as a plugin in your site’s admin dashboard. There is an easy fix for this, just make sure
extended-cpts.php is in a subfolder in your source, whether that’s a catch-all
libs folder or an
extended-cpts folder of its own.
You could include the entire source of Extended CPTs as a subfolder of your source (maybe as a git submodule), but if you did so you would want to make sure you have a build process for releasing your plugin that copies just the required file into the final product. If your plugin is non-trivial, then chances are you already have a build process that can be modified to accommodate this.
I was happy to see that my git checkout had a
phpunit.xml file and
tests folder, I like unit tests. In the
tests folder was a
README.md that succinctly described how to run the unit tests.
composer install was fine, but using the included version of
install-wp-tests.sh didn’t work so well as my Mac doesn’t have
wget. Luckily I have my own version of
install-wp-tests.sh that substitutes
wget. After that
./vendor/bin/phpunit ran fine with no errors.
Let’s demonstrate what Extended CPTs does with a simple example, a Pixie Article custom post type.
With what amounts to 2 lines of code we have a fully functioning plugin that allows us to create Pixie Articles when paired up with the Extended CPTs.
When this plugin is added to our WordPress install those comments at the top of the
pixie-article.php plugin entry file tell WordPress everything it needs to know to show the plugin in the Admin Dashboard.
So far so good, but Extended CPTs hasn’t come into play yet, so let’s activate the Pixie Article plugin. Now we have an admin menu item and fully functional administration pages with all the singular and plural strings properly set. A good chunk of this functionality comes as standard when registering a custom post type, but there are subtle improvements that we’ll come to later.
This is pretty cool, with very little effort we have a usable custom post type purely based on the unique key of “pixie-article”.
There are a lot of admin items created when you use Extended CPTs. Apart from the main admin screen for listing, adding and editing your custom post type that we’ve already seen, you also get a link on the top nav’s “+ New” menu and even a dashboard “At a Glance” entry.
All these conveniences work as expected, and if you don’t want the extras such as the “At a Glance” you can turn them off.
This is a massive win for getting a custom post type created and usable very quickly.
What happens when we switch to another language such as German?
Hmm, there are a number of issues when switching to another language. The “Add New” link and “Search Pixie Articles” button on the “Pixie Articles” list are in need of translation, as is arguably “Pixie Articles” itself. The edit screen shows similar problems…
“Edit Pixie Article”, “Add New” and “View Pixie Article” are all in need of translation.
When you look at the translatable strings picked up by Poedit you can see that there is little available to translate, as to be expected in a library that automates the generation of strings.
This is relatively easy to fix, you just have to supply the labels that WordPress’s
register_post_type() can take as translatable strings.
Now Poedit shows something much more usable.
And when we generate our
pixie-article-de_DE.mo file and save it into our
languages folder in the plugin, magic happens…
Apologies to speakers of the German language for any inaccurate translations, these are just examples taken from Poedit’s suggestions.
It’s a shame that Extended CPTs can’t automagically translate into other languages and needs translatable labels supplied, but as John said in his comment on a PR that attempted to add translatable strings to the library, you simply can’t automate generation of translatable strings due to the varying grammar rules across languages.
That push pin icon in the admin menu for Pixie Articles is the same as the Posts icon, to make our menu item easier to find (especially when the menu is collapsed down to icons only) we need to change that icon.
Because Extended CPTs passes through any of the arguments normally used with
register_post_type() we can use its
menu_icon parameter and either pass in a base64-encoded SVG string or a dashicon class.
Ahh, much better!
You can see the current full file in this gist.
So far our custom post type is effectively a very simple Post, the editor allows input of a Title, Content, Slug and Featured Image. The chances are you’re going to want to add some more type specific data to your custom post type.
To make my life a little easier I’m going to set up a new taxonomy using Extended CPTs sister library, Extended Taxonomies.
With the library included in my plugin and just those two lines of code, I have a fully functioning “Pixie Article Audiences” taxonomy admin screen.
After entering a few terms for my new Pixie Article specific taxonomy I can edit one of my records to add an audience.
The Quick Edit functionality has the expected Pixie Article Audiences inputs too.
Adding new custom taxonomies with Extended Taxonomies is very easy, but like Extended CPTs it has a lot of hidden depth that is probably worth an article of its very own.
At the moment our admin screen is looking a little bare and only showing the title and date of our Pixie Articles. Let’s add an Audiences column too.
This is accomplished by adding entries in an “admin_cols” section of the optional arguments passed to
In the above snippet of code I’ve added a column to show our Pixie Article Audiences, as well as the dates that the article was created and modified. When you start adding admin columns you lose the Date column that was already there alongside the Title column by default, hence I added a couple of dates back into the columns displayed.
As you can see from the above screenshot the new Audiences column nicely formats multiple values, and gives them links to filter the list of entries to just that Audience. You can do plenty other things though, such as change the link to go to the edit, view or list of the taxonomy.
I also added fields from the posts table to the columns list with the
post_field key, you can add other data with keys such as
meta_key (for metadata), and
function (for a callback). It’s worth reviewing the docs for admin_cols to see what else is possible, including restricting columns by a user’s capability.
Sometimes you want to be able to filter the records in the admin screen without using the search box or clicking on a displayed value (it might not be visible in the list), so a dropdown of possible values would be handy. Say no more…
When the above code is added to our options array it gives us a new dropdown select box above the list of Pixie Articles.
When we select an Audience and click the “Filter” button we get a list of just those articles that match.
You can do a lot more with admin filters, such as search boxes and checkboxes for specific values, and restrict showing the controls by capability. Check out the documentation for more information on admin filters.
Our current plugin is doing quite a bit now, and still isn’t a lot of code.
Displaying Our Custom Post Types
The last thing to do on our super simple custom post type based plugin is show our Pixie Article on our website…
Oops, looks like we need to set this up ourselves!
It is a shame that Extended CPTs doesn’t have some magic included that automatically builds and installs a template or two for the custom post type, there are some relatively nice ways of doing this. Even a shortcode or two would come in handy.
I quite like the flexibility of short codes, so I’m going to quickly whip up one to display a list of Pixie Article titles and add it to our completed
With this additional
pixie_article_list() function and usage of
add_shortcode() we’re all set to add it to a new page.
When we visit the page we get the following list of Pixie Articles.
Admittedly not very impressive, but it does prove that our Pixie Articles can be accessed from the front end easily enough. To properly flesh out the display of our custom post types would likely require a whole article of its own.
Comparison With Standard WordPress CPT API
Does the Extended CPTs library really save you time when compared to writing a custom post type with the standard WordPress API? To find out I tried to replicate the plugin without using either Extended CPTs or Extended Taxonomies.
The following Gist shows how far I got before it started to get a bit daunting to keep on going.
This version has the same basic setup for the plugin, but we had to specify a number of options that come for free with Extended CPTs such as being public, hierarchical, the menu position, page based capability and each of the elements to be shown when editing our Pixie Articles (to get the featured image to show).
While I was able to add an admin column for the Pixie Article Audience taxonomy to our admin list of Pixie Articles, it’s not sortable and its title is a rather long “Pixie Article Audience” compared to our previously succinct “Audience”. To otherwise change the admin columns would require a whole lot more HTML and hook usage than I was prepared to use for this little article.
The same goes for custom admin filters, Extended CPTs seriously spoils us on this front, to get the same kind of functionality using the standard WordPress API would require too much work in comparison.
You’ll notice in the above code that I also had to add extra code to deal with flushing the rewrite rules on activation and deactivation. Extended CPTs deals with this itself and also circumvents the need to use the later
init action rather than earlier
plugins_loaded. Not being able to use
plugins_loaded isn’t a big deal, but it is nice to be able to register a custom post type earlier in the process so that other functionality hooked in via
init can use it without the need to change hook priorities. I expect there are ways to get back to using
plugins_loaded when registering a custom post type with rewrite rules, but for this example it’s not important so a simpler solution can be used.
Another item that comes for free with Extended CPTs but missing from my standard WordPress example is the “At a Glance” entry. Again, that requires a bunch of HTML and hook usage that I’m not going to get into for this article.
Extended CPTs works as advertised, it really does allow developers to “quickly build post types without having to write the same code again and again”.
While I was working with Extended CPTs I had zero errors, nothing in my logs that could be attributed to the library, and everything I attempted worked as advertised. You can’t ask for more than that.
Compared to working with the standard WordPress API where I found myself getting the white screen of death a number of times until I fixed the rewrite rules usage, and the example is still lacking a number of nice features enabled by default by the Extended CPTs library.
When doing any work with custom post types in the future, the chances are I’ll be using the Extended CPTs and Extended Taxonomies libraries.