I am using Wordpress with woocommerce and trying to set up some variable products. But variable products show the "In Stock" messages directly below the input/select field. I want it to be shown below the "Add to cart" button.
I am using a child theme for the files, simple.php, variable.php and variation.php.
Feel free to look at the picture to see what I want to do.
http://art-wood.de/instock-move.png
Currently my variable.php looks like this:
<?php
/**
* Variable product add to cart
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product/add-to-cart/variable.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer).
* will need to copy the new files to your theme to maintain compatibility. We try to do this.
* as little as possible, but it does happen. When this occurs the version of the template file will.
* be bumped and the readme will list any important changes.
*
* #see http://docs.woothemes.com/document/template-structure/
* #author WooThemes
* #package WooCommerce/Templates
* #version 2.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
global $product;
$attribute_keys = array_keys( $attributes );
do_action( 'woocommerce_before_add_to_cart_form' ); ?>
<form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $available_variations ) ) ?>">
<?php do_action( 'woocommerce_before_variations_form' ); ?>
<?php if ( empty( $available_variations ) && false !== $available_variations ) : ?>
<div class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></div>
<?php else : ?>
<span class="variations" cellspacing="0">
<?php foreach ( $attributes as $attribute_name => $options ) : ?>
<label class="label" for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label>
<div class="value">
<?php
$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) : $product->get_variation_default_attribute( $attribute_name );
wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
echo end( $attribute_keys ) === $attribute_name ? apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>' ) : '';
?>
</div>
<?php endforeach;?>
</span>
<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
<div class="single_variation_wrap">
/**
* woocommerce_before_single_variation Hook.
*/
do_action( 'woocommerce_before_single_variation' );
/**
* woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
* #since 2.4.0
* #hooked woocommerce_single_variation - 10 Empty div for variation data.
* #hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
*/
do_action( 'woocommerce_single_variation' );
/**
* woocommerce_after_single_variation Hook.
*/
do_action( 'woocommerce_after_single_variation' );
?>
</div>
<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
<?php endif; ?>
<?php do_action( 'woocommerce_after_variations_form' ); ?>
</form>
<?php do_action( 'woocommerce_after_add_to_cart_form' ); ?>
Does somebody know how to move the message down below the Add to cart button? This would be amazing! May its a simple add to the functions.php maybe its a switch in the variable.php... I don't know that. Otherwise I wouldn't ask :)
I found the solution by myself. Maybe its useful for someone so here it is ;)
Simply add this to your functions.php
remove_action( 'woocommerce_single_variation' , 'woocommerce_single_variation', 10);
add_action( 'woocommerce_single_variation', 'woocommerce_single_variation', 20 );
Related
I am working in e-commerce website, i made my own theme and I installed the woocommerce plugin. but there is a problem displaying the products reviews.
the code below is in the woocommerce/single-product/tabs/tabs.php
<?php
/**
* Single Product tabs
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product/tabs/tabs.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* #see https://docs.woocommerce.com/document/template-structure/
* #package WooCommerce\Templates
* #version 3.8.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Filter tabs and allow third parties to add their own.
*
* Each tab is an array containing title, callback and priority.
*
* #see woocommerce_default_product_tabs()
*/
$product_tabs = apply_filters( 'woocommerce_product_tabs', array() );
if ( ! empty( $product_tabs ) ) : ?>
<div class="woocommerce-tabs wc-tabs-wrapper" >
<ul class="tabs wc-tabs" role="tablist">
<?php foreach ( $product_tabs as $key => $product_tab ) : ?>
<li class="<?php echo esc_attr( $key ); ?>_tab" id="tab-title-<?php echo esc_attr( $key ); ?>" role="tab" aria-controls="tab-<?php echo esc_attr( $key ); ?>">
<a href="#tab-<?php echo esc_attr( $key ); ?>">
<?php echo wp_kses_post( apply_filters( 'woocommerce_product_' . $key . '_tab_title', $product_tab['title'], $key ) ); ?>
</a>
</li>
<?php endforeach; ?>
<li class="reviews_tab" id="tab-title---><?php //echo esc_attr( $key ); ?><!--" role="tab" aria-controls="tab---><?php //echo esc_attr( $key ); ?><!--">
<a href="#tab-reviews">
Reviews
</a>
</li>
</ul>
<?php foreach ( $product_tabs as $key => $product_tab ) : ?>
<div class="woocommerce-Tabs-panel woocommerce-Tabs-panel--<?php echo esc_attr( $key ); ?> panel entry-content wc-tab" id="tab-<?php echo esc_attr( $key ); ?>" role="tabpanel" aria-labelledby="tab-title-<?php echo esc_attr( $key ); ?>">
<?php
if ( isset( $product_tab['callback'] ) ) {
call_user_func( $product_tab['callback'], $key, $product_tab );
}
?>
</div>
<?php endforeach; ?>
<div class="woocommerce-Tabs-panel woocommerce-Tabs-panel--reviews panel entry-content wc-tab" id="tab-reviews" role="tabpanel" aria-labelledby="tab-title-reviews">
<div id="reviews">
<?php
woocommerce_get_template_part( 'single-product-reviews' );
?>
</div>
</div>
<?php do_action( 'woocommerce_product_after_tabs' ); ?>
</div>
<?php endif; ?>
I have spent the past 2 days in this problem and couldn't figure out why its not showing the reviews
In order to display the Product Reviews you need to ensure a couple of things
In WooCommerce --> Settings on the Products tab under General, ensure that you have ticked "Enable product reviews"
In the actual products under "Advanced" tick "Enable reviews"
Also when setting up a custom theme if you are overriding the default template for single product you need to ensure that you include the woocommerce_after_single_product_summary action as it includes the woocommerce_output_product_data_tabs that includes displaying the product Reviews
/**
* Hook: woocommerce_after_single_product_summary.
*
* #hooked woocommerce_output_product_data_tabs - 10
* #hooked woocommerce_upsell_display - 15
* #hooked woocommerce_output_related_products - 20
*/
do_action( 'woocommerce_after_single_product_summary' );
Here's a link to an article with images explaining steps 1 and 2 further
Reviews not showing on product page
I need a solution to hide the "Add to Cart" button from the product page when the product price is zero or 100% Discount
I think the following code is probably for the add to cart section
This code was in the following path
mytheme / woocommerce / single-product / add-to-cart / simple.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
global $product;
if ( ! $product->is_purchasable() ) {
return;
}
echo wc_get_stock_html( $product );
$prefix = '_studiare_';
$woo_studiare_btn_link = get_post_meta(get_the_id(), $prefix . 'woo_course_url', true);
$woo_studiare_btn_label = get_post_meta(get_the_id(), $prefix . 'woo_course_label', true);?>
<?php if ( ( empty( $woo_studiare_btn_label ) ) && ( empty( $woo_studiare_btn_link ) ) ) :
if ( $product->is_in_stock() ) : ?>
<?php do_action( 'woocommerce_before_add_to_cart_form' ); ?>
<form class="cart" method="post" enctype='multipart/form-data'>
<?php
/**
* #since 2.1.0.
*/
do_action( 'woocommerce_before_add_to_cart_button' );
/**
* #since 3.0.0.
*/
do_action( 'woocommerce_before_add_to_cart_quantity' );
woocommerce_quantity_input( array(
'min_value' => apply_filters( 'woocommerce_quantity_input_min', 1, $product ),
'max_value' => apply_filters( 'woocommerce_quantity_input_max', $product->get_max_purchase_quantity(), $product ),
'input_value' => isset( $_POST['quantity'] ) ? wc_stock_amount( $_POST['quantity'] ) : $product->get_min_purchase_quantity(),
) );
/**
* #since 3.0.0.
*/
do_action( 'woocommerce_after_add_to_cart_quantity' );
?>
<button type="submit" name="add-to-cart" value="<?php echo esc_attr( $product->get_id() ); ?>" class="single_add_to_cart_button button alt"><?php echo esc_html( $product->single_add_to_cart_text() ); ?></button>
<?php
/**
* #since 2.1.0.
*/
do_action( 'woocommerce_after_add_to_cart_button' );
?>
</form>
<?php do_action( 'woocommerce_after_add_to_cart_form' ); ?>
<?php endif; ?>
<?php else: ?>
<?php echo esc_attr($woo_studiare_btn_label); ?>
<?php endif; ?>
change this code:
if ( $product->is_in_stock() ) : ?>
to:
if ( $product->is_in_stock() && $product->get_price() > 0 ) : ?>
Or add below code in your active theme functions.php and check it.
function wpcustom_is_purchasable( $purchasable, $product ){
if( $product->get_price() == 0 )
$purchasable = false;
return $purchasable;
}
add_filter( 'woocommerce_is_purchasable', 'wpcustom_is_purchasable', 10, 2 );
In WooCommerce, I'm trying to add some custom code into my active theme's functions.php file, which will enable for variable products to display the attribute dropdowns, the quantity field and the add to cart button on shop and archive pages.
Here's my actual code:
// Display variations dropdowns on shop page for variable products
add_filter( 'woocommerce_loop_add_to_cart_link', 'woo_display_variation_dropdown_on_shop_page' );
function woo_display_variation_dropdown_on_shop_page() {
global $product;
if( $product->is_type( 'variable' )) {
$attribute_keys = array_keys( $product->get_attributes() );
?>
<form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
<?php do_action( 'woocommerce_before_variations_form' ); ?>
<?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
<p class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
<?php else : ?>
<table class="variations" cellspacing="0">
<tbody>
<?php foreach ( $product->get_attributes() as $attribute_name => $options ) : ?>
<tr>
<td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
<td class="value">
<?php
$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
echo end( $attribute_keys ) === $attribute_name ? apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>' ) : '';
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
<div class="single_variation_wrap">
<?php
/**
* woocommerce_before_single_variation Hook.
*/
do_action( 'woocommerce_before_single_variation' );
/**
* woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
* #since 2.4.0
* #hooked woocommerce_single_variation - 10 Empty div for variation data.
* #hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
*/
do_action( 'woocommerce_single_variation' );
/**
* woocommerce_after_single_variation Hook.
*/
do_action( 'woocommerce_after_single_variation' );
?>
</div>
<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
<?php endif; ?>
<?php do_action( 'woocommerce_after_variations_form' ); ?>
</form>
<?php } else {
echo sprintf( '<a rel="nofollow" href="%s" data-quantity="%s" data-product_id="%s" data-product_sku="%s" class="%s">%s</a>',
esc_url( $product->add_to_cart_url() ),
esc_attr( isset( $quantity ) ? $quantity : 1 ),
esc_attr( $product->id ),
esc_attr( $product->get_sku() ),
esc_attr( isset( $class ) ? $class : 'button' ),
esc_html( $product->add_to_cart_text() )
);
}
}
But the following errors are thrown and having issues making it work:
Your PHP code changes were rolled back due to an error on line 666 of file wp-content/plugins/woocommerce-composite-products/includes/wc-cp-template-functions.php. Please fix and try saving again.
Uncaught Error: Call to a member function get_type() on null in
wp-content/plugins/woocommerce-composite-products/includes/wc-cp-template-functions.php:666
Stack trace:
#0 wp-includes/class-wp-hook.php(286): wc_cp_before_add_to_cart_button('')
#1 wp-includes/class-wp-hook.php(310): WP_Hook->apply_filters('', Array)
#2 wp-includes/plugin.php(465): WP_Hook->do_action(Array)
#3 wp-content/themes/electro-child/functions.php(2228): do_action('woocommerce_bef...')
#4 wp-includes/class-wp-hook.php(288): woo_display_variation_dropdown_on_shop_page('<a href="https:...')
#5 wp-includes/plugin.php(208): WP_Hook->apply_filters('<a href="https:...', Array)
#6 wp-content/plugins/woocommerce/templates/loop/add
I think there's a conflict with the Composite product plugin I'm using.
Any help is hugely appreciated.
The biggest mistake in your code is that you just forgot that you are using a filter hook and in that case, you need to return the output, but not to echo it.
Then you will use the hook arguments that are included in this filter hook instead.
Also you just need to change the behavior for variable products only, because, with your actual code, as you are also using the WooCommerce Composite Products plugin, it gives you the reported error.
So try instead something like (untested):
// Display variations dropdowns on shop page for variable products
add_filter( 'woocommerce_loop_add_to_cart_link', 'woo_display_variation_dropdown_on_shop_page', 10, 3 );
function woo_display_variation_dropdown_on_shop_page( $add_to_cart_link, $product, $args ) {
if( $product->is_type('variable') ) {
$attribute_keys = array_keys( $product->get_attributes() );
ob_start(); // Start buffering
?>
<form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->id ); ?>" data-product_variations="<?php echo htmlspecialchars( json_encode( $product->get_available_variations() ) ) ?>">
<?php do_action( 'woocommerce_before_variations_form' ); ?>
<?php if ( empty( $product->get_available_variations() ) && false !== $product->get_available_variations() ) : ?>
<p class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
<?php else : ?>
<table class="variations" cellspacing="0">
<tbody>
<?php foreach ( $product->get_attributes() as $attribute_name => $options ) : ?>
<tr>
<td class="label"><label for="<?php echo sanitize_title( $attribute_name ); ?>"><?php echo wc_attribute_label( $attribute_name ); ?></label></td>
<td class="value">
<?php
$selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) : $product->get_variation_default_attribute( $attribute_name );
wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
echo end( $attribute_keys ) === $attribute_name ? apply_filters( 'woocommerce_reset_variations_link', '<a class="reset_variations" href="#">' . __( 'Clear', 'woocommerce' ) . '</a>' ) : '';
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>
<div class="single_variation_wrap">
<?php
/**
* woocommerce_before_single_variation Hook.
*/
do_action( 'woocommerce_before_single_variation' );
/**
* woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
* #since 2.4.0
* #hooked woocommerce_single_variation - 10 Empty div for variation data.
* #hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
*/
do_action( 'woocommerce_single_variation' );
/**
* woocommerce_after_single_variation Hook.
*/
do_action( 'woocommerce_after_single_variation' );
?>
</div>
<?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
<?php endif; ?>
<?php do_action( 'woocommerce_after_variations_form' ); ?>
</form>
<?php
$add_to_cart_link = ob_get_clean(); // The buffered content
}
return $add_to_cart_link;
}
I've moved the Stock.PHP and Price.PHP files from Templates into my theme WooCommerce -> Single Product folder. Now, I've tried to move this line of code from Stock.PHP into Price.PHP:
<p class="stock <?php echo esc_attr( $class ); ?>"><?php echo wp_kses_post( $availability ); ?></p>
This is my Stock.PHP file now.
<?php
/**
* Single Product stock.
* #version 3.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<!-- <p class="stock <?php echo esc_attr( $class ); ?>"><?php echo wp_kses_post( $availability ); ?></p> -->
And this is my Price.PHP file.
<?php
/**
* Single Product Price
* #version 3.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
global $product;
?>
<p class="price"><?php echo $product->get_price_html(); ?></p>
<p class="stock <?php echo esc_attr( $class ); ?>"><?php echo wp_kses_post( $availability ); ?></p>
But for no reason, the Stock is not getting the esc_attr value and the availability of the product. How come this paragraph does get the class and the availability inside the Stock file but not inside the Price file?
Thanks,
Lucian
I am using WooCommerce mini cart widget and I want to limit the number of products shown in the mini cart. If a user adds 10 products to the cart, the mini cart widget shows all 10.
I would like to limit the number lets say to 5 products.
Is there a way to achieve this?
Thanks.
You can achieve this overriding cart/mini-cart.php WooCommerce template via your active theme, ( see this related docs: Template Structure + Overriding Templates via a Theme ).
Briefly (if not done yet), you will need to copy from woocommerce plugin folder, a subfolder named templates to your active child theme (or theme) and to rename it woocommerce..
After that you find inside that new woocommerce folder in cart subfolder a template named mini-cart.php.
Open/edit mini-cart.php template and replace the code by this to limit cart to 5 items:
<?php
/**
* Mini-cart
*
* Contains the markup for the mini-cart, used by the cart widget.
*
* This template can be overridden by copying it to yourtheme/woocommerce/cart/mini-cart.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* #see https://docs.woocommerce.com/document/template-structure/
* #author WooThemes
* #package WooCommerce/Templates
* #version 2.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
?>
<?php do_action( 'woocommerce_before_mini_cart' ); ?>
<ul class="cart_list product_list_widget <?php echo $args['list_class']; ?>">
<?php if ( ! WC()->cart->is_empty() ) : ?>
<?php
// Define HERE the number of items
$number_of_items = 5;
$loop_count = 0;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Limiting number of items displayed in mini cart
if($loop_count < $number_of_items) {
// The counter
$loop_count++;
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
$product_name = apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key );
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key );
$product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
$product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
?>
<li class="<?php echo esc_attr( apply_filters( 'woocommerce_mini_cart_item_class', 'mini_cart_item', $cart_item, $cart_item_key ) ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
'×',
esc_url( WC()->cart->get_remove_url( $cart_item_key ) ),
__( 'Remove this item', 'woocommerce' ),
esc_attr( $product_id ),
esc_attr( $_product->get_sku() )
), $cart_item_key );
?>
<?php if ( ! $_product->is_visible() ) : ?>
<?php echo str_replace( array( 'http:', 'https:' ), '', $thumbnail ) . $product_name . ' '; ?>
<?php else : ?>
<a href="<?php echo esc_url( $product_permalink ); ?>">
<?php echo str_replace( array( 'http:', 'https:' ), '', $thumbnail ) . $product_name . ' '; ?>
</a>
<?php endif; ?>
<?php echo WC()->cart->get_item_data( $cart_item ); ?>
<?php echo apply_filters( 'woocommerce_widget_cart_item_quantity', '<span class="quantity">' . sprintf( '%s × %s', $cart_item['quantity'], $product_price ) . '</span>', $cart_item, $cart_item_key ); ?>
</li>
<?php
}
}
}
?>
<?php else : ?>
<li class="empty"><?php _e( 'No products in the cart.', 'woocommerce' ); ?></li>
<?php endif; ?>
</ul><!-- end product list -->
<?php if ( ! WC()->cart->is_empty() ) : ?>
<p class="total"><strong><?php _e( 'Subtotal', 'woocommerce' ); ?>:</strong> <?php echo WC()->cart->get_cart_subtotal(); ?></p>
<?php do_action( 'woocommerce_widget_shopping_cart_before_buttons' ); ?>
<p class="buttons">
<?php _e( 'View Cart', 'woocommerce' ); ?>
<?php _e( 'Checkout', 'woocommerce' ); ?>
</p>
<?php endif; ?>
<?php do_action( 'woocommerce_after_mini_cart' ); ?>
Here we count the number of items in the foreach loop and we limit them to a specified number in variable $number_of_items.