Limit Woocommerce featured products in a WP_Query - php

I want to get 3 featured products in the header of the site. But my query keeps returning unlimited number of results.
I've been looking online for a solution and came across answers that all answer saying the same thing in terms of the query. What could I be doing wrong?
$meta_query = WC()->query->get_meta_query();
$tax_query = WC()->query->get_tax_query();
$tax_query[] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
'operator' => 'IN',
);
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 2,
'meta_query' => $meta_query,
'tax_query' => $tax_query,
);
$featured_query = new WP_Query( $args );
if ($featured_query->have_posts()) {
while ($featured_query->have_posts()) :
$featured_query->the_post();
$product = get_product( $featured_query->post->ID );
echo $product->title; echo "test";
// Product info here
endwhile;
}
wp_reset_query();
The following query returned 20 results. The code was placed in header.php. Using woocommerce 3.x.

First your code is a bit outdated, since Woocommerce 3, as get_product() need to be replaced with wc_get_product() and $product->title; by $product->get_title();…
Once done your code works and you will get 3 featured products:
$meta_query = WC()->query->get_meta_query();
$tax_query = WC()->query->get_tax_query();
$tax_query[] = array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
'operator' => 'IN',
);
$featured = new WP_Query( array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 3, // <== <== <== 3 products
'meta_query' => $meta_query,
'tax_query' => $tax_query,
) );
// Get the products count in the query
echo '<p>Featured products count: ' .$featured->post_count . '</p>';
if ($featured->have_posts()) : while ($featured->have_posts()) :
$featured->the_post();
$product = wc_get_product( $featured->post->ID );
echo $product->get_title() . '<br>';
// Product info here
endwhile; endif;
wp_reset_postdata();
It should work for you as I have tested successfully this code on header.php file…
As before Woocommerce 3, the "featured products" where handled by post meta data (a meta query), you may need to update product terms count going to Woocommerce settings > status > tools. In "Term counts" section click on "Recount terms".

You should be using wp_reset_postdata() instead of wp_reset_query() since WP_query doesn't overwrite the main query.
If that doesn't solve your issue, make sure any other custom loops use the appropriate reset, and/or try renaming the variable $featured_query if you're using it elsewhere - it may be inheriting posts from a previous loop.
You could also try adding the 'nopaging' => true and 'ignore_sticky_posts' => true arguments
I hate to suggest it, but if you can't figure out why it's returning 20 posts instead of 2, you could just break your while loop with a counter:
if ($featured_query->have_posts()) {
$counter = 0;
while ($featured_query->have_posts()) : $featured_query->the_post();
/* Do post stuff here */
$counter++;
if( $counter == 2 ) break;
endwhile;
}

Related

Custom Shortcode w/ Custom $tax_query Attribute

