Shiba

Adventures in WordPress

  • Home
  • Dog
  • Art
  • Contact
  • WordPress Articles
    • WP Plugins
    • WP Programming
    • WP Admin Panels
    • WP Theme Design
    • WP How-To
    • WP Theme Images
You are here: Home / WordPress Admin / Custom Post Type / Custom Post Type Permalinks – Part 2

Custom Post Type Permalinks – Part 2

by ShibaShake 135 Comments

In Custom Post Type Permalinks – Part 1 we considered how to set different permalink options from the register_post_type function call. Here, we focus on how to create our own permalink structures.

With the register_post_type function, we can easily set our custom post type permalink structure to look like this (assuming custom post type = gallery).

http://shibashake.com/wordpress-theme/gallery/test-1

However, we may want to fully customize our custom object permalink structure, similar to how we can fully customize our post permalink structure. Suppose we want to set our permalink structures as follows –

Post permalink structure

/articles/%postname%

Gallery custom post type permalink structure

/galleries/%year%/%monthnum%/%gallery%

1. Turn Off Default Rewrite Rules

First, we set the rewrite argument to false in our register_post_type function call.

	$args = array(
		'publicly_queryable' => true,
		'query_var' => true,
		'rewrite' => false,
                ...
	); 
	register_post_type('gallery',$args);

After we set the argument to false, our gallery custom post type will no longer use pretty permalinks. Instead, our links will look like this –

http://shibashake.com/wordpress-theme?gallery=test-1

Note – It is also necessary to set query_var to true to enable proper custom post type queries.

2. Add New Custom Post Type Rewrite Rules

To get back our pretty permalinks, we need to add our own %gallery% rewrite tag, and our own gallery perma-structure.

// add to our plugin init function
global $wp_rewrite;
$gallery_structure = '/galleries/%year%/%monthnum%/%gallery%';
$wp_rewrite->add_rewrite_tag("%gallery%", '([^/]+)', "gallery=");
$wp_rewrite->add_permastruct('gallery', $gallery_structure, false);

The add_rewrite_tag function accepts 3 arguments.

  1. tag name – Our custom post type tag name. E.g. %gallery%.
  2. regex – A regular expression that defines how to match our custom post type name.
  3. query -The query variable name to use for our custom post type plus an = at the end. The result of our regular expression above gets appended to the end of our query.

For example, suppose our pretty permalink is –

http://shibashake.com/wordpress-theme/galleries/2010/06/test-1

The result of the regular expression match from %gallery% is test-1. This value gets passed on as a public query to our main blog –

http://shibashake.com/wordpress-theme?gallery=test-1

Later, the query link gets translated back into our original pretty permalink so that our end-users are shielded from this whole process.

The add_permastruct function takes in 4 arguments.

  1. name – Name of our custom post type. E.g. gallery.
  2. struct – Our custom post type permalink structure. E.g.
    /galleries/%year%/%monthnum%/%gallery%
    

  3. with_front – Whether to prepend our blog permalink structure in front of our custom post type permalinks. If we set with_front to true here, our gallery permalinks would look like this –
  4. http://shibashake.com/wordpress-theme/articles/galleries/2010/06/test-1
    

    This is not what we want, so we set it to false.

  5. ep_mask – Sets the ep_mask for our custom post type.

Adding the add_permastruct function changes our gallery object permalinks from

http://shibashake.com/wordpress-theme?gallery=test-1

to

http://shibashake.com/wordpress-theme/galleries/%year%/%monthnum%/test-1

which results in a 404 or file not found page error. This is because the permalink tags %year% and %monthnum% were not properly translated.

3. Translate Custom Post Type Permalink Tags

Finally we need to translate the additional tags in our custom post type permalink. To do this, we hook into the post_type_link filter. The code used in our tag translation function was adapted from the regular WordPress post get_permalink function.

