0

Let's suppose a Custom Post Type called "Whatever", which will have a root slug set to "whatever".

This makes all posts of type "whatever" be available at site.url/whatever/post-name

I also want a parent page called Whatever to be available at site.url/whatever

To achieve that, I must create the page with said slug and also set the has_archive property to false, when registering the "Whatever" CPT.

So far so good.

Now, I want to have some specific child pages for the page "Whatever", and I want them to be available at site.url/whatever/child-page-name

This last step is not working because when accessing that url, WordPress tries to load a CPT of type "Whatever" with name "child-page-name". And it does not exist.

What I want to do is to prioritize pages during the loading process, so that if a child page of "Whatever" exists with the requested name, it should always be loaded (even if a CPT exists with said name).

I don't know where to start. I thought about having the single-whatever.php file look for a page with the requested post name. But, I don't know what to do with that information if the page exists. Should I redirect? Filter the loaded template? Here is where I am stuck, and any help will be appreciated.

2
  • The EASIEST way that you could do this with your set up is create the "child-page-name" as one of your actual post types.
    – rudtek
    Oct 15, 2021 at 4:32
  • @rudtek Thanks for the suggestion, but in this case I also need the page to use page templates, which are different than the post-type template. Oct 15, 2021 at 8:07

1 Answer 1

2

I want to have some specific child pages for the page "Whatever", and I want them to be available at site.url/whatever/child-page-name

What I want to do is to prioritize pages during the loading process, so that if a child page of "Whatever" exists with the requested name, it should always be loaded (even if a CPT exists with said name).

If the child pages have to be Pages (posts of the page type), then you can use the parse_request hook to conditionally change the request type, which in your case is from a custom post type request to a standard (child) Page request, which means that is_page() would return true and WordPress would also load the correct template for Pages like page-whatever.php.

Working example, tried & tested on WordPress 5.8.1:

Note: get_page_uri() is used to retrieve the "directory" path of the child Page, so if parent is the parent Page slug and child is the child Page slug, then the path would be parent/child.

add_action( 'parse_request', 'my_parse_request' );
function my_parse_request( $wp ) {
    $post_type = 'whatever'; // set the post type slug
    // and the "whatever" below is the Page slug

    // This code checks if the path of the current request/page URL begins with
    // "whatever/" as in https://example.com/whatever/child-page-name and also
    // https://example.com/whatever/child-page-name/page/2 (paginated request).
    // We also check if the post_type var is the post type set above.
    if ( preg_match( '#^whatever/#', $wp->request ) &&
        isset( $wp->query_vars['post_type'], $wp->query_vars['name'] ) &&
        $post_type === $wp->query_vars['post_type']
    ) {
        $posts = get_posts( array(
            'post_type' => 'page',
            'name'      => $wp->query_vars['name'],
        ) );

        // If a (child) Page with the same name/slug exists, we load the Page,
        // regardless the post type post exists or not.
        if ( ! empty( $posts ) ) {
            $wp->query_vars['pagename'] = get_page_uri( $posts[0] );

            unset( $wp->query_vars['post_type'], $wp->query_vars['name'],
                $wp->query_vars[ $post_type ] );
        }
    }
}
1
  • 1
    Thanks for sharing this excellent solution. Oct 18, 2021 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.