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:
http://my-online-course.com/lesson/[course-name]/[lesson-name]/
Taking my AngularJS series for example, it would look like:
http://my-online-course.com/lesson/angularjs-wp-api/lesson-1-using-angularjs-in-wp-theme/
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
To build a custom URL structure for our CPT, there are three functions to do the job: add_rewrite_tag
, add_permastruct
andย add_rewrite_rule
.
If you have ever set the permalinks in your WordPress Settings, you must be familiar with built-in URL structure tagsย like “%year%
“, “%monthnum%
“, “%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.
Leave a Reply