Hello, beautiful readers! In this post we are going to solve a very specific need for custom post types in WordPress. I’ve actually written about it two years ago, which is to select post parent from another post type. But today you’ll learn more deeply on the same topic with a live example.
Say there’s a project for building online courses website, we plan to have two custom post types in WordPress, which are “Courses” and “Lessons”. Registering two CPTs won’t be a big deal for us but the not-so-easy part is we’d like to set the URL structure of the lessons in such format:
Taking my AngularJS series for example, it would look like:
So the workflow to manage our online course would be like, we always add a new course first, input some course information, prerequisites, curriculum etc. After that we’ll create as many lessons as needed, and for each lesson, we have to set the “parent post” for it, which is the course it’s attached to.
If you find this use case compelling, keep reading and here’s what I’m going to show you (preview the final demo if you’d like):
- Registering two custom post types: Courses and Lessons.
- Updating the “Parent” meta box so we can choose a “course” as the parent post for a “lesson”.
- Setting the exactly URL structure we want.
- Updating the permalink of the CPT to reflect the new URL structure.
Ready? Let’s go!
1. Registering custom post types
Registering custom post types should be relatively easy for you. Just visiting the WordPress Codex to see what arguments to set or you may even use some cool GUI tools to write the snippets much quicker.
An important note here is the Courses must be hierarchical but NOT the Lessons. This is a requirement for this tutorial to work. Check the gist above (line 13 and 26), be sure you set them right. We do so for two reasons:
- Courses need to be hierarchical because we can only use the
wp_dropdown_pages()(more on this soon) to get the courses if it’s hierarchical.
- Lessons can’t be hierarchical because if so WordPress will use a different way to process its permalink and that’ll break the URL structure hacks we’re going to use.
2. Updating the “Parent” meta box
This step is kind of cool that, by default a post type needs to be hierarchical and also supports “page-attributes“, to get the “Parent” field (meta box) to set the parent post, which is a select box to display a bunch of posts to choose from.
Since we didn’t set the Lessons to be hierarchical, we can’t have WordPress to create the field for us. But we can create our own Parent post field by registering a new meta box for it (line 2-5).
And even more, by naming the select to “
parent_id” (line 9,
wp_dropdown_pages() can be not only used to get pages, but also any other hierarchical post types), a default filed name that all hierarchical post type has in common, we don’t have to write an extra function to save the value to post meta, WordPress will process that automatically.
3. Setting the exactly URL structure
If you have ever set the permalinks in your WordPress Settings, you must be familiar with built-in URL structure tags like “
%day%” or “
%postname%“, which must start and end with a
% symbol. Now in our case we’re going to create a new structure tag “
%lesson%“, so we can use it to build the custom URL structure for “Lessons” post type.
The next is to use
add_permastruct() to create a new permalink structure for the “Lessons” post type. Here we use a structure tag “%course%” that doesn’t really exist (we didn’t register it with
add_rewrite_tag). Because we don’t really want to use it globally in WordPress, it’s just like a shortcode so we can replace it with the real course name (the lesson’s parent) in the next step.
And then we need to create a new rewrite rule that tells WordPress, when someone visits URLs in this very format (
/lesson/%course%/%lesson%/), WordPress should redirect them to another URL (
/index.php?lesson=%lesson%), which is the true URL with query strings (a.k.a. the ugly link) so WordPress can actually do the queries and fetch posts for us.
The fun part is we don’t really need every tag in the URL structure to be in part of the query strings. In our case, we need only
%lesson% but not
%course%, in fact, we can even say
%course% is totally useless for WordPress to fetch this singular post, we use it to decorate the URL so our visitors will know “this lesson belongs to that course“. With only
%lesson%, the query can work fine.
4. Updating the permalink for our custom post type
In the final step, we have to update the permalink for the Lessons post type. Here comes a powerful filter called
post_type_link, we can use it to modify the permalink for any post type in WordPress. Now the most important task left is to interpret
%course% tag into the slug of the parent course we’ve set. You can see the gist above and that’s what line 7-10 is for.
Bonus tip: using completely delete plugin to better manage the courses and lessons
My plugin Completely Delete is made to better manage hierarchical posts. With it you can delete a parent post and also all its child posts (include attachments) at the same time. So it would be very helpful in our case, when trashing or deleting a certain course, all lessons can be gone with it.
Now things should work as I’ve promised you at the beginning of this tutorial. Check the demo screencast and see it actually works! Also, you can grab the full functions.php you need for this project.
I hope you enjoy this tutorial and learn something new with me. The Lunar New Year in Taiwan is coming and I wish everyone read so far will have an awesome Monkey Year! Leave comments or shoot me an email (yoren [at] 1fix.io), I’ll get back to you ASAP even when I’m on a 9-day vacation.