51

The WP Codex says to do this:

// You wish to make $my_var available to the template part at `content-part.php`
set_query_var( 'my_var', $my_var );
get_template_part( 'content', 'part' );

But how do I echo $my_var inside the template part? get_query_var($my_var) does not work for me.

I've seen tons of recommendations for using locate_template instead. Is that the best way to go?

  • Had about the same question and got it to work with set_query_var and get_query_var, however this was for using the values of an $args array that is passed to a WP_Query. Might be helpful for other people starting to learn this. – lowtechsun Jun 14 '17 at 23:59
49

As posts get their data set up via the_post() (respectively via setup_postdata()) and are therefore accessible through the API (get_the_ID() for e.g.), let's assume that we are looping through a set of users (as setup_userdata() fills the global variables of the currently logged in user and isn't useful for this task) and try to display meta data per user:

<?php
get_header();

// etc.

// In the main template file
$users = new \WP_User_Query( [ ... ] );

foreach ( $users as $user )
{
    set_query_var( 'user_id', absint( $user->ID ) );
    get_template_part( 'template-parts/user', 'contact_methods' );
}

Then, in our wpse-theme/template-parts/user-contact_methods.php file, we need to access the users ID:

<?php
/** @var int $user_id */
$some_meta = get_the_author_meta( 'some_meta', $user_id );
var_dump( $some_meta );

That's it.

The explanation is actually exactly above the part you quoted in your question:

However, load_template(), which is called indirectly by get_template_part() extracts all of the WP_Query query variables, into the scope of the loaded template.

The native PHP extract() function "extracts" the variables (the global $wp_query->query_vars property) and puts every part into its own variable which has exactly the same name as the key. In other words:

set_query_var( 'foo', 'bar' );

$GLOBALS['wp_query'] (object)
    -> query_vars (array)
        foo => bar (string 3)

extract( $wp_query->query_vars );

var_dump( $foo );
// Result:
(string 3) 'bar'
  • 1
    still working great – huraji Jun 11 at 13:43
23

The hm_get_template_part function by humanmade is extremely good at this and I use it all the time.

You call

hm_get_template_part( 'template_path', [ 'option' => 'value' ] );

and then inside your template, you use

$template_args['option'];

to return the value. It does caching and everything, though you can take that out if you like.

You can even return the rendered template as a string by passing 'return' => true into the key/value array.

/**
 * Like get_template_part() put lets you pass args to the template file
 * Args are available in the tempalte as $template_args array
 * @param string filepart
 * @param mixed wp_args style argument list
 */
function hm_get_template_part( $file, $template_args = array(), $cache_args = array() ) {
    $template_args = wp_parse_args( $template_args );
    $cache_args = wp_parse_args( $cache_args );
    if ( $cache_args ) {
        foreach ( $template_args as $key => $value ) {
            if ( is_scalar( $value ) || is_array( $value ) ) {
                $cache_args[$key] = $value;
            } else if ( is_object( $value ) && method_exists( $value, 'get_id' ) ) {
                $cache_args[$key] = call_user_method( 'get_id', $value );
            }
        }
        if ( ( $cache = wp_cache_get( $file, serialize( $cache_args ) ) ) !== false ) {
            if ( ! empty( $template_args['return'] ) )
                return $cache;
            echo $cache;
            return;
        }
    }
    $file_handle = $file;
    do_action( 'start_operation', 'hm_template_part::' . $file_handle );
    if ( file_exists( get_stylesheet_directory() . '/' . $file . '.php' ) )
        $file = get_stylesheet_directory() . '/' . $file . '.php';
    elseif ( file_exists( get_template_directory() . '/' . $file . '.php' ) )
        $file = get_template_directory() . '/' . $file . '.php';
    ob_start();
    $return = require( $file );
    $data = ob_get_clean();
    do_action( 'end_operation', 'hm_template_part::' . $file_handle );
    if ( $cache_args ) {
        wp_cache_set( $file, $data, serialize( $cache_args ), 3600 );
    }
    if ( ! empty( $template_args['return'] ) )
        if ( $return === false )
            return false;
        else
            return $data;
    echo $data;
}
11

I was looking around and have found a variety of answers. Its seems at a native level, Wordpress does allow for variables to be accessed in Template parts. I did find that using the include coupled with locate_template did allow for variables scope to be accessible in the file.

