Pregunta

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?

¿Fue útil?

Solución

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.

Update (WP >= v5.5)

As pointed out in the comments, current versions of WP offer a third parameter for get_template_part(): array $args. So from this version on, you do not need to use set_query_var( 'foo', 'bar' ) anymore. Example:

<?php
get_header();

// etc.

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

foreach ( $users as $user )
{
    $args = (array) $user;
    get_template_part( 'template-parts/user', 'contact_methods', $args );
}

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

<?php
/** @var array $args */
$some_meta = get_the_author_meta( 'some_meta', $args['ID'] );
var_dump( $some_meta );

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'

Otros consejos

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;
}

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'));
// 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.

Starting in 5.5, it will be possible to pass data to templates via the various core template-loading functions.

All of the WordPress template-loading functions will support an additional parameter of $args, which allows theme authors to pass along an associative array of data to the loaded template. The functions that support this new parameter are:

get_header()
get_footer()
get_sidebar()
get_template_part()
locate_template()
load_template()

Any hooks associated with the functions also pass along the data.

For more information: https://make.wordpress.org/core/2020/07/17/passing-arguments-to-template-files-in-wordpress-5-5/

Update

As selrond correctly answered as of Wordpress 5.5 get_template_part() (see changelog) now accepts a third parameter array $args = array(), which will be available in your template file as $args.

See this example:

$bar = 'bar';

// get helper-my-template.php
get_template_part(
    'template-parts/helper',
    'my-template',
    array(
        'foo' => $bar, // passing this array possible since WP 5.5
    )
);

In your template file

e.g. helper-my-template.php you can now access your variable like this:

<?php

/**
 * @var array $args
 */

$foo = $args['foo'];

?>

<h1><?php echo $foo; ?></h1>

<?php // will print 'bar' ?>

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>

The $args parameter for template loading functions has just landed in WordPress 5.5 “Eckstine”:

Passing data to template files

The template loading functions (get_header(), get_template_part(), etc.) have a new $args argument. So now you can pass an entire array’s worth of data to those templates.

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.

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>

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.

For ones who looks very easy way to pass variables, you can change function to include:

include( locate_template( 'YourTemplate.php', false, false ) );

And then you will be able to use all variables which are defined before you are including template without PASSING additionally each one for the template.

Credits goes to: https://mekshq.com/passing-variables-via-get_template_part-wordpress/

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

Licenciado bajo: CC-BY-SA con atribución
scroll top