WordPress: Creating new routes (custom URLs) with WP Router

August 6, 2012

Quite often I come across projects where I need to create custom URLs and endpoints for specific functionality. I am currently working on a rather large API and working with the built in WordPress Rewrite API can be a major pain in the rear. Enter WP Router by Jonathan Brinely. WP Router takes all the fuss out of creating new endpoints by giving developers a simple and easy to understand array of values to set. I am going to show you a quick example of creating a new endpoint that will simply output some text.

Step One: Go get the plugin

WP Router is a library that has been built into a plugin. Visit the Plugins -> Add New menu on your WordPress install and you should be able to find it with the search ‘wp router’. Click the Install Now button and you will be off to the races!

Alternatively you may also download the zip file from the WordPress Repository and manually install WP Router:

http://wordpress.org/extend/plugins/wp-router/

Or you can use Git to pull the project files from Github:

https://github.com/jbrinley/WP-Router

MAKE SURE YOU ENABLE THE WP ROUTER PLUGIN!

Step Two: Get hooked

I am going to assume you understand how to add functionality through your theme functions.php file or a plugin and skip the low level detail. If you are not sure how to write a plugin read this Writing a Plugin entry on the Codex.

WP Router provides custom action hooks that you can tap into. The hook that we will be looking at is wp_router_generate_routes. This hook will pass one parameter to your function that is the WP Router object itself. This object is what you will use to add new routes. The following line is the action hook used in this demo:

add_action( 'wp_router_generate_routes', 'bl_add_routes', 20 );

Step Three: Add the route

The WP Router object that is passed to our bl_add_routes() function has a method called add_route(). The add_route() method takes two parameters:

  1. String – Unique route id
  2. Associative Array – Route arguments

For this demo you will not need all of the possible arguments. A full list of the arguments can be found in the readme.txt file.

Arguments

The arguments are in the form of an associative array. It does not matter which order you input the arguments in. Here are the arguments you will want to be familiar with:

  • `path` (required) – A regular expression to match against the request path. This corresponds to the array key you would use when creating rewrite rules for WordPress.
  • `query_vars` – An associative array, with the keys being query vars, and the values being explicit strings or integers corresponding to matches in the path regexp. Any query variables included here will be automatically registered.
  • `page_callback` (required) – A callback to use for dynamically generating the contents of the page. The callback should return the contents of
    the page. If `FALSE` is returned, nothing will be output, and control of the page contents will be handed back to WordPress. The callback will be called during the `parse_request` phase of WordPress’s page load. If `access_callback` returns `FALSE`, `page_callback` will not be called.
  • `access_callback` – A callback to determine if the user has permission to access this page. If `access_arguments` is provided, default is `current_user_can`, otherwise default is `TRUE`. If the callback returns `FALSE`, anonymous users are redirected to the login page, authenticated users get a 403 error.
    `access_callback` can be either a single callback function or an array specifying callback functions for specific HTTP methods (e.g., `GET`, `POST`, `PUT`, `DELETE`, etc.). If the latter, the `default` key will be used if no other keys match the current request method.
  • `title` - The title of the page.
  • `template` – An array of templates that can be used to display the page. If a path is absolute, it will be used as-is; relative paths allow for overrides by the theme. The string `$id` will be replaced with the ID of the route. If no template is found, fallback templates are (in this order): `route-$id.php`, `route.php`, `page-$id.php`, `page.php`, `index.php`. If FALSE is given instead of an array, the page contents will be printed before calling `exit()` (you can also accomplish this by printing your output and exiting directly from your callback function).

Here is the demo code you will use for this post:

$route_args = array(
     'path' => '^new-demo-route',
     'query_vars' => array( ),
     'page_callback' => 'bl_new_demo_route_callback',
     'page_arguments' => array( ),
     'access_callback' => true,
     'title' => __( 'Demo Route' ),
     'template' => array(
         'page.php',
         dirname( __FILE__ ) . '/page.php'
     )
);

I want to make you aware here that there are several argument that help make the title dynamic. Read the WP Router readme.txt for the full argument list

add_route

Whew! Now that that mess is over you can easily add the new route. Simply pass in the unique route id along with the argument list list so:

$router->add_route( 'demo-route-id', $route_args );

See how simple that was? If you have ever worked with the WordPress Rewrite API you will be sighing in relief. If you have not, count your blessings.

Step Four: Handle the new route

Remember back in step three when you created the page_callback argument? That is the name of the function WP Router will call when the URL containing your new route is entered. To make it easy on yourself in the beginning you have created a route that does not require any arguments passed into the callback function, and to see it working quickly you are going to output a simple string instead of doing fancy complex work right off the bat. Let’s take a look at what this callback function looks like.

function bl_new_demo_route_callback( ) {
     return "Congrats! Your demo callback is fully functional. Now make it do something fancy";
}

Simple, quick, beautiful

Step Five: View your handiwork

Now that you have gone through all that hard work it is time to view your handiwork and see what the new route looks like. At this point you should have the WP Router plugin installed and the above demo code in a functions.php file or plugin (You enabled the theme and plugins correct?). It is time to view the result. Go to http://yourdomain.com/new-demo-route (Obviously replace yourdomain.com with your real domain). I created this demo with the Twenty Twelve theme enabled so your output may look different than mine. Not to worry, if it displays in your theme then you did it right.

