I’ve dug through every question here on custom post type permalinks, but most seem to be either problems with custom taxonomy rewrites, or the obvious missing of flush_rewrite_rules(). But in my case, I’m only using a custom post type (no taxonomy), set to be hierarchical (so I can assign parent-child relationships), with the proper “support” for the attributes metabox, etc, etc. I’ve flushed rewrite rules a thousand different ways. I’ve tried different permalink structures. But child URLs always result in 404!

I originally had independent custom post types for the “parent” and “child” elements (using p2p), and I probably would have had no trouble using a taxonomy for the “parental” grouping – I know those would be semantically more accurate. But for the client, it is easiest for them to visualize the hierarchy when the “posts” are displayed in the admin just like pages are: a simple tree where children appear underneath the parent, prefixed with a “–“, and in the proper order. Also, various methods for assigning order via drag-n-drop can be used. Grouping via taxonomy (or p2p) results in a flat list of “posts” in the admin listings, which is simply not as visually obvious.

So what I’m after is literally the exact same behavior as core “pages”, but with my custom post type. I’ve registered the post type just as expected, and in the admin it works perfectly – I can assign a parent and a menu_order for each newsletter “post”, they appear correctly in the edit listings:

Spring 2012
— First Article
— Second Article

And their permalinks appear to be constructed properly. In fact, if I change anything about the structure, or even alter the rewrite slug when registering the post type, they automatically update correctly, so I know something’s working:

http://mysite.com/parent-page/child-page/                  /* works for pages! */
http://mysite.com/post-type/parent-post/child-post/        /* should work? */
http://mysite.com/newsletter/spring-2012/                  /* works! */
http://mysite.com/newsletter/spring-2012/first-article/    /* 404 */
http://mysite.com/newsletter/spring-2012/second-article/   /* 404 */

I also have standard core “pages” with hierarchical relationships created, and they look just the same in the admin, but they actually work on the front-end too (both parent and child URLs work fine).

My permalink structure is set to:

http://mysite.com/%postname%/

I’ve also attempted this (just because so many other answers seemed to indicate it was needed, though it didn’t make sense in my case):

http://mysite.com/%category%/%postname%/

My register CPT args include:

$args = array(
    'public'                => true,
    'publicly_queryable'    => true,
    'show_ui'               => true,
    'has_archive'           => 'newsletter',
    'hierarchical'          => true,
    'query_var'             => true,
    'supports'              => array( 'title', 'editor', 'thumbnail', 'page-attributes' ),
    'rewrite'               => array( 'slug' => 'newsletter', 'with_front' => false ),

The only visible difference between my custom post type children and normal page children, is that my CPT has the slug at the beginning of the permalink structure, then followed by the parent/child slugs (where pages just begin with the parent/child slugs, no “prefix”). Why this would foul things up, I don’t know. Plenty of articles seem to indicate that this is exactly how such hierarchical CPT permalinks should behave – but mine, though nicely formed, don’t work.

What also baffles me is when I examine the query_vars for that 404 page – they seem to contain the correct values for WP to “find” my child pages, but something’s not working.

$wp_query object WP_Query {46}
public query_vars -> array (58)
'page' => integer 0
'newsletter' => string(25) "spring-2012/first-article"
'post_type' => string(10) "newsletter"
'name' => string(13) "first-article"
'error' => string(0) ""
'm' => integer 0
'p' => integer 0
'post_parent' => string(0) ""
'subpost' => string(0) ""
'subpost_id' => string(0) ""
'attachment' => string(0) ""
'attachment_id' => integer 0
'static' => string(0) ""
'pagename' => string(13) "first-article"
'page_id' => integer 0
[...]

I’ve tried this with various themes, including twentytwelve, just to be sure it’s not some missing template on my part.

Using Rewrite Rules Inspector, this is what shows up for the url:
http://mysite.com/newsletter/spring-2012/first-article/

newsletter/(.+?)(/[0-9]+)?/?$   
       newsletter: spring-2012/first-article
           page: 
(.?.+?)(/[0-9]+)?/?$    
       pagename: newsletter/spring-2012/first-article
           page: 

how its displayed on another inspector page:

RULE:
newsletter/(.+?)(/[0-9]+)?/?$
REWRITE:
index.php?newsletter=$matches[1]&page=$matches[2]
SOURCE:
newsletter

This rewrite output would lead me to believe that the following “non-pretty” permalink would work:

http://mysite.com/?newsletter=spring-2012&page=first-article

It doesn’t 404, but it shows the parent CPT item “newsletter”, not the child. The request looks like this:

Array
(
    [page] => first-article
    [newsletter] => spring-2012
    [post_type] => newsletter
    [name] => spring-2012
)

5

This is my first time participating here on Stack Exchange, but I’ll give this a go and see if I can help point you in the right direction.

By default, hierarchical CPTs behave exactly the way you’ve described. The unique slug prefix, “newsletter” in this case, is to let the rewrite engine know how to tell requests for different post types apart.

Those CPT registration args look fine, but when a CPT is requested, the pagename query var should not have a value and the name query var should be the same as newsletter here, so it appears that there’s a conflict somewhere in your setup.

To help debug, install and activate the Rewrite Rules Inspector Plugin, then visit the screen at “Tools -> Rewrite Rules.”

  1. While looking at the list, all of your rules for the newsletter CPT should be listed before any page rewrite rules. Verify this is the case by scanning the “Source” column.
  2. If that checks out, input the URL for your “first-article” CPT in the “Match URL” field and click the “Filter” button to see which rule is matched. It should match a newsletter rule and a page rule, but the newsletter rule should be first.

If that doesn’t reveal any issues, search the post_name column in wp_posts to find other posts with the “first-article” slug to see if there might be a collision. Do a search for “newsletter” as well, just to be sure.

Add the following snippet to inspect your query vars early in the request to check and see which ones are being set by the rewrite rule match (visit the child CPT on the front end). If the pagename var doesn’t appear here, then it’s being set by something later in the request:

add_filter( 'request', 'se77513_display_query_vars', 1 );

function se77513_display_query_vars( $query_vars ) {
    echo '<pre>' . print_r( $query_vars, true ) . '</pre>';

    return $query_vars;
}

Any plugins/functions that modify the query vars could be causing a conflict, so disable them if you still see issues. Also flush your rewrite rules after each step, especially if the request was matching a wrong rule in the second step above (keep the “Permalinks” screen open in a separate tab and just refresh it).

Leave a Reply

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