Reputation: 53
I would like to show only "in stock" related products on WooCommerce single product pages.
I know I can override single-product/related.php
template file via My theme. Here below is the related code for this template:
<section class="related products">
<h2><?php _e( 'You May Also Want', 'MyStore' ); ?></h2>
<?php woocommerce_product_loop_start(); ?>
<?php foreach ( $related_products as $related_product ) : ?>
<?php
$post_object = get_post( $related_product->get_id() );
setup_postdata( $GLOBALS['post'] =& $post_object );
wc_get_template_part( 'content', 'product' ); ?>
<?php endforeach; ?>
<?php woocommerce_product_loop_end(); ?>
</section>
Is it possible making some changes to this file to only show "in stock" related products on WooCommerce single product page? Any help is appreciated.
Upvotes: 3
Views: 2723
Reputation: 81
This is what I've been using, and it works for me:
add_filter( 'woocommerce_related_products', 'mysite_filter_related_products', 10, 1 );
function mysite_filter_related_products( $related_product_ids )
{
if (!is_admin()) {
foreach ($related_product_ids as $key => $value) {
$relatedProduct = wc_get_product($value);
if (!$relatedProduct->is_in_stock() ) {
unset($related_product_ids["$key"]);
}
}
return $related_product_ids;
}
}
Upvotes: 2
Reputation: 29614
UPDATE
Because when the 4 first products are out of stock it does not display anything (In case the default 4 products are shown) you could use the following snippet instead of overwriting the template file.
function filter_woocommerce_related_products( $related_posts, $product_id, $args ) {
foreach( $related_posts as $key => $related_post ) {
// Get product
$related_product = wc_get_product( $related_post );
// Is a WC product
if ( is_a( $related_product, 'WC_Product' ) ) {
// Stock status
$stock_status = $related_product->get_stock_status();
// NOT instock
if ( $stock_status != 'instock' ) {
unset( $related_posts[$key] );
}
}
}
return $related_posts;
}
add_filter( 'woocommerce_related_products', 'filter_woocommerce_related_products', 10, 3 );
Overwriting the template file
There are always multiple solutions but 1 of them could be by overwriting the template file.
yourtheme/woocommerce/single-product/related.php
Replace
<?php foreach ( $related_products as $related_product ) : ?>
<?php
$post_object = get_post( $related_product->get_id() );
setup_postdata( $GLOBALS['post'] =& $post_object ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, Squiz.PHP.DisallowMultipleAssignments.Found
wc_get_template_part( 'content', 'product' );
?>
<?php endforeach; ?>
With
<?php foreach ( $related_products as $related_product ) : ?>
<?php
$stock_status = $related_product->get_stock_status();
if ( $stock_status == 'instock' ) {
$post_object = get_post( $related_product->get_id() );
setup_postdata( $GLOBALS['post'] =& $post_object ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited, Squiz.PHP.DisallowMultipleAssignments.Found
wc_get_template_part( 'content', 'product' );
}
?>
<?php endforeach; ?>
Upvotes: 1
Reputation: 253773
Instead of editing template files, you can use woocommerce_product_related_posts_query
dedicated filter hook to alter the query excluding "out of stock" products from displayed related products:
add_filter( 'woocommerce_product_related_posts_query', 'alter_product_related_posts_query', 10, 3 );
function alter_product_related_posts_query( $query, $product_id, $args ){
global $wpdb;
$query['join'] .= " INNER JOIN {$wpdb->postmeta} as pm ON p.ID = pm.post_id ";
$query['where'] .= " AND pm.meta_key = '_stock_status' AND meta_value != 'outofstock' ";
return $query;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Related: Customize related products with a custom meta query in Woocommerce
Upvotes: 2