Recently, I was having a conversation with a fellow developer via email about maintaining a separation of concerns in custom WordPress queries and WordPress templates.

The gist of the conversation boiled down to this:

Is it a good idea to keep custom queries in the template files?

A couple of years ago, I would’ve said yes but as I’ve begun to work on more complex projects, I’ve changed my mind: I’m actually a fan of keeping custom queries in functions.php.

I think that this keeps code more maintainable, cleaner, and easier to read, though I’m not sure if this is the most common practice.

Custom WordPress Queries in Templates

In the simplest case, including a custom WordPress query is easy enough. For example, say that for a given template you want to retrieve four featured posts (based on specific post meta data).

The template and the custom query will likely look something like this:

<?php
$args = array(
	'post_type'		    =>	'project',
	'posts_per_page'	=>	4,
	'meta_query'		  =>	array(
								array(
									'key'	  =>	'featured_project',
									'value'	=>	'1'
								)
							)
);
$projects = new WP_Query( $args );
?>
<?php if( $projects->have_posts() ) { ?>
	<hr />
	<div id="featured-projects">

		<h3>Featured Projects</h3>
		<div class="row">
		<?php while( $projects->have_posts() ) { ?>
			<?php $projects->the_post(); ?>

				<div class="span3 featured-project">

					<h5><?php the_title(); ?></h5>

					<p class="featured-project-thumb">
						<?php the_post_thumbnail(); ?>
					</p><!-- /.featured-project-thumb -->

					<div class="entry-content">
						<?php the_excerpt( __( 'Read More...', 'lb' ) ); ?>
					</div><!-- /.entry-content -->

				</div><!-- /.featured-project -->

		<?php } // end while ?>
		<?php wp_reset_postdata(); ?>
		</div><!-- /.row -->

	</div><!-- /#featured-projects -->
<?php } // end if ?>

In this case, it’s a single query and it’s not referenced anywhere else, so although this really increases the mix of PHP and HTML in the given template, its not necessarily a maintenance problem and its easy enough to read.

But if you find yourself increasing the number of custom queries in a template or you’re referencing the query in multiple places, it’s time to separate them – or, to speak proper programmerese, de-couple them – from the templates.

Separating Custom WordPress Queries

Here’s a second example from a project that I just finished. The given template is responsible for retrieving:

  • The most recent 30 posts each of which are a custom post type
  • A single featured post as designated by post meta data set in the dashboard
  • 10 posts from featured users each of which are designated by meta data set in the dashboard and that span across multiple roles

This is clearly a more complex example as the template is executing three custom WordPress queries each of which can also be used in other parts of the application.

In this case, I think it’s a prime example of when custom queries should be abstracted into functions.php and called by associated templates.

Reading three custom queries and their refactoring? Ain’t no body got time for that. As such, I’m going to show a single query prior to refactoring, and then a query after refactoring.

In the list of queries above, the most complicated is retrieving 10 posts from featured users who span across multiple roles, so I’ll be using that as my example.

Before

// get the featured editors
$editor_query = new WP_User_Query(
	array(
		'role'			    =>	'editor',
		'meta_key' 		  => 'featured',
		'meta_value'	  =>	'yes',
		'meta_compare'	=>	'=',
		'number'		    =>	5
	)
);
$editors = $editor_query->get_results();

// get the featured admins
$administrator_query = new WP_User_Query(
	array(
		'role'			    =>	'administrator',
		'meta_key' 		  => 'featured',
		'meta_value'	  =>	'yes',
		'meta_compare'	=>	'=',
		'number'		    =>	5
	)
);
$admins = $administrator_query->get_results();

// store them all as users
$users = array_merge( $admins, $editors );

// Store the author ID's in a CSV removing the last comma
$featured_user_ids = '';
foreach( $users as $user ) {
	$featured_user_ids .= $user->ID . ',';
} // end foreach
$featured_user_ids = substr( $featured_user_ids, 0, strlen( $featured_user_ids ) - 1 );

// Query for the 10 most recent posts by the specified users
$args = array(
	'post_type' 		  => 'post',
	'posts_per_page' 	=> 10,
	'author'			    => $featured_user_ids
);
$the_query = new WP_Query( $args );

// Finally, loop through the posts
while ( $the_query->have_posts() ) {
	$the_query->the_post();
	/* Snipped for brevity, but this is where the markup would render post data. */
} // end while
wp_reset_postdata();

It’s relatively easy to read, but it’s incredibly long. This makes makes working with the given template a bit of a hassel because you have to wade through this information prior to actually marking up the data.

By abstracting this into its own method, you can greatly simplify the foreach loop and, consequently, the template that renders this data.

After