// Add filter to plugin init function
add_filter('post_type_link', 'gallery_permalink', 10, 3);	
// Adapted from get_permalink function in wp-includes/link-template.php
function gallery_permalink($permalink, $post_id, $leavename) {
	$post = get_post($post_id);
	$rewritecode = array(
		'%year%',
		'%monthnum%',
		'%day%',
		'%hour%',
		'%minute%',
		'%second%',
		$leavename? '' : '%postname%',
		'%post_id%',
		'%category%',
		'%author%',
		$leavename? '' : '%pagename%',
	);

	if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {
		$unixtime = strtotime($post->post_date);
	
		$category = '';
		if ( strpos($permalink, '%category%') !== false ) {
			$cats = get_the_category($post->ID);
			if ( $cats ) {
				usort($cats, '_usort_terms_by_ID'); // order by ID
				$category = $cats[0]->slug;
				if ( $parent = $cats[0]->parent )
					$category = get_category_parents($parent, false, '/', true) . $category;
			}
			// show default category in permalinks, without
			// having to assign it explicitly
			if ( empty($category) ) {
				$default_category = get_category( get_option( 'default_category' ) );
				$category = is_wp_error( $default_category ) ? '' : $default_category->slug;
			}
		}
	
		$author = '';
		if ( strpos($permalink, '%author%') !== false ) {
			$authordata = get_userdata($post->post_author);
			$author = $authordata->user_nicename;
		}
	
		$date = explode(" ",date('Y m d H i s', $unixtime));
		$rewritereplace =
		array(
			$date[0],
			$date[1],
			$date[2],
			$date[3],
			$date[4],
			$date[5],
			$post->post_name,
			$post->ID,
			$category,
			$author,
			$post->post_name,
		);
		$permalink = str_replace($rewritecode, $rewritereplace, $permalink);
	} else { // if they're not using the fancy permalink option
	}
	return $permalink;
}

We Are Done

Once we translate the additional permalink tags, our gallery permalinks will look like this –

http://shibashake.com/wordpress-theme/galleries/2010/06/test-1

And just like that – we are done!

Permalink Conflicts

A common issue that arises when you create your own permalinks are permalink conflicts.

Permalink conflicts occur when two permalinks share the same regular expression structure.

Note – it is regular expression structure and NOT tag structure.

You can use tags with different and unique sounding names but it will not remove your conflict issue as long as your regular expression structure remains the same.

When permalink conflicts happen, you will usually get a 404 Page Not Found error. This happens because when the WordPress system goes to look for the proper permalink rule to fire, there are multiple ones that match. As a result, the system will only fire the first rule that it sees. Those objects that are tied to all subsequent but duplicate patterns will necessarily return a Page Not Found error because the system is using the wrong permalink rule.

The easiest way to avoid permalink conflict issues is to insert a unique slug into your structure. For example, instead of doing –

/%author%/%gallery%/

Do –

/gallery/%author%/%gallery%/

Adding the unique slug/keyword gallery into your permalink structure ensures that there are no regular expression pattern conflicts.


Is it possible to have multiple objects share the same permalink rule?

Yes, but this can get very messy. One way to do this is to only create a single permalink structure for all of your target objects. Then you must manually resolve which object you want by hooking into the request filter hook.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Comments

