More than once when I look at WordPress themes I see a lot of disorganised code. This varies from functions.php -files with hundreds lines of code that are a mixture user-specified functions and WordPress-specific functions (add_filter() , add_action() , etc). Also the templates files themselves are more often a complete mixture of HTML and massive blocks of PHP-code. Even the default theme that ships with WordPress is setup like this so you could say that WordPress encourages to create your themes in this manner. In this article I’m going to share with you a different approach on how to setup your WordPress theme in a clean way.
Concatenate your template in a class
To prevent putting a lot of disorganised code in your functions.php, consider putting your theme-specific functions inside a separate class. This class can contain functions for initialisation, as well as taking care of some theme-specific hooks. Lets put this file in inc/MyTheme.php :
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyTheme { public function __construct() { // Register menus: register_nav_menu('primary', 'Primary Menu'); register_nav_menu('footer', 'Footer Menu'); } } // Autoload this class on inclusion: new MyTheme(); |
Now in our functions.php , we include this class:
1 |
require_once('inc/MyTheme.php'); |
Now our functions.php -file is nice and tidy, and our theme-logic can be put in it’s own file.
Use separate classes for different purposes.
At this point, you might be wondering: ‘why is this better than just placing everything in functions.php?’. Let me show you just how we are now about to organise our code by using separate classes for different purposes. Let’s say that we want to add some extra functionality to our pages. For example, add thumbnail / featured image support to pages. You might be tempted to add this functionality to your functions.php -file. But let us instead create a new class in inc/MyTheme_Page.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MyTheme_Page { /** * Constructor */ public function __construct() { // Add support for featured images: add_theme_support('post-thumbnails', array('post', 'page')); } } new MyTheme_Page(); |
See how we separated the adjustment for pages from our theme functionality? Now this is a very basic example, but can you imagine when you want to add more functionality to pages? Like adding extra metaboxes or actions? This way you can concatenate all the logic you do for pages in one single file. For example, when we want to add some extra metaboxes, we could do something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class MyTheme_Page { /** * Constructor */ public function __construct() { // Add support for featured images: add_theme_support('post-thumbnails', array('post', 'page')); // Add actions: add_action('add_meta_boxes', array($this, 'addMetaBoxes')); add_action('save_post', array($this, 'savePost')); } /** * Function to add meta boxes */ public function addMetaBoxes() { // logic to add meta boxes } /** * Save Post * * @param int $postId */ public function savePost($postId) { // logic when saving a post } } new MyTheme_Page(); |
The same logic can be applied to other post types or your own custom post types. Also note how we can specify array($this, ‘functionName’) to set a function inside this class as a callback. This way we can use generic names for functions that are used in multiple post types so they are setup in a generic way.
Use static methods in templates
WordPress templates can get bloated very quickly. This is because they are a complete mixture of HTML and PHP where PHP tends to get the upper hand quickly. Take this piece of template for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<article> <p class="category"><?php $categories = get_the_category(); if(count($categories) > 0) { echo $categories[0]->name; } ?></p> <h1><?php the_title(); ?></h1> <h3><?php $more = false; the_content(false); $more = true; ?></h3> <p class="date"><?php the_date(); ?></p> <?php $content = get_the_content(null, true); $content = apply_filters( 'the_content', $content ); $content = str_replace( ']]>', ']]>', $content ); // Remove the empty paragraph with the <span id="more-.."></span>-tag: if($removeMoreTag) { $content = preg_replace('/<p><span id="more-\d+"><\/span><\/p>/m', '', $content); } echo $content; ?> <?php if ( comments_open() || get_comments_number() ) { comments_template(); } ?> </article> |
This template is a perfect example of how PHP can take the upper hand in this template. I mean, almost 90% of this piece of code is PHP! Now, let’s separate the PHP logic into our template class. Then our template class is going to look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
class MyTheme { public function __construct() { // Register menus: register_nav_menu('primary', 'Primary Menu'); register_nav_menu('footer', 'Footer Menu'); } /** * Get the name of the first category * * @return string */ public static function getCategoryName() { $categories = get_the_category(); if(count($categories) > 0) { return $categories[0]->name; } return ''; } /** * Echo the content before the <!--more--> tag */ public static function getContentBeforeMore() { global $more; $more = false; the_content(false); $more = true; } /** * Echo the content after the <!--more--> tag */ public static function getContentAfterMore($removeMoreTag = true) { $content = get_the_content(null, true); $content = apply_filters( 'the_content', $content ); $content = str_replace( ']]>', ']]>', $content ); // Remove the empty paragraph with the <span id="more-.."></span>-tag: if($removeMoreTag) { $content = preg_replace('/<p><span id="more-\d+"><\/span><\/p>/m', '', $content); } echo $content; } /** * Output the comments template */ public static function getComments() { if ( comments_open() || get_comments_number() ) { comments_template(); } } } // Autoload this class on inclusion: new MyTheme(); |
Now our template looks something like this:
1 2 3 4 5 6 7 8 |
<article> <p class="type"><?php echo MyTheme::getCategoryName(); ?></p> <h1><?php the_title(); ?></h1> <h3><?php MyTheme::getContentBeforeMore(); ?></h3> <p class="date"><?php the_date(); ?></p> <?php MyTheme::getContentAfterMore(); ?> <?php MyTheme::getComments(); ?> </article> |
Now, from a template point of view this looks far more lean and clean. You keep touch of your HTML-structure and PHP-logic is separated in the therefore destined class. Working in this way also makes it more easier to split tasks to separate developers: one developer can work on the templates, while the other one makes sure the theme-specific functions work. So that’s all there’s to it. I hope this article helps you in writing leaner, cleaner and better manageable templates. Happy templating!
Visitors give this article an average rating of 3.0 out of 5.
How would you rate this article?
★ ★ ★ ★ ★
Great tips, Giel. Only wish more people would do anything remotely close to this. The thing I was talking about last friday was: Timber. Which looks very interesting too. Creates an even larger gap between logic and templates.
Looks good. Although I totally agree with you that WordPress’ way of ‘templating’ is ugly and inconvenient as hell, I’m also wondering what cans of worms solutions like Timber are going to cause with 3rd party plugins, or integration with plugins-that-are-more-than-plugins like WooCommerce or BuddyPress. Do you have any experience with that?