Passing variables to get_template_part

tldr:

I created a simple WordPress plugin that creates a new function very similar to get_template_part(), but which allows you to pass named variables to the template. You can check it out on GitHub here.

Introduction

In case you stumbled across this without having a really good sense of what get_template_part() does in WordPress, check out the developer docs. It’s basically a really nice way to re-use templates or sections of templates, so you don’t have to duplicate code and violate the DRY principle.

As great as get_template_part() is, it does have limitations, which brings us to one of the most common questions I’ve seen come up:

How do I pass variables to get_template_part?

The short answer is,WordPress doesn’t allow this right now. The reason for this is that when you include or require another PHP file, the included file will only have access to the variables in the scope of the calling function. This is best demonstrated by looking at load_template(), the function get_template_part() eventually uses to load the file it finds:

function load_template( $_template_file, $require_once = true ) {
    global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID;
    if ( is_array( $wp_query->query_vars ) ) {
        extract( $wp_query->query_vars, EXTR_SKIP );
    }
    if ( isset( $s ) ) {
        $s = esc_attr( $s );
    }
    if ( $require_once ) {
        require_once( $_template_file );
    } else {
        require( $_template_file );
    }
}

This function is specifically bringing in a bunch of global WordPress variables, such as $posts, $post, $wp_query, etc., in order to make those variables available to the required template file.

It’s also looking at the $wp_query->query_vars array, and using PHP’s extract to bring all of those variables into the scope of load_template() (as long as the value is not empty, thanks to EXTR_SKIP).

Hey, let’s just throw our variables in there then!

Yes, as some people have pointed out, this means you could use set_query_var() to add a variable to $wp_query->query_vars and then have it be extracted before the template is included. In my opinion, this is really gross. Those variables aren’t really query_vars (they aren’t affecting the query itself), and at a glance it probably wouldn’t be clear what the goal of the set_query_var() call would be. Just looking at it, would you know the intention was to pass the variable to a template? Just looking at the template, would it be clear where the variable was coming from?

This could be really confusing for other developers if you’re working on a project with a team, or even for yourself a few months later.

Are there any other alternatives?

Of course! One alternative is to use something like the following, as pointed out by Keith Devon, instead of get_template_part():

include(locate_template('your-template-name.php'));

This will include the template file and it will inherit the current scope’s variables. There is a downside, though. You have to know specifically which template file you’re looking for. One of the great things about get_template_part() is its second parameter.

Here’s an example that really shows off what the function was designed for in the first place:

get_template_part('content', get_post_type());

get_post_type() will return the slug post type name, such as 'post' or 'page' for default WordPress content types.

Let’s say you called this inside a page template, so get_post_type() would return 'page'. This means the function would be equivalent to:

get_template_part('content', 'page');

What this would do is first look for content-page.php in both the child theme, if one is being used, then the parent theme. If that file can’t be found, it would look for content.php in both places, once again checking the child theme first. This allows theme developers to have a reasonable fallback, in this case content.php, but they can also apply special processing to certain content types.

Why would I want to pass variables anyway?

I can imagine many use cases where you would want to pass variables to get_template_part(). Using the example above, what if the only difference between content.php and content-page.php was a single line of code, and everything else was duplicated between the two files? This wouldn’t be very DRY and could be a nightmare to maintain.

Alright, so what’ve you got for me?

Although there are a lot of solutions out there already, I decided to take a stab at making one of my own. I created a very small plugin that creates a new function named jpr_get_template_part_with_vars(). It takes the same arguments as get_template_part(), but it also accepts a third variable, an associative array of $key => $value pairs, that will end up being made available to the loaded template file.

$variables = [
    'someExample' => 'hello',
];

jpr_get_template_part_with_vars('content', 'page', $variables)

// In content-page.php
<div>
    <?php echo $someExample; ?>
</div>

// Produces
<div>
    hello
</div>

One big advantage to this is that you get to decide the variable name. If you set up a template to expect a variable named $someExample, you don’t necessarily have to muddle up the global scope to get it there.
You could even create a wrapper around this function if you find yourself needing to include the same template with the same variable names a lot:

function myprefix_get_author_box(WP_User $user) {
    $variables = [
        'myprefix_author' => $user,
    ];
    jpr_get_template_part_with_vars('author', 'box', $variables);
}

Using a wrapper function like this, as well as type hinting, allows you to be confident that the correct variables will always come through.

Where can I check this out?

If this sounds like something you might find useful, please take a look at the code on the GitHub page. I really do hope that other people find this as useful as I’ve found it, and would welcome any feedback or requests for additional functionality.

Leave a Comment