I'm trying to create a custom shortcode in Wordpress that will let me place an attribute to specify a category and query within a custom post type called "case_studies". The taxonomy itself is called "case_study_categories" and I've set the default to be "furniture-interior", as you can see.
What I need is for the shortcode to look something like this:
[get_project_gallery order="ASC" tax_query="flooring"]
Ideally, the shortcode attribute I've placed would override the default of "furniture-interior" and instead use "flooring" to display the appropriate posts. Actually, I would think I don't even really need a default defined at all (so it would just get all the case_studies posts if no attribute is specified) but I can't get that to work either.
BTW, the attribute for order works exactly the way I would want - it overrides the defined default. I just can't figure out how to get this done with the $tax_query.
Any help on this would be very much appreciated. Thanks in advance all!
Here's my code:
function project_gallery_function($atts, $content = null) {
extract(shortcode_atts(array(
'order' => 'DESC',
'orderby' => 'date',
'tax_query' => array(
array(
'taxonomy' => 'case_study_categories',
'field' => 'slug',
'terms' => 'furniture-interior'
)
)
), $atts));
$args = array(
'post_type' => 'case_studies',
'post_status' => 'publish',
'posts_per_page' => '4',
'order' => $order,
'orderby' => $orderby,
'tax_query' => array($tax_query)
);
// The Query
$the_query = new WP_Query( $args );
// The Loop
if ( $the_query->have_posts() ) {
echo '<aside class="grid-gallery four-col grid-gap-4">';
while ( $the_query->have_posts() ) {
$the_query->the_post();
$gallery_thumb = get_the_post_thumbnail_url( $page->ID, 'thumbnail' );
echo '<img src="'. $gallery_thumb .'" alt="" />';
}
echo '</aside>';
}
/* Restore original Post Data */
wp_reset_postdata();
}
add_shortcode( 'get_project_gallery', 'project_gallery_function' );
You shouldn't be worrying about the whole entire tax_query unless you plan on actually redoing the whole tax_query. You should consider changing it to just using $terms instead, since that's the only part you're replacing with the shortcode (you don't need to rebuild the whole array).
Secondly, make sure your code is normalized with your spacing and indentation - future you appreciates the forethought!
Lastly, you could consider passing even more variables to the array to make it a bit more extensible - $posts_per_page, $taxonomy etc.
function project_gallery_function( $atts, $content = null ){
extract( shortcode_atts( array(
'terms' => 'furniture-interior',
'field' => 'slug',
'order' => 'DESC',
'orderby' => 'date',
'taxonomy' => 'case_study_categories',
'post_type' => 'case_studies',
'posts_per_page' => 4,
), $atts ) );
$tax_query = array(
array(
'taxonomy' => $taxonomy,
'field' => $field,
'terms' => explode(',', preg_replace('/\s+/', '', $terms)), // Force to array, allowing comma sep values
)
);
$args = array(
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => $posts_per_page,
'order' => $order,
'orderby' => $orderby,
'tax_query' => array( $tax_query )
);
// The Query
$the_query = new WP_Query( $args );
// The Loop
if( $the_query->have_posts() ){
echo '<aside class="grid-gallery four-col grid-gap-4">';
while( $the_query->have_posts() ){
$the_query->the_post();
if( $gallery_thumb = get_the_post_thumbnail_url( $page->ID, 'thumbnail' ) ){
printf( '<img src="%s" alt="" />', get_the_permalink(), $gallery_thumb );
}
}
echo '</aside>';
}
/* Restore original Post Data */
wp_reset_postdata();
}
add_shortcode( 'get_project_gallery', 'project_gallery_function' );
A few other notes, get_the_post_thumbnail_url() returns a falsey value if it doesn't exist, so you should wrap that in an if statement incase the image can't be found for some reason (corrupted, removed, forgot to add, etc). Also you have a $page->ID variable in get_the_post_thumbnail_url() - but don't seem to have a global $page in the function? Make sure that's the variable you want to use there.
Using code like what I've provided will let you do what you want, [get_project_gallery order="ASC" terms="flooring"], or even add multiple [get_project_gallery order="ASC" terms="flooring,something-else,a-third-thing"] - just match up the shortcode attributes with the parameter you want to override: [get_project_gallery order="ASC" terms="flooring,something-else,a-third-thing" posts_per_page="15"]

Get featured products in Woocommerce 3