First, you’d setup a function in functions.php specifically for returning the query. Obviously this will look exactly the same as above – it’s simply encapsulating the calls to the WordPress API:

function example_get_featured_user_posts() {

	// get the featured editors
	$editor_query = new WP_User_Query(
		array(
			'role'			    =>	'editor',
			'meta_key' 		  => 'featured',
			'meta_value'	  =>	'yes',
			'meta_compare'	=>	'=',
			'number'		    =>	5
		)
	);
	$editors = $editor_query->get_results();

	// get the featured admins
	$administrator_query = new WP_User_Query(
		array(
			'role'			     =>	'administrator',
			'meta_key' 		   => 	'featured',
			'meta_value'	   =>	'yes',
			'meta_compare'	 =>	'=',
			'number'		     =>	5
		)
	);
	$admins = $administrator_query->get_results();

	// store them all as users
	$users = array_merge( $admins, $editors );

	// Store the author ID's in a CSV removing the last comma
	$featured_user_ids = '';
	foreach( $users as $user ) {
		$featured_user_ids .= $user->ID . ',';
	} // end foreach
	$featured_user_ids = substr( $featured_user_ids, 0, strlen( $featured_user_ids ) - 1 );

	// Query for the 10 most recent posts by the specified users
	$args = array(
		'post_type' 		  => 'post',
		'posts_per_page' 	=> 10,
		'author'		      => $featured_user_ids
	);
	$the_query = new WP_Query( $args );

	return $the_query;

} // end example_get_featured_user_posts

Next, you’d then make a call to this function in the foreach to render the data:

$users_query = example_get_featured_user_posts();
while( $users_query->have_posts() ) {
	$users_query->the_post();
	/* Snipped for brevity, but this is where the markup would render post data. */
} // end while
wp_reset_postdata();

Technically, I think you can make further improvements by breaking the query functionality into multiple functions, but that’s a whole other post.

Better, But Best Practice?

When it comes to custom WordPress queries, I almost always keep them in functions.php and then call them from templates even if I rarely call them. I find that it generally makes for strong separation of concerns and keeps the template code cleaner.

At the very least, I think that this should be done whenever a custom WordPress query is called multiple times throughout a theme, plugin, or application, or if a custom query is relatively complicated.

The thing is, I’m not certain that this is considered a best practice or not. I’ve found it to be useful in my efforts, but I’m curious as to where the rest of you stand.

Category:
Articles
Tags:

