WordPress get_template_part pass variable - wordpress

Is there a way of passing a variable to get_template_part() in wordpress:
<?php get_template_part( 'element-templates/front', 'top' ); ?>
<?php get_template_part( 'element-templates/front', 'main' ); ?>
In the front-top.php and front-main.php (which the above is calling) I need to access numeric variables (a different one for each section). Is there a way of passing a variable to each of the calls above?
Thank you

Using WordPress 5.5+
As of WordPress 5.5, passing variables via get_template_part is part of core.
Starting in WordPress 5.5, the template loading functions will now
allow additional arguments to be passed through to the matched
template file using a new $args parameter.
get_template_part( string $slug, string $name = null, array $args = null )
Example:
get_template_part( 'template-parts/featured-image', null,
array(
'class' => 'featured-home',
'data' => array(
'size' => 'large',
'is-active' => true,
)
)
);
and then in the included file (i.e. template-parts/featured-image.php), you can either just display the variables (as per above example):
if ( $args['class'] ) {
echo $args['class'];
}
or
echo $args['data']['size'];
Alternatively, setup defaults first, using wp_parse_args:
// Setup defaults
$array_defaults = array(
'class' => 'featured',
'data' => array(
'size' => 'medium',
'is-active' => false,
)
);
$args = wp_parse_args($args, $array_defaults );
<div class="widget <?php echo esc_html( $args['class'] ); ?>">
<?php echo esc_html( $args['data']['size'] ); ?>
</div>
To be backwards compatible in your theme, you should probably also check the current WordPress version.
Using set_query_vars
The original answer to this question was to use set_query_var
In your theme:
<?php
set_query_var( 'my_var_name', 'my_var_value' );
get_template_part( 'template-parts/contact' );
?>
In the template part:
<?php
$newValue = get_query_var( 'my_var_name' );
if ( $newValue ) {
// do something
}
?>

The core get_template_part() function doesn't support the passing of variables. It only accepts two parameters, slug and name. While there is no built-in solution to this problem, the best approach is to create a function that closely mimics get_template_part() to handle it.
Normally I would create a function that just takes the name of the template file and the variables I want to pass in as an array. In your example however, you're using both arguments for get_template_part() already which means you'll need a slightly more complex function. I'm going to post both versions below.
Simplified Version - Name (slug) and Data
function wpse_get_partial($template_name, $data = []) {
$template = locate_template($template_name . '.php', false);
if (!$template) {
return;
}
if ($data) {
extract($data);
}
include($template);
}
Usage example: wpse_get_partial('header-promotion', ['message' => 'Example message']);
This would load up a file named header-promotion.php with $message available inside of it. Since the second parameter is an array, you can pass in as many variables as you need.
Copy of get_template_part - adding a third parameter
If you don't need both $slug and $name when you call get_template_part(), you probably want the simplified version. For those that do, here's the more complex option.
function wpse_get_template_part($slug, $name = null, $data = []) {
// here we're copying more of what get_template_part is doing.
$templates = [];
$name = (string) $name;
if ('' !== $name) {
$templates[] = "{$slug}-{$name}.php";
}
$templates[] = "{$slug}.php";
$template = locate_template($templates, false);
if (!$template) {
return;
}
if ($data) {
extract($data);
}
include($template);
}
Usage example: wpse_get_template_part('header-promotion', 'top', [$message => 'Example message']);
Neither function is a perfect copy of get_template_part(). I've skipped all of the extra filters the core function uses for the sake of simplicity.
What about globals or query vars
Globals are pretty commonplace in WordPress but are generally best avoided. They will work but start to get messy when you use the same template part more than once on a single page.
Query vars (get_query_var() / set_query_var()) aren't made for that purpose. It's a hacky workaround that can introduce unintended side-effects.

As of WordPress 5.5, you're be able to achieve this using the following method:
<?php
get_template_part(
'my-template-name',
null,
array(
'my_data' => array(
'var_one' => 'abc',
'var_two' => true,
)
)
);
In your template, you can then parse the date and escape it in the following way:
<?php
$args = wp_parse_args(
$args,
array(
'my_data' => array(
'var_one' => 'xyz', // default value
'var_two' => false, // default value
)
)
);
?>
<?php echo esc_html( $args['my_data']['var_one'] ); ?>

