Custom routing mechanism in WordPress themes (MVC like)

I had a need to implement custom routing system in my WordPress theme. I’m developing a web application, not a typical theme. There is a predefined set of pages in the app and each of them has its own view (template). Ideally, the design and structure (HTML/CSS) of these templates are located in files, not in a database. They can be versioned in Git that way. Nonetheless, I still have to insert the pages with corresponding page template settings into the database. Why?

Disclaimer: Written for WordPress version 4.1.1

Request parsing and routing 101

After obtaining database and other configuration, WordPress includes a lot of files from the wp-includes folder into the memory. Primary components are included in this order:

  1. must-use plugins
  2. plugins
  3. theme

Main classes such as WP, WP_Rewrite, WP_Query and many more are all instantiated at this point.

Next come parsing the URL request, sending HTTP headers and querying initial posts.

Parsing a request

During this process, WordPress rewrite rules are loaded from the database and matched against the URL string. Query variables such as name of a post or search string are extracted from the URL as well. For example, if we have an URL “https://lamosty.com/contact/” and enabled pretty permalinks (rewrite rules for pages and posts), pagename would be “contact”. WordPress will use these variables when deciding which theme file to load later on (index.php or page.php , etc).

Before finishing the parsing process, functions hooked into parse_request action hook are executed. This is an important moment and we’ll get back to it in a while.

Sending HTTP headers

Not the most important part for us. Several headers, such as Content-Type, Last-Modified (for caching) and other headers are set and send at this occasion. There is another action hook named send_headers located just before exiting the function.

Querying initial posts

If we have fully parsed request into variables, we can query some data from the database. It actually calls get_posts() method of WP_Query object, which is also called if you do:

$query = new WP_Query( $args );
// or
$posts = get_posts( $args )

in your plugins or themes.

The method is complex and I’m not going to delve into it as it has been described many times before.

The problem

If I’m working on a web application with a predefined static pages stored in files, why should I query the database?

Empty page
Figure 1: Empty page

Only to comply with the standard routing mechanism of WordPress? Wouldn’t it be better (in this case) to specify routing rules in a file which would be directly loaded just after the URL request is parsed?

Let’s look at Laravel routing:

Route::get('/', function()
{
    return 'Hello World';
});

Route::get('user/{id}', function($id)
{
    return 'User '.$id;
});

Or Ruby on Rails:

get '/', to 'app#index'

get '/user/:id', to: 'users#show'

Isn’t it beautiful? Instead of storing routes (= rewrite rules in WordPress) in the database as serialized string, store them nicely in a file. It has better performance and every developer on your team can instantly observe the routes. If the routing mechanism was like this in WordPress, we wouldn’t have to create blank pages pointing to static templates in our themes just for routing to work properly. We would simply include the templates in the matched routes and job is done.

My (failed) attempt at solving this “problem”

I attached a callback function to the parse_request action hook, after the request URL is parsed. Then I carefully inspected the query variables, loading page views (template files) according to them. Saving the code, reloading the site and… admin toolbar is missing – WTF?

Turns out I skipped a number of action and filter hooks! Oh my. Some of them:

  • send_headers
  • wp
  • template_redirect (!!!)

A few hours of fiddling with the code and reading how to do it the “best” way, I came to a conclusion — Do you want to use WordPress for your web application? Work the WordPress way then!

I’m not complaining

Well, I’m not complaining. While I have to do some sacrifices here and there, WordPress gives me numerous benefits (described in other articles on the web). I just needed to realize and learn how to go along with it, not against the stream.

You don't want to go against this water stream, do you?
Figure 2: You don’t want to go against this water stream, do you?

WordPress is powerful in its own ways. We should embrace them.

How others try to solve this “problem”

Join the Conversation

2 Comments

    1. OH I am wrong. it seems it still have to add page in wp-admin. So sad.

      However, I figure out some work around.
      to add only one page as a router to route every page in your web application
      1. add one page in wp-admin (for example, page named “router”),
      2. add a template in your folder for it with wordpress template hierarchy (page-router.php)
      3. add rewrite rule to make the url in your web application to redirect to “router”
      4. template page-router.php take the argument passed from url.

      Hope anyone have some smarter way to overcome this problem

      Like

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: