I’ve a great deal of experience with two open source content management systems: WordPress and Drupal. They both have their strengths and weaknesses, which I won’t get into here. If I had to pick the key difference between the two, though, it’s in how they answer one question:
What is a URL?
In WordPress, a URL indicates which posts to display. Your request is mapped to query variables (if you don’t have permalinks turned on, you’ll see these variables directly in your query string), and those variables are used to manipulate the database query in $wp_query->query()
. It’s a very nice, consistent system: a URL equals one or more posts (or a 404 error). (NB. the admin section is a different beast entirely.)
With Drupal, a URL is an entry in the menu system. When Drupal sees the URL, it looks up the corresponding entry, and calls the function specified in that entry. That function is responsible for creating the contents of the page, be it a list of nodes, a form, flying monkeys, or what have you. You can use a module like Views to achieve results similar to WordPress, but you have a lot more power and flexibility to do something different with a given page.
Sometimes you want that power and flexibility in WordPress. You just want to say, “When a user visits this URL, call this function and display its output.” That’s not really the “WordPress way” of doing things, and it requires a fair amount of code to accomplish what seems to be a simple instruction.
Abstract the Details Away
So I went and wrote another WordPress plugin. WP Router abstracts away all the messy details of declaring a callback function for a URL in WordPress. One method call is all it takes to set up your path, your rewrite rules, your query variables, your access rules, your title, your template overrides. It reduces a few dozen action/filter callbacks to a small list of easy-to-understand arguments.
If you’ve every worked with Drupal’s menu system, you’ll find many of the arguments familiar, adapted, of course, for the WordPress framework. Read all about them in the usage notes, and check out the sample code included in the plugin.
Ongoing Development
Right now, this is at version 0.2, which is synonymous with “has all the features I thought to add initially, plus one more”. But I’m just one developer. What would make this plugin more useful to you? What doesn’t work like you expect it to? What part of the API is confusing? Please leave a comment here or create an issue in Github.
And if you’re interested in contributing, feel free to fork the project and send me updates. It’s hosted at Github, with released copied over to WordPress’s plugin repository. As usual with code I write, it’s licensed under the MIT License (i.e., do whatever you want with it, just mention where it came from).
I guess my main question before I dig into the code is: does the routed callback effectively prevent WP_Query from firing?
Anyway, looks like a cool idea – I don’t have any projects that would require this at the moment, but I have one on the horizon that it might be useful for.
WP_Query still fires, but the query is modified to return a dummy page that will be filled in with the content from your callback. Checkout
WP_Router_Page::add_hooks()
. That’s where we set up the hooks to modify the query and then filter the contents of the page.It’s definitely a bit above my head and I don’t fully understand why and how this would be used. I sort of understand the need to be able to go to any URL and control what’s displayed there, but find WordPress’ method of URL structuring pretty sufficient for all my needs so far. You have pages, posts, CPT’s, etc. and nice URL’s are generated for each. Within the standard template files or via hooks, the content on any page can be manipulated to display what and how you want to display it. Can you provide some more examples of where and how this would be used?
I’m always excited by WordPress plugins to make my and my clients lives easier and better but I don’t have that “oh my god I’ve been waiting for this forever and this is a must use plugin” reaction yet…
Maybe you can persuade me? Why should I use this plugin or why do I need it? Good work though and congratulations on this accomplishment!
Definitely, the usefulness depends on what you need to do; this is no sonic screwdriver. A few use cases I can think of:
1. Forms (e.g., a registration form, a cart/checkout form, a send a letter to your congressman form, etc.). Some forms make sense to store in posts, other don’t. For the latter, you need a way to include the form on the page, and process the results, and this plugin makes that easier.
2. APIs (especially of the RESTful variety). This makes it easy to set up URL endpoints, and pass arguments from the URL string to a callback function.
3. Displaying external data. Say you’re pulling data off of an external API, and you want to generate reports or dynamic content based on that data. You won’t have any posts in your database corresponding to this external data, but you need to call a function to display it.
In general, if you want to display a post, or a list of posts, this plugin is not the answer (although it could be horribly abused to do so). If you want to display something that is not a post, though, this provides an easy way to do it that was missing in WordPress.
Thanks for the reply.
In case #1 I would almost always use a separate plugin because of the complexities involved with forms and eCommerce. No sense to me in reinventing a wheel when there’s the fantastic Gravity Forms and Shopp eCommerce plugins.
Case #2 & #3 (especially #3), I can see as being useful but I still can’t imagine a scenario where I’d use it.
To display something other than a post I like to use Pods. You setup whatever data type you need and add whatever fields you need and display them wherever and however you want. I wonder how similar your plugin is to Pods? Have you tried working with Pods at all?
APIs are a great use-case. It’d probably be a good idea to put that (and any other examples that spring to mind) in the readme as an example of something that this could be used for.
Hi, Nice plugin, working well if I use it with a GET request (like you sample). But when I try to do a POST request it told’s page not found, but it use the template for the route-url, so something is working.. I wonder if you have some more samples, one with post request maybe?
Hi, Mikael. Here’s what it would look like with separate GET and POST callbacks:
display_activation_page()
will be called by default, butprocess_activation_form()
will be called on POST requests.Tanks, but if i have this code:
$router->add_route('wp-router-sample', array(
'path' => '^route/(.*?)$',
'query_vars' => array(
'sample_argument' => 1,
),
'page_callback' => array(get_class(), 'sample_callback'),
'page_arguments' => array('sample_argument'),
'access_callback' => TRUE,
'title' => 'Page title',
'template' => array('page-sample.php', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'page-sample.php'),
));
It works go in to the page url directly, but when I do a POST (from the sample_callback-function to the same url)will I came to the right page-taplate but function is not call and it is not writing the correct page title.
When I try to change the code to:
$router->add_route('wp-router-sample', array(
'path' => '^route/(.*?)$',
'query_vars' => array(
'sample_argument' => 1,
),
'page_callback' => array('default' => array( $this, 'sample_callback' ),
POST' => array( $this, 'sample_callback_form' ),),
'page_arguments' => array('sample_argument'),
'access_callback' => TRUE,
'title' => 'Page title',
'template' => array('page-sample.php', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'page-sample.php'),
));
destroys it all and I land on my news page.
Some tips?
It looks like you’re setting up your route correctly, so there must be something elsewhere in your code that’s intercepting the request. If you want to share it (maybe a gist), I’ll take a look.
How do I create multiple paths? Do I need to create a classes.php file for each one ?
Just call $router->add_route() for each path. Give each one a unique ID and an appropriate path.
I get this error:
Fatal error: Cannot redeclare WP_Router_Sample::generate_routes()
I posted the full problem here: http://wordpress.org/support/topic/how-to-add-multiple-routes
I decided to forego using the *.classes.php in wp-route/ dir and instead I have the code in a plugin I made.
I do have multiple $router->add_route() with unique IDs and still no joy.
What could the issue be?
You mentioned above:
“2. APIs (especially of the RESTful variety). This makes it easy to set up URL endpoints, and pass arguments from the URL string to a callback function.”
How would this work with an API exactly? Say I have data that I retrieve from an API via a URL endpoint, it comes in as xml and I need to grab certain pieces of that data to populate a form. I am currently doing this using jquery ajax and php curl. Would this plugin make this easier? Safer? How so?
Thanks for your time.