you can pass data to tempalte part via Global varible
like this
$var = 'smoe data here';
<?php get_template_part( 'element-templates/front', 'top' ); ?>
then in your template part file use
<?php
global $var;
echo $var; // smoe data here
?>

Like mentioned by thetwopct - since WP 5.5, you can pass $args to the get_template_part function like so:
$value_of_first = 'pizza';
$value_of_second = 'hamburger';
$value_of_first = 'milkshake';
$args = array(
'first' => $value_of_first,
'second' => $value_of_second,
'third' => $value_of_third,
)
get_template_part( 'element-templates/front', 'top', $args );
then, in element-templates/front-top.php get the vars like so:
[
'first' => $value_of_first,
'second' => $value_of_second,
'third' => $value_of_third,
] = $args;
echo 'I like to eat '.$value_of_first.' and '.$value_of_second.' and then drink '.$value_of_third.';
// will print "I like to eat pizza and hamburger and then drink milkshake"
Note: the method described above for getting the vars in the template file uses associative array restructuring, available only from PHP 7.1. You can also do something like:
echo 'I like to eat '.$args[value_of_first].' and ...

A quick and dirty workaround can be made using PHP Constants:
In your theme:
define("A_CONSTANT_FOR_TEMPLATE_PART", "foo");
get_template_part("slug", "name" );
Now in slug-name.php template file you can use the A_CONSTANT_FOR_TEMPLATE_PART constant. Be sure not override system constants by using a custom prefix for your constant name.
In your template part file:
echo A_CONSTANT_FOR_TEMPLATE_PART;
You should see "foo".

Related

How to reset after nested WP_Query inside WooCommerce products loop?