« Previous 1 … 3 4 5 6 7 8 Next »
  1. Ian says

    May 8, 2012 at 3:44 pm

    Thanks so much for this in depth post. Really saved me on a bad permalink custom post type I had set up. I updated the permalink structure and they all broke. They are fixed now!

    Reply
  2. Gam says

    April 25, 2012 at 6:06 pm

    There was a question about showing cats and sub-cats in the url, like you can with regular wordpress categories. Of note here is when you register the taxonomy, there is a setting in the rewrite rules to do this.

    Under rewrite, the argument pair ‘hierarchical’ => true, turns on allowing /topic/sub-topic/super-sub-topic/, based on your structure, in the example called topic.


    register_taxonomy('topic', 'gam_qa',
    array(
    'labels' => array(
    'name' => _x( 'Topics', 'taxonomy general name' ),
    'singular_name' => _x( 'Topic', 'taxonomy singular name' ),
    'search_items' => __( 'Search Topics' ),
    'all_items' => __( 'All Topics' ),
    'parent_item' => __( 'Parent Topic' ),
    'parent_item_colon' => __( 'Parent Topic:' ),
    'edit_item' => __( 'Edit Topic' ),
    'update_item' => __( 'Update Topic' ),
    'add_new_item' => __( 'Add New Topic' ),
    'new_item_name' => __( 'New Topic Name' ),
    'menu_name' => __( 'Topics' ),
    ),
    'public' => true,
    'show_ui' => true,
    'hierarchical' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'q-and-a/topic', 'with_front' => true, 'hierarchical' => true ),
    'capabilities' => array(
    'manage_terms' => 'manage_qa_terms',
    'edit_terms' => 'edit_qa_terms',
    'delete_terms' => 'delete_qa_terms',
    'assign_terms' => 'assign_qa_terms'
    )
    )
    );

    Then the function you call from. This is a stripped down version, using only the %topic% tag, which will be the name of your custom taxonomy.

    The code looks for a parent to the topic, and as long as it finds one, adds it to the url.


    function gam_qa_permalinks($permalink, $post_id, $leavename) {

    $post = get_post($post_id);
    $rewrite_code = array(
    '%topic%'
    );

    if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {

    if ( strpos($permalink, '%topic%') !== false ) {
    $terms = wp_get_object_terms($post->ID, 'topic');
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) {
    $topics[] = $terms[0]->slug;

    $parent = $terms[0]->parent;
    while($parent) {
    $term = get_term($parent, 'topic');
    $parent = $term->parent;
    $topics[] = $term->slug;
    }
    $topics = array_reverse($topics);

    $taxonomy_slug = join('/', $topics);
    }else{
    $taxonomy_slug = 'no-topic';
    }
    }else{
    $taxonomy_slug = '';
    }

    $rewrite_replace =
    array(
    $taxonomy_slug
    );
    $permalink = str_replace($rewrite_code, $rewrite_replace, $permalink);
    } else { // if they're not using the fancy permalink option
    }
    return $permalink;
    }

    Reply
  3. Kyle says

    April 18, 2012 at 2:02 pm

    You just saved my sanity. I’ve been fighting with these dang ole permalinks/pagination issues for the last week straight.

    Great stuff, keep it up and thanks!

    Reply
  4. Amy says

    March 27, 2012 at 6:35 am

    Very basic error, not sure if I’m being silly. I’ve put the ‘// Add filter to plugin init function’ at the top of functions and then am using a custom post type called features, followed all your steps above.

    I want my structure to be

    features/%year%/%month/%day%/%postname%

    but I’m just getting a 404 error?

    add_action('init', 'feature_register');

    function feature_register() {

    $labels = array(
    'name' => _x('Feature', 'post type general name'),
    'singular_name' => _x('Feature', 'post type singular name'),
    'add_new' => _x('Add New', 'Feature item'),
    'add_new_item' => __('Add New Feature'),
    'edit_item' => __('Edit Feature Item'),
    'new_item' => __('New Feature Item'),
    'view_item' => __('View Feature Item'),
    'search_items' => __('Search Feature'),
    'not_found' => __('Nothing found'),
    'not_found_in_trash' => __('Nothing found in Trash'),
    'parent_item_colon' => ''
    );

    $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true,
    'query_var' => true,
    'rewrite' => false,
    'capability_type' => 'post',
    'hierarchical' => false,
    'has_archive' => true,
    'menu_position' => null,
    'supports' => array('title','editor','thumbnail', 'comments','trackbacks', 'author'),
    'taxonomies' => array('post_tag'),
    );

    register_post_type( 'feature' , $args );
    }

    // add to our plugin init function
    global $wp_rewrite;
    $feature_structure = '/features/%year%/%monthnum%/%day%/%postname%';
    $wp_rewrite->add_rewrite_tag("%feature%", '([^/]+)', "feature=");
    $wp_rewrite->add_permastruct('feature', $feature_structure, false);

    Any help appreciated, thanks

    Reply
    • ShibaShake says

      March 27, 2012 at 8:05 pm

      Try
      ‘/features/%year%/%monthnum%/%day%/%feature%’

      Reply
      • Connor says

        November 19, 2014 at 11:54 am

        This gives me a 404 as well. The only way for me to avoid the 404 is with something like this:

        global $wp_rewrite;
        $story_structure = ‘/stories/%story%’;
        $wp_rewrite->add_rewrite_tag(“%story%”, ‘([^/]+)’, “story=”);
        $wp_rewrite->add_permastruct(‘story’, $story_structure, false);

        Anything involving %year%/%monthnum%/%day% results in a 404. Any thoughts?

      • ShibaShake says

        November 19, 2014 at 1:36 pm

        Sounds like a permalink conflict issue.
        http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2#conflict

  5. Xevo says

    January 24, 2012 at 9:30 am

    Hi, nice article, but is it possible to use a custom post meta in the permalink?

    Reply
    • Xevo says

      January 25, 2012 at 12:19 am

      Already solved this and it is possible, however when visiting the url, it gives a 404.

      I checked the comments, but could not find a good solution for this.

      Reply
      • ShibaShake says

        January 25, 2012 at 10:56 am

        http://shibashake.com/wordpress-theme/add-custom-taxonomy-tags-to-your-wordpress-permalinks

  6. Amanda says

    December 21, 2011 at 1:59 pm

    Thanks for the info! It worked great to change the permalinks on my custom post type, but I am getting a 404 when I try to view the post? Is there something I’m missing? I’ve tried saving the Permalinks Settings Page, but that hasn’t done anything. Thanks in advance!

    Reply
    • Amanda says

      December 21, 2011 at 2:03 pm

      Sorry, I wrote too soon! I saw you already answered it in the previous comments and that worked for me too. Thanks again for the great info!

      Reply
  7. Rafal Jaskolski says

    November 2, 2011 at 5:48 am

    Hi 🙂

    great thanks for this tutorial. It works for me, but….

    when I set rewrite as “%category%” im my post custom type there is conflict with pemalink for standard post.

    How can I solve this?

    Reply
    • ShibaShake says

      November 8, 2011 at 11:24 am

      Add in a unique slug.
      http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2#conflict

      Reply
  8. Giuseppina says

    October 9, 2011 at 6:00 am

    Hello there, thanks for your job, I have set a permalink structure for my custom type ‘spartiti’ in this way:
    /%year%/%monthnum%/%day%/%postname%/
    but I would like to have my archive for this custom post type in this way:

    mywebsite/spartiti

    Is this possible?
    Thank you

    Reply
  9. Tobias says

    September 29, 2011 at 6:09 pm

    Works great, thanks a billion!!! I modified it slightly for a custom post that I wanted to display like /books/%post_id%/%postname% so I was able to remove much of the code, and it looks like this:


    function BookPermalink( $permalink, $post_id, $leavename )
    {
    $post = get_post( $post_id );

    // Don't rewrite unless it's published
    if ( '' != $permalink && !in_array( $post->post_status, array('draft', 'pending', 'auto-draft') ) )
    {

    // These are the things we'll look for
    $rewrite_old = array(
    '%post_id%',
    $leavename? '' : '%pagename%',
    $leavename? '' : '%postname%'
    );

    // And here we decide what to replace them with
    $rewrite_new = array(
    $post->ID,
    $post->post_name,
    $post->post_name
    );

    // Now do the replacing
    $permalink = str_replace($rewrite_old, $rewrite_new, $permalink);

    }

    return $permalink;
    }

    Reply