I want to add Featured products for upsell (or Recent Products OR Best Selling Products) in New order Email template. It works like Upsell with email marketing. How do I add it to woocommerce email template so that there is a section in the end of the email which shows my featured/Recent/best selling products. I tried using this code in my New order Email template but nothing works. I use all latest version of wordpress n woocommerce.
$args = array(
'post_type' => 'product',
'meta_key' => '_featured',
'meta_value' => 'yes',
'posts_per_page' => 1
);
$featured_query = new WP_Query( $args );
if ($featured_query->have_posts()) :
while ($featured_query->have_posts()) :
$featured_query->the_post();
$product = get_product( $featured_query->post->ID );
// Output product information here
endwhile;
endif;
wp_reset_query(); // Remember to reset
Since Woocommerce 3, the products following properties:
"featured",
"stock status",
"catalog visibility"
"rating system"
are now stored like a post term under 'product_visibility' taxonomy, for better performances. So old postmeta data doesn't work anymore.
To make your code working you need instead to make a tax_query this way:
function custom_featured_products(){
$query = new WP_Query( array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1 ,
'tax_query' => array( array(
'taxonomy' => 'product_visibility',
'field' => 'term_id',
'terms' => 'featured',
'operator' => 'IN',
) )
) );
$featured_product_names = array();
if ( $query->have_posts() ): while ( $query->have_posts() ): $query->the_post();
$product = wc_get_product( $query->post->ID );
$featured_product_names[] = $product->get_title();
endwhile; wp_reset_query();endif;
// Testing output
echo '<p>Featured products: ' . implode(', ', $featured_product_names) . '</p>';
}
// Displaying the featured products names in new order email notification
add_action ('woocommerce_email_customer_details', 'new_order_featured_products_display', 30, 4 );
function new_order_featured_products_display( $order, $sent_to_admin, $plain_text, $email ){
// Only for "New Order" email notification
if( 'new_order' != $email->id ) return;
custom_featured_products(); // calling the featured products function output
}
This code goes on function.php file of your active child theme (or theme).
Tested and works.
Updated related to your comments…
Added the code in a function that is called via a hooked function in "New order" email notification.
To get the product image use: $product->get_image( 'shop_thumbnail' );
To get the product link use : $product->get_permalink();
<ul class="products">
<?php
$args = array(
'post_type' => 'product',
'posts_per_page' => 12,
'tax_query' => array(
array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => 'featured',
),
),
);
$loop = new WP_Query( $args );
if ( $loop->have_posts() ) {
while ( $loop->have_posts() ) : $loop->the_post();
echo '<p>'.get_the_title().'</p>';
endwhile;
} else {
echo __( 'No products found' );
}
wp_reset_postdata();
?>
</ul><!--/.products-->

Wordpress Recent Posts/Projects

Hi I have created my own custom post type within Wordpress to contain projects that i can call via my theme files.
I am new to creating my own themes. I currently am using the following code in my single.php file to call in related articles based on the category of the blog post.
<?php
// Default arguments
$args = array(
'posts_per_page' => 3, // How many items to display
'post__not_in' => array( get_the_ID() ), // Exclude current post
'no_found_rows' => true, // We don't ned pagination so this speeds up the query
);
// Check for current post category and add tax_query to the query arguments
$cats = wp_get_post_terms( get_the_ID(), 'category' );
$cats_ids = array();
foreach( $cats as $wpex_related_cat ) {
$cats_ids[] = $wpex_related_cat->term_id;
}
if ( ! empty( $cats_ids ) ) {
$args['category__in'] = $cats_ids;
}
// Query posts
$wpex_query = new wp_query( $args );
// Loop through posts
foreach( $wpex_query->posts as $post ) : setup_postdata( $post ); ?>
<div class="col-md-4 related-post">
<?php the_post_thumbnail('large'); ?>
<?php the_title(); ?>
</div>
<?php
// End loop
endforeach;
// Reset post data
wp_reset_postdata(); ?>
In my new post type "projects" i would like to call in related projects. Which im assuming would be very similar code except i need to stop it looking for posts and instead look for my projects.
Here is my code for new post type:
// Projects
add_action( 'init', 'create_post_type' );
add_post_type_support( 'bw_projects', 'thumbnail' );
add_post_type_support( 'bw_projects', 'custom-fields' );
function create_post_type() {
register_post_type( 'bw_projects',
array(
'labels' => array(
'name' => __( 'Projects' ),
'singular_name' => __( 'Projects' )
),
'public' => true,
'has_archive' => true,
'taxonomies' => array( 'category','post_tag'),
)
);
}
What would i need to change in my first code snippet in order to look for bw_projects and not look for 'posts' anymore. I tried playing around and changing certain lines myself but i caused more issues and stopped the page loading. Is this even right i can use the same code, slightly altered or would i need something completely different?
Thanks in advance.
You can get any post type that you require using get_posts();
<?php $args = array(
'posts_per_page' => 5,
'offset' => 0,
'category' => '',
'category_name' => '',
'orderby' => 'date',
'order' => 'DESC',
'include' => '',
'exclude' => '',
'meta_key' => '',
'meta_value' => '',
'post_type' => 'projects',
'post_mime_type' => '',
'post_parent' => '',
'author' => '',
'author_name' => '',
'post_status' => 'publish',
'suppress_filters' => true
);
$posts_array = get_posts( $args ); ?>
Simply set the 'post_type' argument to that of you custom post type, to get only these posts. You can also set the number of post, and filter by category etc.
You can find more info in the codex.
Alternatively, if you wanted to keep something similar to your existing code you could try using 'pre_get_posts' to filter the query to just your projects. However you'd need to remember to add / remove this filter so it only operates on the queries that need it.
To display the posts you can use a simple foreach to churn them out. You#d obviously want to do some sort of styling to get the layout correct:
$args = array("posts_per_page" => 10, "orderby" => "comment_count");
$posts_array = get_posts($args);
foreach($posts_array as $post)
{
echo "<h1>" . $post->post_title . "</h1><br>";
echo "<p>" . $post->post_content . "</p><br>";
}
Or a really concise way of doing all of the above would be something like:
$args = array("posts_per_page" => 5, "post_type" => "projects");
$posts_array = get_posts($args);
foreach($posts_array as $post)
{
echo "<h1>" . $post->post_title . "</h1><br>";
echo "<p>" . $post->post_content . "</p><br>";
}

