This is How to Create a Custom URL Structure for WordPress Custom Post Types
As a WordPress developer, you know that custom post types are a powerful tool for organizing and displaying content on your website. But have you ever wanted to create a more intuitive and user-friendly URL structure for your custom post types?
In this article, we'll walk you through the steps to create a custom URL structure for your WordPress custom post types. We'll also cover how to set up a hierarchical structure for your "Lessons" post type and a non-hierarchical structure for your "Classes" post type.
By the end of this guide, you'll have a custom URL structure that not only looks great, but also helps users and search engines navigate your content more effectively.
Why Custom URL Structures Matter
The default URL structure for WordPress custom post types can be somewhat clunky and unintuitive. For example, the default URL for a custom post type called "Lessons" might look something like this:
https://example.com/lesson/lesson-title/
While this URL structure gets the job done, it doesn't do much to help users or search engines understand the content of the page. A more intuitive URL structure might look something like this:
https://example.com/courses/beginner-course/lessons/introduction-to-wordpress/
This type of URL structure provides much more context about the content of the page, making it easier for users to understand and navigate your site. It also helps search engines better understand the hierarchy and relationships between your content, which can improve your search engine optimization (SEO) efforts.
Step 1: Set Up Your Custom Post Types
Before we can start creating a custom URL structure, we need to set up our custom post types. In this example, we'll be creating two custom post types: "Lessons" and "Classes".
Here's how to set up your custom post types using the register_post_type()
function:
// Register the "Lessons" post type
function my_custom_post_type_lessons() {
$labels = array(
'name' => _x( 'Lessons', 'Post type general name', 'textdomain' ),
'singular_name' => _x( 'Lesson', 'Post type singular name', 'textdomain' ),
'menu_name' => _x( 'Lessons', 'Admin Menu text', 'textdomain' ),
'name_admin_bar' => _x( 'Lesson', 'Add New on Toolbar', 'textdomain' ),
'add_new' => __( 'Add New', 'textdomain' ),
'add_new_item' => __( 'Add New Lesson', 'textdomain' ),
'new_item' => __( 'New Lesson', 'textdomain' ),
'edit_item' => __( 'Edit Lesson', 'textdomain' ),
'view_item' => __( 'View Lesson', 'textdomain' ),
'all_items' => __( 'All Lessons', 'textdomain' ),
'search_items' => __( 'Search Lessons', 'textdomain' ),
'parent_item_colon' => __( 'Parent Lesson:', 'textdomain' ),
'not_found' => __( 'No lessons found.', 'textdomain' ),
'not_found_in_trash' => __( 'No lessons found in Trash.', 'textdomain' )
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'courses/%course%/lessons' ),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => true,
'menu_position' => null,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
);
register_post_type( 'lessons', $args );
}
add_action( 'init', 'my_custom_post_type_lessons' );
// Register the "Classes" post type
function my_custom_post_type_classes() {
$labels = array(
'name' => _x( 'Classes', 'Post type general name', 'textdomain' ),
'singular_name' => _x( 'Class', 'Post type singular name', 'textdomain' ),
'menu_name' => _x( 'Classes', 'Admin Menu text', 'textdomain' ),
'name_admin_bar' => _x( 'Class', 'Add New on Toolbar', 'textdomain' ),
'add_new' => __( 'Add New', 'textdomain' ),
'add_new_item' => __( 'Add New Class', 'textdomain' ),
'new_item' => __( 'New Class', 'textdomain' ),
'edit_item' => __( 'Edit Class', 'textdomain' ),
'view_item' => __( 'View Class', 'textdomain' ),
'all_items' => __( 'All Classes', 'textdomain' ),
'search_items' => __( 'Search Classes', 'textdomain' ),
'parent_item_colon' => __( 'Parent Class:', 'textdomain' ),
'not_found' => __( 'No classes found.', 'textdomain' ),
'not_found_in_trash' => __( 'No classes found in Trash.', 'textdomain' )
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'classes' ),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
);
register_post_type( 'classes', $args );
}
add_action( 'init', 'my_custom_post_type_classes' );
In this example, we're creating two custom post types: "Lessons" and "Classes". The "Lessons" post type is set to be hierarchical, which means that lessons can have parent-child relationships. The "Classes" post type, on the other hand, is set to be non-hierarchical.
Notice that in the "Lessons" post type, we've set the 'rewrite'
parameter to array( 'slug' => 'courses/%course%/lessons' )
. This tells WordPress to use a custom URL structure for the "Lessons" post type, with the "course" slug being a dynamic placeholder that will be replaced with the slug of the parent "Course" post.
Step 2: Create the Custom URL Structure
Now that we have our custom post types set up, let's create the custom URL structure. We'll do this using the post_type_link
filter in WordPress.
// Custom URL structure for Lessons
function my_custom_lesson_url( $post_link, $post, $leavename, $sample ) {
if ( $post->post_type == 'lessons' ) {
$terms = wp_get_object_terms( $post->ID, 'course' );
if ( $terms ) {
return str_replace( '%course%', $terms[0]->slug, $post_link );
} else {
return str_replace( 'courses/%course%/lessons', 'lessons', $post_link );
}
}
return $post_link;
}
add_filter( 'post_type_link', 'my_custom_lesson_url', 10, 4 );
// Custom URL structure for Classes
function my_custom_class_url( $post_link, $post, $leavename, $sample ) {
if ( $post->post_type == 'classes' ) {
return str_replace( 'classes', 'classes/%class%', $post_link );
}
return $post_link;
}
add_filter( 'post_type_link', 'my_custom_class_url', 10, 4 );
In the code above, we're using the post_type_link
filter to modify the default URL structure for our "Lessons" and "Classes" post types.
For the "Lessons" post type, we're checking if the post has a "course" taxonomy term associated with it. If it does, we're replacing the %course%
placeholder in the URL with the slug of the parent "Course" post. If the post doesn't have a "course" term, we're removing the courses/%course%
part of the URL.
For the "Classes" post type, we're simply replacing the classes
slug with classes/%class%
to create a more intuitive URL structure.
With these custom URL structures in place, your URLs will now look something like this:
- "Lessons" post type:
https://example.com/courses/beginner-course/lessons/introduction-to-wordpress/
- "Classes" post type:
https://example.com/classes/yoga-for-beginners/
Step 3: Implementing the Hierarchical Structure for Lessons
As mentioned earlier, we've set the "Lessons" post type to be hierarchical, which means that lessons can have parent-child relationships. To implement this, we'll need to add a custom meta box to the "Lessons" post type admin screen.
Here's how to create the custom meta box:
// Add custom meta box for Lessons
function my_custom_lesson_metabox() {
add_meta_box(
'lesson_parent',
'Parent Lesson',
'my_custom_lesson_metabox_callback',
'lessons',
'side',
'high'
);
}
add_action( 'add_meta_boxes', 'my_custom_lesson_metabox' );
// Callback function for the custom meta box
function my_custom_lesson_metabox_callback( $post ) {
$parent_id = get_post_meta( $post->ID, 'parent_id', true );
$parent_post = get_post( $parent_id );
$parent_title = $parent_post ? $parent_post->post_title : '';
?>
<label for="parent_id">Parent Lesson:</label>
<select name="parent_id" id="parent_id">
<option value="0">None</option>
<?php
$lessons = get_posts( array(
'post_type' => 'lessons',
'post_parent' => 0,
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC'
) );
foreach ( $lessons as $lesson ) {
$selected = ( $lesson->ID == $parent_id ) ? 'selected' : '';
echo '<option value="' . $lesson->ID . '" ' . $selected . '>' . $lesson->post_title . '</option>';
}
?>
</select>
<p>Current parent: <strong><?php echo $parent_title; ?></strong></p>
<?php
}
// Save the parent_id meta field
function my_custom_lesson_save_metabox( $post_id, $post ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( $post->post_type == 'lessons' ) {
if ( isset( $_POST['parent_id'] ) ) {
update_post_meta( $post_id, 'parent_id', $_POST['parent_id'] );
}
}
}
add_action( 'save_post', 'my_custom_lesson_save_metabox', 10, 2 );
In this code, we're adding a custom meta box to the "Lessons" post type admin screen that allows the user to select a parent lesson. The selected parent lesson is then stored in a custom parent_id
meta field.
When a new lesson is saved, the my_custom_lesson_save_metabox()
function is called, which automatically saves the parent_id
meta field. This means you don't need to manually save the meta box data – WordPress will take care of it for you.
With this custom meta box in place, users can now easily create a hierarchical structure for their lessons, which will be reflected in the custom URL structure we set up earlier.
Conclusion
In this article, we've shown you how to create a custom URL structure for your WordPress custom post types. We've also covered how to set up a hierarchical structure for your "Lessons" post type and a non-hierarchical structure for your "Classes" post type.
By implementing these custom URL structures, you can make your website more user-friendly and improve your search engine optimization efforts. And with the custom meta box for the "Lessons" post type, you can easily create a hierarchical structure for your content, without the need to manually save the meta box data.
If you're looking for a powerful tool to help you analyze and optimize your website's conversion rates, be sure to check out Flowpoint.ai. Flowpoint uses advanced AI algorithms to identify technical, UX/UI, and content-related issues that may be impacting your conversion rates, and provides actionable recommendations to help you fix them
Get a Free AI Website Audit
Automatically identify UX and content issues affecting your conversion rates with Flowpoint's comprehensive AI-driven website audit.