« Previous 1 … 3 4 5 6 7 8 Next »

Recent Posts

  • Screenshot of an example article in code view of a modified Gutenberg editor.How to Harness the Power of WordPress Gutenberg Blocks and Combine It with Legacy Free-Form Text
  • Screenshot of the Success, WordPress has been installed page.Migrating Your WordPress Website to Amazon EC2 (AWS)
  • Screenshot of WinSCP for creating a SFTP configuration.How to Set-Up SFTP on Amazon EC2 (AWS)
  • WordPress Gutenberg code view screenshot of this article.How to Prevent Gutenberg Autop from Messing Up Your Code, Shortcodes, and Scripts
  • Screenshot of the Success, WordPress has been installed page.How to Create a WordPress Website on Amazon EC2 (AWS)

Recent Comments

  • Screenshot of the Success, WordPress has been installed page.How to Create a WordPress Website on Amazon EC2 (AWS) (1)
    • Erik
      - Great article. All worked great except for this step:apt install php-mysqlChanging to this fixed it:apt install ...
  • Add Custom Taxonomy Tags to Your WordPress Permalinks (125)
    • Anthony
      - Where does this code go? Like, what exact .php file please?
  • Screenshot of an example article in code view of a modified Gutenberg editor.How to Harness the Power of WordPress Gutenberg Blocks and Combine It with Legacy Free-Form Text (1)
    • tom
      - hi,my experience was like the same, but for me as theme developer the "lazy blocks"(https://wordpress.org/plugins/lazy-blocks/) ...
  • WordPress Custom Taxonomy Input Panels (106)
    • Phil T
      - This is unnecessarily confusing. Why declare a variable with the same name as your taxonomy? Why did you choose a taxonomy ...
  • Create Pop-up Windows in Your WordPress Blog with Thickbox (57)
    • Jim Camomile
      - I have used this tutorial several times and it is one of the few simple and effective ways to add popups with dynamic content, ...

Copyright © 2024 · Genesis Skins by ShibaShake · Terms of Service · Privacy Policy ·