Woocommerce checking stock of several products in a DRY way?

How can i check the stock of several products of several products of the same parent category
in a DRY way? I would either need to get the total stock and / or check that the stock of any given product doesn't fall under a certain threshold.
So far, i know how to retrieve the stock for each individual product, i would need (1) to return the value of the total amount of stocks and (2) to check if no individual value falls under a certain threshold.
Here's my code:
<?php
$args = array(
'post_type' => 'product',
'product_cat' => 'name_of_my_category',
'orderby' =>'date',
'order' => 'DESC',
);
$name_of_my_query = new WP_Query( $args );
if ($name_of_my_query->have_posts()) :
while ($name_of_my_query->have_posts()) :
$name_of_my_query->the_post();
$product = get_product( $name_of_my_query->post->ID );
echo $product->stock;
endwhile;
endif;
wp_reset_query();
?>
At this stage, i can only think of repeating this code for each product, assigning each value returned to a different variable and sum them up... quite a lengthy process, i'm sure there must be a simpler way?
Thanks for your help!
You could create an array of argument arrays, and loop through them with a foreach statement:
$threshold = 100;
$args_array = array(
array(
'post_type' => 'product',
'product_cat' => 'name_of_my_category',
'orderby' =>'date',
'order' => 'DESC'
),
array(
'post_type' => 'product2',
'product_cat' => 'name_of_my_category2',
'orderby' =>'date',
'order' => 'DESC'
),
array(
'post_type' => 'product3',
'product_cat' => 'name_of_my_category3',
'orderby' =>'date',
'order' => 'DESC'
)
);
foreach ($args_array as $args) {
$name_of_my_query = new WP_Query( $args );
if ($name_of_my_query->have_posts()) :
while ($name_of_my_query->have_posts()) :
$name_of_my_query->the_post();
$product = get_product( $name_of_my_query->post->ID );
echo $product->stock;
if((int)$product->stock <= $threshold){
//do stuff here to alert yourself that stock is low
echo "Stock is low on".$product->name;
}
endwhile;
endif;
wp_reset_query();
}

Custom sort of featured products in woocommerce

Can any body please help me to sort the featured products only using different sorting attributes like price,date in woo-commerce.
I have tried the code
function sort_by()
{
$args = array(
'post_type' => 'product',
'meta_key' => '_featured',
'meta_value' => 'yes',
'posts_per_page' => '-1',
'orderby' => $_POST['sorter'] ,
'order'=>'ASC'
);
$featured_query = new WP_Query( $args );
print_r($featured_query);
if ($featured_query->have_posts()) :
while ($featured_query->have_posts()) :
$featured_query->the_post();
$product = get_product( $featured_query->post->ID );
echo the_title();
endwhile;
endif;
wp_reset_query(); // Remember to reset
die();
}
add_action('wp_ajax_sort_by','sort_by');
add_action('wp_ajax_no-prev_sort_by','sort_by');
The above code works when ajax hit but it's cannot sort the feature products according to date, price, popularity and rating.

Categories