All together now!

Here is the demo code in its entirety, for your pleasure:

add_action( 'wp_router_generate_routes', 'bl_add_routes', 20 );

function bl_add_routes( $router ) { 
    $route_args = array(
                        'path' => '^new-demo-route',
                        'query_vars' => array( ),
                        'page_callback' => 'bl_new_demo_route_callback',
                        'page_arguments' => array( ),
                        'access_callback' => true,
                        'title' => __( 'Demo Route' ),
                        'template' => array(
                                    'page.php',
                                dirname( __FILE__ ) . '/page.php'
                        )
                );

    $router->add_route( 'demo-route-id', $route_args );
}

function bl_new_demo_route_callback( ) {
    return "Congrats! Your demo callback is fully functional. Now make it do something fancy";
}

Questions? Feel free to leave a comment below and I will get back to you ASAP

13 thoughts on “WordPress: Creating new routes (custom URLs) with WP Router

  1. D (October 21, 2013)

    What did you call the file name that you put this code in?

    1. Ben Lobaugh (blobaugh) (October 22, 2013)

      The name does not matter. It was simply in a plugin file. Any plugin file you create will do. http://codex.wordpress.org/Writing_a_Plugin

      1. D (October 23, 2013)

        Thank you for your reply!

        Oh! I see. This is code you put in plugin file. Apologies, didn’t comprehend that clearly.
        So, still unclear about something. In the plugins/WP-Router dir there is a file name called WP-Router-Sample.class.php
        That shows an example of how the plugin works.
        If one creates a plugin, there is no need to have a file like this desginated?

        Also how does one declare multiple routes?

        Thanks in advance!

        1. Ben Lobaugh (blobaugh) (October 23, 2013)

          You can add as many of these as you want

              $route_args = array(
              'path' => '^new-demo-route',
              'query_vars' => array( ),
              'page_callback' => 'bl_new_demo_route_callback',
              'page_arguments' => array( ),
              'access_callback' => true,
              'title' => __( 'Demo Route' ),
              'template' => array(
              'page.php',
              dirname( __FILE__ ) . '/page.php'
              )
              );
          $router->add_route( 'demo-route-id', $route_args );
          

          Just be sure to change the names and callbacks.

          1. D (October 23, 2013)

            does each one need to be it’s own plugin or they can be seperate code blocks within the same plugin?

          2. D (October 23, 2013)

            Disregard previous the multiple plugin question I just asked, please. My question is with each new route, do I need to mention it in:
            `add_action( ‘wp_router_generate_routes’, ‘bl_add_routes’, 20 );`

            for example:
            `add_action(‘wp_router_generate_routes’, array(‘bl_add_routes’, ‘bl_add_2nd_route’, ‘bl_add_3rd_route’, 20);`

            and then have to make them functions, right?

          3. Ben Lobaugh (blobaugh) (October 23, 2013)

            No need for separate functions or plugins. All the routes can be created in the one function called from your add_action(). Just setup as many arrays as needed and pass them to $router->add_route() for each new array.

        2. Allen Snook (allendav) (October 23, 2013)

          Test comment, please ignore

          $foo = ‘bar’;

          Here ends the test comment

  2. Gaetano (November 11, 2013)

    I grabbed the code from all together now and used it in the functions.php
    I have a page and a template added to that page. Within the template I want to execute some code and will use the route to another page when active. Is this correct or do I need to do something else?
    Can you add more comments to show me how to invoke this and get this running so I can see it in action?
    Thanks

  3. davidstanley01 (February 21, 2014)

    How do you use the access_callback parameter for different HTTP methods. The docs say “an array specifying callback functions for specific HTTP methods (e.g., `GET`, `POST`, `PUT`, `DELETE`, etc.)” but there is no example of how to structure that.

    1. Ben Lobaugh (blobaugh) (February 24, 2014)

      I am not sure what you are getting at. Can you provide or link to an example? Here are a list of the possible arguments. I do not see a callback https://core.trac.wordpress.org/browser/tags/3.8.1/src/wp-includes/http.php#L136

  4. Saurav (September 26, 2017)

    Hi Ben I am trying to add routes with multiple segment like htttp://www.example.com/find-store/category/location , please help me out with this. Also the regex for path is not working. You can find mycode below.
    add_action( ‘wp_router_generate_routes’, ‘bl_add_routes’, 20 );

    function bl_add_routes( $router ) {
    $route_args = array(
    ‘path’ => ‘^find-store/(.*)$’,
    ‘query_vars’ => array( ‘name’ => 1),
    ‘page_callback’ => ‘bl_new_demo_route_callback’,
    ‘page_arguments’ => array( ‘name’),
    ‘access_callback’ => true,
    ‘title’ => __( ‘Demo Route’ ),
    ‘template’ => array(
    ‘page.php’,
    dirname( __FILE__ ) . ‘/page.php’
    )
    );

    $router->add_route( ‘demo-route-id’, $route_args );
    }

    function bl_new_demo_route_callback( ) {
    return “Congrats! Your demo callback is fully functional. Now make it do something fancy”;
    }

  5. Vae (December 10, 2017)

    Hi, it is possible to handle the plain case of permalink? I mean the case which started with ‘?’. How can I do it?