On the front-page I use multiple shortcodes like [products limit="4"] or custom WP queries. All of them fail because I am doing additional WP_Query inside each product in those loops.
Inside WooCommerce template content-product.php I am trying to call a function and I need to pass product ID to it.
<li <?php wc_product_class('', $product); ?>> <!-- this is the usual Woocommerce stuff to begin loop -->
<?php
$data = testtttt($product->get_id());
var_dump($data);
This is a function (I've simplified it for the sake of example)
function testtttt($product_id)
{
$args = array(
'posts_per_page' => -1,
'post_type' => 'discounts',
'meta_query' => array(
array(
'key' => 'connected_product_id',
'value' => $product_id
),
)
);
$discount_query = new WP_Query($args);
$data = [];
if ($discount_query->have_posts()) :
while ($discount_query->have_posts()) :
$discount_query->the_post();
$data[] = get_the_title();
endwhile;
endif;
//$discount_query->reset_postdata();
wp_reset_postdata();
return $data;
}
The problem is the following error, so everything after first <li>{product}</li> brakes completely.
PHP Fatal error: Uncaught Error: Call to a member function is_on_sale() on null in C:\wamp64\www\...\wp-content\plugins\woocommerce\templates\loop\sale-flash.php:26
That is probably because $product variable disappears after ...->the_post() thing. So I tried to make the first part like this:
// Try to save product variable for later reset
$product_temp = $product;
$data = testtttt($product->get_id());
var_dump($data);
// Try to reset product variable
global $product;
$product = $product_temp;
Now the code doesn't brake anymore but each <li></li> has completely wrong data, instead of product titles, titles for each product is now "Frontpage".
Any idea how to solve this?

Shortcode issue

I am currently creating a shortcode in order to display custom taxonomy terms as a list in my template :
// First we create a function
function list_terms_forme_juridique_taxonomy( $atts ) {
// Inside the function we extract custom taxonomy parameter of our
shortcode
extract( shortcode_atts( array(
'custom_taxonomy' => 'forme_juridique',
),
$atts ) );
// arguments for function wp_list_categories
$args = array(
taxonomy => $custom_taxonomy,
title_li => ''
);
// We wrap it in unordered list
echo '<ul>';
echo wp_list_categories($args);
echo '</ul>';
}
// Add a shortcode that executes our function
add_shortcode( 'forme_juridique', 'list_terms_forme_juridique_taxonomy'
);
I run in the 2 following issues :
The shortcode (render) is displayed at the top of my page, not where I've placed it in the page;
PHP Console flag the 2 followings errores :
Use of undefined constant taxonomy - assumed 'taxonomy'
Use of undefined constant title_li - assumed 'title_li'
Any help appreciated!
Thanks
Firstly the output of your shortcode is displaying at the top of your page because you're echoing the output. You should create a $output variable and build it up with what you want to display, and then return it. For example:
$output = '';
$output .= '<ul>';
$output .= wp_list_categories($args);
$output .= '</ul>';
return $output;
Secondly you're getting the errors because you've not quoted the keys in your array declaration. Therefore PHP assumes they should be constants that were previously defined.
$args = array(
taxonomy => $custom_taxonomy,
title_li => ''
);
Should be:
$args = array(
'taxonomy' => $custom_taxonomy,
'title_li' => ''
);

$wp_query and WP_QUERY - same arguments, different results

I spend this whole day trying to figure out a problem with a combination of a custom query and custom post types. This is my last resort...
The setting
I wrote a plugin that introduces some custom post types to my WordPress. To display them in the main query I hooked them into the query like this:
function add_cpt_to_query( $query ) {
*some code...*
// add custom post types to query
if ( !( is_admin() || is_post_type_archive() || is_page() ) && $query->is_main_query() ) {
$query->set('post_type', array_merge( array('post'), $cpt ) );
}
}
add_action('pre_get_posts','add_cpt_to_query');
In my theme on the other hand I setup ajax pagination like this:
function setup_pagination() {
global $wp_query;
$max_pages = $wp_query->max_num_pages;
$current_page = ( $wp_query->paged > 1 ) ? $wp_query->paged : 1;
$ajaxurl = admin_url( 'admin-ajax.php' );
wp_register_script( 'ajax-pagination', get_template_directory_uri() .'/js/dummy.js', array('jquery'), '', true);
wp_localize_script( 'ajax-pagination', 'ajaxpagination', array(
'max_pages' => $max_pages,
'current_page' => $current_page,
'ajaxurl' => $ajaxurl,
'query_vars' => $wp_query->query_vars
));
wp_enqueue_script( 'ajax-pagination' );
}
add_action( 'wp_enqueue_scripts', 'setup_pagination' );
function pagination() {
$query = $_POST['query_vars'];
$query['paged'] = $_POST['next_page'];
/*
$query = array(
'paged' => 2,
'post_type' => array('post', 'custom_post_type_1', 'custom_post_type_2' )
);
*/
$posts = new WP_Query( $query );
$GLOBALS['wp_query'] = $posts;
// Start the loop.
while ( have_posts() ) : the_post();
?>
*some code...*
<?php endwhile;
die();
}
add_action( 'wp_ajax_nopriv_ajax_pagination', 'pagination' );
add_action( 'wp_ajax_ajax_pagination', 'pagination' );
and the script part:
$.ajax({
url: ajaxpagination.ajaxurl,
type: 'post',
data: {
action: 'ajax_pagination',
query_vars: ajaxpagination.query_vars,
next_page: parseInt(ajaxpagination.current_page) + 1
}
});
The problem
If I pass the query_vars array I get from $wp_query with the altered 'paged' value back to WP_QUERY, it returns the wrong set of posts. It looks like, that WP_QUERY does not account for the cpts in the loop. Though these cpts are mentioned in the 'post_type' of the query_vars array and thereby passed along to the new WP_QUERY.
When I manually set 'post_type' and only pass this argument, it works as intended. The aspect that blows my mind is, that the resulting query_vars used in the ajax call to WP_QUERY are exactly the same, but only when I manually set 'post_type' the pagination works as it should.
I dont know if this was a somewhat understandable explanation, but I'm thankful for every idea that could help me out. Big THX!
Ok... I got it now.
I made a mistake in wp_localize_script(). The query_vars should be a json-string, I on the other hand just passed the array itself. My code above has to be altered in two lines:
function mk_setup_pagination() {
...
wp_localize_script( 'ajax-pagination', 'ajaxpagination', array(
...
'query_vars' => json_encode($wp_query->query_vars) <- convert to json-string
));
...
}
function mk_pagination() {
$query = json_decode( stripslashes( $_POST['query_vars'] ) , true); <- convert json-string to array
...
Works like a charm now. :)
btw: the code is based on a tutorial by wpmudev.org: Loading WordPress Posts Dynamically With AJAX

Accessing Advanced Custom Fields by Page Name

I am trying to retrieve all advanced custom fields tied to a particular page. This is different than iterating through posts, I am familiar with the following:
$posts = get_posts(array(
'post_type' => 'post_name',
'meta_key' => 'color',
'meta_value' => 'red'
));
However this method is specific to posts and does not allow me to retrieve all ACF by page name.
I appreciate any suggestions on how to accomplish this.
The are too ways to do this that come to mind...
1. Using the Loop
Using WP_Query you can do something like this...
<?php
// WP_Query arguments
$args = array (
'pagename' => 'homepage',
);
// The Query
$query = new WP_Query( $args );
// The Loop
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
the_field( "field_name" );
}
} else {
// no posts found
}
// Restore original Post Data
wp_reset_postdata();
?>
In place of the 'homepage' in 'pagename' => 'homepage', you want to put the page slug of your page. And of course in place of the_field( "text_field" ); you want to add your fields/content.
You can also query by Page ID and some other parameters. You can find all other parameters that you can use here:
https://codex.wordpress.org/Class_Reference/WP_Query#Post_.26_Page_Parameters
2. Adding second parameter to the_field() function
More simple way, without custom loop, is just to use ACF's built-in the_field() function with the $post->ID parameter added as a second parameter.
<?php the_field('field_name', 123);
This might be the way to go since you want to show the content of only one page, and therefore don't really need to loop.
Reference: http://www.advancedcustomfields.com/resources/how-to-get-values-from-another-post/
You can use ACF's get_fields() function -
<?php $fields = get_fields( $post->ID ); ?>
You could then loop through them or simply print the array for testing.
http://www.advancedcustomfields.com/resources/get_fields/

