Child themes are the easiest way to style WordPress sites. Rather than create a site from scratch, you can create a theme that shares most of its code and styling with a parent theme. The benefits of creating a child theme instead of editing the main theme is that if the main theme gets updates, none of your changes are lost.
But what if you need to modify a child theme? If you’re working with a Framework, like Genesis, it’s very likely you are already using a child theme – and editing that brings about all the disadvantages of editing a parent theme – you lose your changes if the theme is ever updated.
The solution is surprisingly simple. Instead of editing the child theme, create a grandchild theme. It’s very similar to creating a child theme, except you do it via a plugin. You add your custom functions to the plugin, just as you normally would in functions.php (though remember your plugin will be called much earlier than functions.php, so you’ll need to make sure that any code in your plugin only runs when an action is fired). Use the wp_register_style and wp_enqueue_style functions to add your CSS (by default a grandchild theme will add your CSS to the CSS of the parent or child theme, but you can change this behaviour by using the wp_dequeue_style function). Finally filter the result of the get_query_template function to control which PHP files are executed when a page is requested.
A typical grandchild theme might look like this:
/*
Plugin Name: Grandchild Theme
Plugin URI: http://www.wp-code.com/
Description: A WordPress Grandchild Theme (as a plugin)
Author: Mark Barnes
Version: 0.1
Author URI: http://www.wp-code.com/
*/
// These two lines ensure that your CSS is loaded alongside the parent or child theme's CSS
add_action('wp_head', 'wpc_theme_add_headers', 0);
add_action('init', 'wpc_theme_add_css');
// This filter replaces a complete file from the parent theme or child theme with your file (in this case the archive page).
// Whenever the archive is requested, it will use YOUR archive.php instead of that of the parent or child theme.
add_filter ('archive_template', create_function ('', 'return plugin_dir_path(__FILE__)."archive.php";'));
function wpc_theme_add_headers () {
wp_enqueue_style('grandchild_style');
}
function wpc_theme_add_css() {
$timestamp = @filemtime(plugin_dir_path(__FILE__).'/style.css');
wp_register_style ('grandchild_style', plugins_url('style.css', __FILE__).'', array(), $timestamp);
}
// In the rest of your plugin, add your normal actions and filters, just as you would in functions.php in a child theme.
The two actions to add the CSS should be familiar to you (although you may also want to read this post). The filter is the important function. You can create as many of these as you wish, for different pages. Supported pages include:
- index
- 404
- archive
- author
- category
- tag
- taxonomy
- date
- home
- front_page
- page
- paged
- search
- single
- attachment
If you want more control before deciding which template file to give to WordPress, create a proper function and insert your logic code into it:
add_filter ('archive_template', 'emw_archive_template', 10, 1);
function wpc_archive_template ($suggested_templates) {
// Your logic here
if ($test_passed)
return $template_file;
else
return $alternative_template_file;
}
Mark, your author URI is incorrect
Thanks, Ramon. It’s fixed now.
In the last code snippet shouldn’t the callback function be wpc_archive_template in the add_filter call?
No, it’s correct. The callback function is supplied by the ‘function’ function, if that makes sense.
I think the filter name is actually “frontpage_template”, rather than “front_page_template”.
Also. there are other types of templates that need to be overridden too, like header/footer/sidebar, loop templates included with get_template_part(), the comments template, the search form, etc. Some of them have their own filters (like “get_search_form” — which expects a string of HTML, not a filename), but so far I haven’t found filters for some of the others.
No, I’m sure it’s “front_page”. You can see the templates listed in wp-includes/template.php
get_query_template() strips out the _ with preg_replace() — line 24 of template.php.
And here we see the problem with theme frameworks like Genesis. WordPress doesn’t do proper grandchild themes (nor should it) so we hack presentation in to a plugin.
Themes are for presentation. Plugins are for extending function.
I agree that theme shops shouldn’t be selling child themes, and that there are good reasons for WP to not support grandchild themes natively, and that presentation doesn’t belong in plugins, but I also think it’s important to not be overzealous about these kinds of things.
Sometimes you have to work with what you have, even if it’s not an ideal situation; and sometimes what you have is a client whose site was built on a parent/child theme combo like Genesis, and using a technique like this is the least-bad way to maintain the site until it can be rebuilt properly.
No. Build it properly in the first place. If you are using a child theme for a system like Genesis, then you should fork it rather than try to hack your presentation into a plugin. Throw the current version of the child theme into Git or SVN, then create a dev branch to contain your customizations. If (and this is a big “if” since most won’t) the child theme is update, you can create a diff between the original and the new version using your VCS and apply the diff to your branch.
Is it more work? Yes. Is it an ugly hack using a plugin? No. Work with what you have, but don’t advocate a system like this that introduces a third level of moving parts into the theme’s presentation.
Actually, that is a good idea. Normally I prefer extending to forking, but in this case I think you’re right. Using a plugin adds more complexity and points of failure, so the benefits of avoiding a fork are outweighed.
There may still be other use cases where the plugin technique is appropriate, but for this kind of situation I agree with you.
Can you check your spam? I left a long comment on here a week or two ago and it still hasn’t shown up