How to Include Your Rails App Layout in Your Wordpress Theme (or any PHP application)

23 February 2010

Here's the problem: You have a Ruby on Rails application and its Wordpress blog. For the sake of consistency and branding, you'd like to use the same layout in your Wordpress blog as you do for your Rails app. Perhaps your blog is one of the main links or tabs in your application's header.

Why this doesn't work: You proceed with creating a custom theme for your Wordpress blog that looks exactly like your Rails application. Then over the next couple years, you begin to realize how tedious this solution is. You have to copy over your CSS stylesheets from your Rails app, your Javascript files (if your standard layout contains any Javascript elements, like a Twitter widget in the footer or whatever), and your layout images and assets. Then, every time you change or update the layout of your Rails app, you have to copy the changes in your custom Wordpress theme. And you cannot just copy the changes, because if you have any Ruby/Rails functionality in the layout, you have to translate them into PHP functionality in your Wordpress layouts.

And then there's the impossible stuff... if anything in your Rails app layout loads information from the database in your Rails app, then you cannot have those elements in your Wordpress layout, unless you do something like create an XML or JSON feed in your Rails app and then import it in your Wordpress layout. But this is quite tedious still if you want to change any of it!

There's an easier way.

Include Your Rails Layout Directly In Your Wordpress Layout

The solution I found turns out to be quite simple. The basic idea is to create a `:partial` in your Rails app layout, make it publicly accessible via your `config/routes.rb`, have it generate and simply return the appropriate html using your controller method, and then import that html (and CSS) in your Wordpress layout.

For this to really work well, you'll want to make sure that you make your Rails app cache the partials to be imported into your Wordpress layout, so that a lot of traffic on your blog doesn't cause your Rails app server to explode.

1. Break Your Rails Layout into Partials

The first step is to break as much of your app/views/layouts/application.html.erb into partials as possible/desired. At the very least, this means a header and a footer partial. Typically, you would not include the stuff in the <head> part of the layout, because your Wordpress will likely require additional stylesheets and a different set of javascripts (not to mention you'll want to make your meta information specific for your blog pages).

A simple Rails layout using this method would look something like this:

<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<html>
  <head>
    ...
  </head>
  <body>
    <%= render :partial => 'static/header' %>
    <div id="content">
      <%= yield %>
    </div>
    <%= render :partial => 'static/footer'%>
    <%= render :partial => 'static/widgets' %>
  </body>
</html>

I've also included a widgets partial to show that this can be done with static javascript widgets as well.

Note that your layout and partials can have any and all Ruby/Rails code in them that you want. Since your Wordpress theme is importing this stuff from your Rails application, the Rails app will run as usual and just return the end result, which is HTML, so your Wordpress will have no problem interpreting it.

A simple app/views/static/_header.html.erb partial may look something like this:

<div id="header">
  <%= link_to image_tag("layout/my_logo.png"), root_path, :alt => "My Rails App", :id => "banner_logo" %>
  <div id="menu">
    <ul>
      <li>
        <%= link_to "Products", products_path, :class => ('active' if request.path == products_path) %>
      </li>
      <li>
        <%= link_to "Blog", blog_path, :class => ('active' if @blog) %>
      </li>
      <li>
        <%= link_to "Contact", contact_path, :class => ('active' if request.path == contact_path) %>
      </li>
    </ul>
  </div>
</div>

Notice that the normal links in the header are "active" simply if the current URL matches the link path, but the "Blog" link is active if the @blog variable is set and true. This is because, as you will see in the next section, the URL for the header when it is called from your blog won't be the blog URL.

2. Make Your Layout Partials Publicly Accessible

The next step is to make your layout partials publicly accessible. I.e. make it so that you can access your layout partials from the browser using a direct URL, like http://mysite.com/static/header. The actual URL you use doesn't really matter, because users won't ever need to see or use it.

The way we do this is to explicitly set a path for each layout partial in our config/routes.rb and then create a controller method for each that renders the HTML generated by the partial.

A path in your routes for your header partial might look like this:

ActionController::Routing::Routes.draw do |map|
  ...
  map.header_partial 'static/my_blog_header', :controller => 'static', :action => 'blog_header_partial'
  ...
end

Then in the app/controllers/static_controller.rb, your corresponding action would look like this:

def blog_header_partial
  @blog = true
  render :partial => 'static/header'
end

Now, hopefully you see why, in the header partial, we couldn't just say that the blog menu item is :class => ('active' if request.path == blog_path). The path that the header partial will be seeing when the Wordpress layout requests it will be the path you defined in your routes.rb, in this case "static/header". This is why we instead say :class => ('active' if @blog) and then set @blog = true in the controller action.

Include the Rails Partials In the Wordpress Layout

Here's the real trick. In order to include the Rails partials in your Wordpress layout, you might be inclined to use on of these PHP functions: include() or require(). This won't work though for two reasons.

1) include() and require() both read .php files. However, your publicly-accessible layout partials are going to return straight HTML.

2) By default, neither method allows absolute URLs due to security precautions. You can change your PHP setup to allow this, but again, this is a security issue. Not to mention, for our purposes, you still would have to deal with reason 1 above.

Instead, we can use PHP's function to retreive HTML code, file_get_contents().

In your wp-content/themes/my_theme/header.php file, you would then include this line:

...
<body>
  <?php $a = file_get_contents("http://myrailsapp.com/static/my_blog_header"); echo $a; ?>
...

Import the Rails CSS Stylesheet for Wordpress

Of course, you'll also want to import the base stylesheet that styles your layout HTML for your Wordpress theme. You can do this a few different ways. The easiest is to just include it in your header.php file in the <head> area like any other stylesheet, just using the absolute URL to the CSS file.

...
<link rel="stylesheet" href="http://myrailsapp.com/stylesheets/style.css" type="text/css" media="screen" />
<link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css" media="screen" />
...

Notice that we include the Rails app stylesheet before the blog stylesheet. This is so that it's easy to override any of the styles we need to specifically in the blog stylesheet.

An alternative way we can do this is using the CSS @import method.

Cache the Layout Partials On Your Rails Server

The astute programmer in you may have noticed by now that this turns a single request on your blog into multiple additional requests on your Rails app server (one additional request per partial that's imported in your Wordpress layout, e.g. header, footer, etc.). We can't have that!

Luckily this is easy to fix with Rails's built-in page-caching.

In your app/controllers/static_controller.rb file, include the following.

class StaticController < ApplicationController
  caches_page :index, :blog_header_partial, :blog_header_footer, :blog_widgets
  ...
end

And make sure that caching is turned on in your production app, in config/environments/production.rb:

...
config.action_controller.perform_caching = true
config.action_view.cache_template_loading = true
...

And/Or Cache Your Wordpress Pages (recommended)

Of course, if you install and setup caching in your Wordpress installation (highly recommended), then the complete layout files will be cached and never even need to make calls to your Rails application after the cached pages are generated the first time. This is even faster for your Wordpress blog, because it'll load faster now that it doesn't have to import portions of the layout from an external URL every time a page is loaded.

Note that when you do this, you'll need to expire/delete the cached Wordpress files anytime you update the layout in your Rails app.

Done!

Now you have a great looking Wordpress blog that looks like you're never leaving your Rails application. And best of all, you don't have to tediously duplicate efforts every time you update your Rails app layout! How's that for DRY design?

About the author:

Steve Schwartz // Owner of Alfa Jango, CTO of Genomenon, co-founder of Carcode (acquired by Edmunds.com in 2014), engineer, developer, open-source enthusiast, guitarist, and racecar driverist.



Comments are loading...