Join the conversation! 26 Comments

  1. Fantastic post Tom!
    And I think you know where I stand. ;)

  2. I’ve been doing this as well. In my job, I could easily be inside of 100 different WordPress projects in a year (not all dev, but maintenance as well). When I’m creating a new site, my tendency for a long time was to put my queries in the templates they are being displayed from, but over time it’s changed.

    From a maintenance perspective, it’s much easier to find the right code when much of the “meat and potatoes” code for display purposes is in a file like functions.php (note I specify display). Then in the code comments, I can portray what it’s for, and also what files it may be used in. Likewise, if I run across a function name in the theme files, theoretically the comments can point to the functions file.

    The biggest reason this has been a help is because though I may start a project thinking I only need that custom query (or any functionality) once, of course I end up using it multiple times, and organizing it from a central location from the start makes it easier for me to be DRY. And if I break the functions into small enough bits, I can reuse parts across multiple areas in a project, and I’m much more likely to do that in an organized manner when it’s all in the functions file.

    • I think it’s definitely a learned behavior even if it’s not done specifically in WordPress.

      By that, I mean I think that programmers who are coming from other backgrounds learned the whole DRY principle or the benefits of keeping a logical separation of concerns earlier and bring that with them to WordPress-based work.

      On the other hand, other developers – much like you – learn it while working with WordPress.

      At any rate, all of your points are spot on and exactly why it’s important to keep the functions and the templates separated.

  3. I see cases for both approaches. I usually run simpler queries right in the template files and only abstract the code into a plugin/functions.php if additional logic or caching is required.

    Also, depending on the project, instead of abstracting redundant code to a plugin/functions.php, I’ll abstract them to template parts. This way I can test new functionality and call modified versions of the template parts as needed.

    • Yeah – there’s always a bit of gray area in this.

      Obviously, I take the stance that code should be separated more often than not, but if you’re working on a smaller project or a one-off that’s only going to be built once, I think it’s more acceptable to leave the query in the single template.

      It’s when the project begins to grow that things get a bit more difficult to maintain.

      And on template parts, I’m with you – I use them a lot, but I generally see them as ways to keep markup separated. I often think of template parts as the functions of markup – you write them once and then can call them from multiple places.

  4. I think this will vary from project to project.

    For a simple project with only one custom query, you should be fine just doing this in the template. However, even the simplest of projects have a tendency to eventually become more complex.

    With a site that is more complex, I would certainly separate them out. And even on smaller projects, if I have an idea going in that we may add more to the site later and need more custom queries, I would go ahead a break them apart.

    In terms of where I would place the custom query code, I do like the functions.php approach; however, I prefer a cleaner functions.php file, so I normally break out all my major functions to separate files, place them in a lib folder and simply include the file from my functions.php file. This way I can quickly locate and modify the specific functions as needed.

    If it would be of any help, here is a write-up on this approach at http://wp.me/p2TlaM-91

    Ed

    • For a simple project with only one custom query, you should be fine just doing this in the template. However, even the simplest of projects have a tendency to eventually become more complex.

      Exactly. I think that the more time developers spend on projects, the more they have a sense on where the project is going to actually head so they’re able to anticipate it. In that case, it can often just save time to keep it abstracted in its own function.

      Anyway, you’re not the first developer who has suggested keeping a lean functions.php, either. This is something that I’m still experimenting with – for now, I generally have sections of the file that I list in a “table of contents” of sorts so that I can easily find the code during development.

      The thing is, a few projects have resulted in functions.php being over 3000 lines long so, yeah, it would stand to be separated a bit more.

      Good stuff, Ed. Thanks for the food for thought.

  5. I think it’s a case-by-case basis. Often times, I’ll include a single custom_query function that will take variables (i.e. count, meta keys, etc) and call that numerous times then parse it out as needed.

    • So you basically have a wrapped functions for the query functions? I can get behind that. The only thing that would seem to be a little challenging would be if the arrays you’re having to pass as arguments get really long.

      Though I understand the architectural reasons that WordPress has so many functions accept arrays, they take up a lot of “visual space” when setting them up, you know?

      Either way, I dig this solution, too.

  6. It may be unrealistic to expect WordPress and the developers in the community to ever fully commit to MVC, in theory or in practice. But certainly, I should hope we can mostly agree that it’s a wise thing to separate logic and presentation – certainly this is among the higher-level benefits of the MVC approach.

    I’m no purist on the matter, but I fail to see any major benefit from leaving custom queries in the template, nor do I see it as any major hindrance to separate the logic into a functions.php file.

    Obviously, the benefits to doing that are magnified when you’re able to abstract code into reusable bits, but I would argue that it’s a best practice even in the absence of foreseen reuse.

    • The big thing that gets me when I look at themes with query logic all over is that it makes them way harder to read. Same goes when I don’t see custom template tags (that do more than WordPress built in ones). The theme is harder to work with because everything is in the theme templates.

    • Yeah – I don’t expect WordPress developers to commit to MVC partially because that’s not the way WordPress itself works. I talked a little bit about it in this post, but the principles of good architecture still persists The usual DRY and KISS, then Bob Martin’s SOLID principles all of which are things I try hard to keep in mind in my work.

  7. Totally agree! I like to keep as much functional code as possible outside of my templates, either in functions.php or in a core functionality plugin, leaving the templates mostly about display.

    Related, but a little different is my announce how often I still come across templates using query_posts for replacing the main query which should be done in functions and hooked to pre_get_posts…

    • Yeah – you’re right. Functional stuff should be relegated to functional files :).

      And to your point regarding query_posts, that’s actually been replaced by WP_Query which is now the preferred way to go about doing many more of the tasks originally done by the original API call.

      Definitely check it out when you get a chance.

      • Just to clarify… _I_ would never use query_posts, but I still come across it being used by others ALL the time… drives me nuts… even more so when they forget wp_reset_query().

        • Note! I misread your last comment :).

          But yeah, I can be relatively forgiving about using query_posts based on how old the code is, but there are some mistakes – like forgetting wp_reset_query() that tend to me more clues of lack of understanding of what’s actually going on (and required) in development.

  8. I’m doing things a little different.
    I started moving everything to plugins to allow for clean upgrades or redesigns.
    Then creating short codes that can be moved form area to area and always work, This also keeps the Theme layer much cleaner too.

    Thanks for the great post ;c )

    • I’m actually a big fan of this strategy, but it largely depends on the nature of the project or the requirements for how I handle it.

      It definitely definitely keeps the theme layer cleaner, but I do think it adds a new layer or a new vertical (depending on how you envision it) of complexity.

      Regardless, though, it does make upgrades much easier and it keeps the code much more compartmentalized and cohesive.

  9. This is something I’m going to start doing from now on. I’ve tidied up almost every other aspect of my WP child theme development, but moving custom queries into a separate file is a good idea.

Leave a Reply