include(locate_template('your-template-name.php'));
3
// you can use any value including objects.

set_query_var( 'var_name_to_be_used_later', 'Value to be retrieved later' );
//Basically set_query_var uses PHP extract() function  to do the magic.


then later in the template.
var_dump($var_name_to_be_used_later);
//will print "Value to be retrieved later"

I recommend to read about PHP Extract() function.

2

I ran into this same issue on a project I'm currently working on. I decided to create my own small plugin that allows you to more explicitly pass variables to get_template_part by using a new function.

In case you might find it useful, here's the page for it on GitHub: https://github.com/JolekPress/Get-Template-Part-With-Variables

And here's an example of how it would work:

$variables = [
    'name' => 'John',
    'class' => 'featuredAuthor',
];

jpr_get_template_part_with_vars('author', 'info', $variables);


// In author-info.php:
echo "
<div class='$class'>
    <span>$name</span>
</div>
";

// Would output:
<div class='featuredAuthor'>
    <span>John</span>
</div>
1

I like the Pods plugin and their pods_view function. It works similar to the hm_get_template_part function mentioned in djb's answer. I use an additional function (findTemplate in the code below) to search for a template file in the current theme first, and if not found it returns the template with the same name in my plugin's /templates folder. This is a rough idea of how I'm using pods_view in my plugin:

/**
 * Helper function to find a template
 */
function findTemplate($filename) {
  // Look first in the theme folder
  $template = locate_template($filename);
  if (!$template) {
    // Otherwise, use the file in our plugin's /templates folder
    $template = dirname(__FILE__) . '/templates/' . $filename;
  }
  return $template;
}

// Output the template 'template-name.php' from either the theme
// folder *or* our plugin's '/template' folder, passing two local
// variables to be available in the template file
pods_view(
  findTemplate('template-name.php'),
  array(
    'passed_variable' => $variable_to_pass,
    'another_variable' => $another_variable,
  )
);

pods_view also supports caching, but I didn't need that for my purposes. More information about the function arguments can be found in the Pods documentation pages. See the pages for pods_view and Partial Page Caching and Smart Template Parts with Pods.

1

Based on the answer from @djb using code from humanmade.

This is a lightweight version of get_template_part that can accept args. This way variables are scoped locally to that template. No need to have global, get_query_var, set_query_var.

/**
 * Like get_template_part() but lets you pass args to the template file
 * Args are available in the template as $args array.
 * Args can be passed in as url parameters, e.g 'key1=value1&key2=value2'.
 * Args can be passed in as an array, e.g. ['key1' => 'value1', 'key2' => 'value2']
 * Filepath is available in the template as $file string.
 * @param string      $slug The slug name for the generic template.
 * @param string|null $name The name of the specialized template.
 * @param array       $args The arguments passed to the template
 */

function _get_template_part( $slug, $name = null, $args = array() ) {
    if ( isset( $name ) && $name !== 'none' ) $slug = "{$slug}-{$name}.php";
    else $slug = "{$slug}.php";
    $dir = get_template_directory();
    $file = "{$dir}/{$slug}";

    ob_start();
    $args = wp_parse_args( $args );
    $slug = $dir = $name = null;
    require( $file );
    echo ob_get_clean();
}

For example in cart.php :

<? php _get_template_part( 'components/items/apple', null, ['color' => 'red']); ?>

In apple.php :

<p>The apple color is: <?php echo $args['color']; ?></p>
0

How about this?

render( 'template-parts/header/header', 'desktop', 
    array( 'user_id' => 555, 'struct' => array( 'test' => array( 1,2 ) ) )
);
function render ( $slug, $name, $arguments ) {

    if ( $arguments ) {
        foreach ( $arguments as $key => $value ) {
                ${$key} = $value;
        }
    }

$name = (string) $name;
if ( '' !== $name ) {
    $templates = "{$slug}-{$name}.php";
    } else {
        $templates = "{$slug}.php";
    }

    $path = get_template_directory() . '/' . $templates;
    if ( file_exists( $path ) ) {
        ob_start();
        require( $path);
        ob_get_clean();
    }
}

By using ${$key} you can add the variables into the current function scope. Works for me, quick and easy and its not leaking or stored into the global scope.

-2

This is exact solution and it worked well. https://developer.wordpress.org/reference/functions/set_query_var/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.