6

I created a plugin that sets up a custom route and then loads a template file for that url. Everything works fine, except that WordPress seems to think it's a 404 even though it's correctly rendering my template.

For instance it says 404 in the document title and an error404 class is added to <body>

The custom url is domain.com/path/:id where :id is a dynamic value corresponding to a post id, so the URL could be domain.com/path/275. In the example below some_id is used as the post id variable.

Here's a reduced version of my plugin:

<?php

class MyPlugin {
  public function __construct() {
    add_action( 'init', array($this, 'add_response_endpoint') );
    add_filter( 'template_include', array($this, 'add_response_template') );
  }

  public function add_response_endpoint() {
    add_rewrite_rule(
      '^path/([0-9]+)/?',
      'index.php?pagename=my_custom_url&some_id=$matches[1]',
      'top'
    );

    add_rewrite_tag('%some_id%', '([^&]+)');
  }

  public function add_response_template($template) {
    if ( get_query_var( 'pagename' ) === 'my_custom_url' ) {
      $template = trailingslashit( dirname( __FILE__ ) ) . 'templates/custom-page-template.php';
    }

    return $template;
  }
}

new MyPlugin();

Am I missing something here? Or should I start looking for this bug elsewhere?

5
  • Did you flush your rewrite rules? codex.wordpress.org/Function_Reference/flush_rewrite_rules
    – jgraup
    Commented Jun 1, 2016 at 13:30
  • Yes. Flushed the rewrite rules. Both manually and in the plugin's activation hook.
    – Ahrengot
    Commented Jun 2, 2016 at 10:16
  • Rewrite rules have to result in a successful main query. If my_custom_url is not the slug of an actual page, you will get a 404. If some_id is a valid post ID, then set the correct native query vars with that ID so the query succeeds.
    – Milo
    Commented Mar 1, 2017 at 16:08
  • That's just not always an option. For instance, if I want to provide a "My profile" frontend page for my users where they can update basic information about themselves. There's no post associated with such a page, but I still need a URL for it. In any case, I figured out a solution and posted my answer below.
    – Ahrengot
    Commented Mar 1, 2017 at 19:27
  • 1
    Here's an example of an approach that short-circuits the query parsing phase and doesn't produce a query that's just doomed to fail.
    – Milo
    Commented Mar 2, 2017 at 4:27

2 Answers 2

3

Manually setting is_404 = false; fixed my issue. However I'm not sure this is the best way to do it. I tried using the pre_get_posts filter instead without any luck.

Anyway, for anyone else in the same boat, you can do this to get rid of the 404 state:

public function add_response_template($template) {
  global $wp_query;
  if ( 'my_custom_url' === get_query_var( 'pagename' ) ) {
    $wp_query->is_404 = false;
    $template = trailingslashit( dirname( __FILE__ ) ) . 'templates/custom-page-template.php';
  }

  return $template;
}

And to update the document title (The stuff inside <title> in the <head> section) here's a snippet for making that work nicely too.

add_filter( 'document_title_parts', function($title_arr) {
  if ( 'my_custom_url' === get_query_var('pagename') ) {
    $title_arr['title'] = "Document title for my custom route";
  }

  return $title_arr;
}, 10, 1 );

If anyone knows of a better way please let me know.

1
  • 2
    is_404 = false; is not working for me the status still 404 but status_header(200); works! Commented Oct 19, 2020 at 21:09
0

I have not found custom routes in WP mature enough. I suggest you use any of the 3 routes solutions mentioned in Timber docs. https://timber.github.io/docs/v2/guides/routing/

if you use Upstatement library (https://github.com/Upstatement/routes) you would do:

Routes::map('info/:name/page/:pg', function($params){
    //make a custom query based on incoming path and run it...
    $query = 'posts_per_page=3&post_type='.$params['name'].'&paged='.intval($params['pg']);

    //load up a template which will use that query
    Routes::load('archive.php', null, $query, 200);
});

See the 200 in Routes::load, that sends the proper header.

If you need something more versatile use Rareloop router https://github.com/Rareloop/router

Your Answer

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

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