Wordpress shortcode name as an Attribute

I was wondering if possible to setup a shortcode and have the name of the shortcode also work as an attribute. How I have mine currently setup is like so
add_shortcode('tooltip', 'tooltip');
function tooltip( $atts $content = null) {
array(
'type' => '',
);
So when someone in wordpress uses the shortcode you type in
[tooltip type="fruit"]Item Name[/tooltip]
Although I was wondering is it possible to just use the name of the shortcode as a atts so I can short it a little bit and have it look like this
[tooltip="fruit"]Item Name[/tooltip]
So pretty much cut out the type attribute and use the name of the shortcode tooltip as an attribute instead.
Nope, what you're proposing isn't possible. It may be shorter but in my opinion it would be confusing so I don't see it ever being something that's made possible short of you building the functionality yourself.
You have to use first item in $atts array, ($atts[0]) when using shortcode tag as attribute.
Working example:
<?php
add_shortcode('tooltip', 'tooltip');
function tooltip(Array $atts = array(), $content = null, $tag = null) {
$args = shortcode_atts(array( 0 => null ), $atts);
$args['type'] = trim($args[0], '"=');
unset($args[0]);
extract($args);
// Your code starts here ...
$output = array(
'$type' => $type,
'$content' => $content
);
$output = '<pre>' . print_r($output, true) . '</pre>';
return $output;
}
Please replace everything after // Your code starts here ...
Executing example:
[tooltip="fruit"]Item Name[/tooltip]
will return:
<pre>Array
(
[$type] => fruit
[$content] => Item Name
)
</pre>

Resources