Customising rewrite rules for CPT single post URL to work as paged URL

I’ve got a CPT called Service registered as following:

$args = array(
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true,
    'has_archive' => false,
    'rewrite' => array('slug' => 'services', 'with_front' => false),
    'query_var' => true,
    'capability_type' => 'post',
    'hierarchical' => false,
    'show_in_nav_menus' => false,
    'menu_position' => 20,
    'supports' => array(
      'title'
      'editor',
      'thumbnail',
      'author',
      'revisions'
    )
);
register_post_type('service', $args);

I have a page called “Services” that has got a custom template assigned to it that displays all of the services with their permalinks of type services/copywriting (where ‘copywriting’ is the name of the Service). So far so good.

Read More

I’ve got another CPT called Case Study. Now, the Case Studies are connected to Services posts via Posts 2 Posts plugin.

I’m trying to do the following: to use a single Service post URL of type /services/copywriting to display all Case Studies associated with that Service.

For that purpose I’ve added the template redirect rule as following: so that instead of single.php template the archive.php template will be displayed.

add_action("template_redirect", 'template_redirect');
function template_redirect(){
    global $wp, $wp_query;

    // if Service CPT and it's a single page URL then redirect to archive.php template
    if($wp->query_vars["post_type"] == "service" && is_single()){
        include(TEMPLATEPATH . '/archive.php');
            exit; //thanks to @kaiser's comment
    }
}

In the archive.php template I’ve got:

  • a custom SQL that gets all the Case Studies associated with current Service (that I determine by looking at the URL services/copywriting where ‘copywriting’ is the current service).
  • a function that creates links for pagination of type services/copywriting/page/<page_num>

However, those links of type services/copywriting/page/<page_num> don’t work and they always get redirected to the single Service URL link services/copywriting.

Is there something I can add to re-write rules or filters or something to make the paged links work in that manner? So that services/copywriting/page/2 will work and would be directed to archive.php template where my custom SQL will kick in and display the correct Case Studies.

Do I have to have a dedicated template rather than archive.php?

I would really appreciate any tips and help!

Many thanks,
Dasha

EDIT

I’ve installed Rewrite Analyzer plugin. And the test for services/copywriting/page/2 matched the following pattern:

services/([^/]+)/page/?([0-9]{1,})/?$ 

with the following substitution:

service: copywriting
paged: 2

I’ve no idea what it means though 🙁

EDIT 2

OK, I’ve tried adding a rewrite rule as following in the functions.php:

add_action('init', 'my_rewrite_add_rewrites');
function my_rewrite_add_rewrites(){

    //rewrite rule for Service single post, make it work as paged
    add_rewrite_rule(
        '^services/(.+?)/?(page/([0-9]+))?/?$',
        'index.php?post_type=service&service=$matches[1]&paged=$matches[3]',
        'top'
    );
}

I’ve flushed the rewrite rules. However, when I go to URL of type services/copywriting/page/2 it always just displays the URL without the page bit services/copywriting/.

I can’t figure out what’s wrong. Is my regexp wrong or the index.php construction or both?

I would really appreciate any help!

Related posts

Leave a Reply

2 comments

  1. Ok, so I got it working more or less correctly, although there is still a small problem left, please read on..

    Here is what I’ve got:

    in fucntions.php:

    Rewrite rules

    add_action('init', 'my_rewrite_add_rewrites');
    function my_rewrite_add_rewrites(){
        add_rewrite_rule(
            '^services/([^/]+)/?$',
            'index.php?post_type=service&order=ASC&orderby=menu_order',
            'top'
        );
        add_rewrite_rule(
            '^services/([^/]+)/page/?([0-9]{1,})/?$',
            'index.php?post_type=service&paged=$matches[2]&order=ASC&orderby=menu_order',
            'top'
        );
    }
    

    Redirect rule

    add_action("template_redirect", 'my_template_redirect');
    function my_template_redirect(){
        global $wp_query;
    
        // if Service CPT URL then redirect to archive.php template
        if($wp->query_vars["post_type"] == "service"){
            include(TEMPLATEPATH . '/archive.php');
            exit;
        }
    }
    

    So now, the URLs of type ‘services/copywriting’ and ‘services/copywriting/page/2’ and so on are working.

    However, there is a small problem left. For example, if I’ve got 2 pages of copywriting services in total then the following two URLs are working correctly:

    • services/copywriting/
    • services/copywriting/page/2

    However, the URL with a “non existing page” doesn’t get redirected to 404 template, but still displays archive template with, obviously, empty content. So how can I make sure that such “non existing” paged URLs will be redirected to 404 template correctly? For example, services/copywriting/page/3 URL when there are only data for 2 pages.

    EDIT

    I have just also noticed that when a non existing service is referenced as in ‘services/bla-bla’ then the archive template is also displayed rather than 404 template.

    I would hugely appreciate any tips, before I go totally mad.
    Thanks.

  2. As far as I understood the problem, you want to make your CPT paged.
    After you correctly declared CPT in your functions.php, just make sure that a relevant single-{posttypename}.php template has global $page $paged declared in the head, and contains reference to wp_link_pages() function.

    Exampli gratia:

    <div class="navigation"><?php
    wp_link_pages(array('before' => '<strong>Pages:</strong> ', 'after' => '&nbsp;', 'next_or_number' => 'number'));
    </div>

    Now this CPT supports <!--nextpage--> and your URLs will generate accordingly.

    Also this discussion may help. Remember – you cannot have CPT and Page with the same name.