Move the variation description to the Woocommerce product variable description - php

What I am trying: Show the Variation Description of the Child of a Variable Product on single product page in the product tab description area.
In WooCommerce and variable products the parent has the description which is stored in wp_posts->post_content and every child has an additional _variation_description field. On the frontent the description of the parent is always visible and the _variation_description of the Child is showing as a variation is selected. It gets updated with js and is displayed before the add to cart button together with the price of the variation.
The description is called in description.php with the code
the_content();
which outputs the parent products post_content db field.
The variation description is called in variation.php with the code
<script type="text/template" id="tmpl-variation-template">
<div class="woocommerce-variation-description">{{{ data.variation.variation_description }}}</div>
</script>
As I understand this works with wp.template and I also checked the info on Javascript Reference wp.template
The js is in add-to-cart-variation.js and the template is built with
template = wp.template( 'variation-template' );
Then the data can be updated which I do not really understand in detail. What I understand is that all the js calls are in the form,
form.$singleVariation.html( $template_html );
so then I realized why I couldn't just copy the code from variation.php and paste it to description.php because there it would be outside the form tag.
On the other hand the data for SKU is not called from within the form, it is called through .product_meta so if I put a div with class="product_meta" I can call the sku and have it updated with js from anywhere. For example I can put this code into description.php
<div class="product_meta">
<?php if ( wc_product_sku_enabled() && ( $product->get_sku() || $product->is_type( 'variable' ) ) ) : ?>
<?php if ( $sku = $product->get_sku() ) : ?>
<span class="sku_wrapper"><?php esc_html_e( 'SKU:', 'woocommerce' ); ?>
<span class="sku" itemprop="sku"><?php echo $sku; ?></span></span>
<?php endif; ?>
<?php endif; ?>
</div>
and it works fine.
So here I am stuck. I just want to have the variation description displayed dynamically in the description.php and I thought if I understand how the wp.template works in add-to-cart-variation.js I could figure it out. But I can't. I do not exactly understand how the content is being updated in the variation form and what the difference is to the data in SKU.
Maybe there is a totally simple and easy solution for what I want, and maybe my approach is too complicated. I would be very glad for any Ideas or hints or just pointing in the right direction.

This can be done with some jQuery code, where we will copy the variation description to the variable product description tab. But first you need to make some very small changes on some templates.
Here is the official docs related to: Template structure & Overriding templates via a theme
The templates changes and the necessary code:
On single-product/tabs/description.php template file, you will replace the line:
<?php the_content(); ?>
by the following line (We just add a surrounding html <div> tag with a selector class):
<div class="product-post-content"><?php the_content(); ?></div>
On single-product/add-to-cart/variation.php template file, you will replace the line:
<div class="woocommerce-variation-description">{{{ data.variation.variation_description }}}</div>
by the following line (we just hide the variation description):
<div class="woocommerce-variation-description" style="display:none;">{{{ data.variation.variation_description }}}</div>
Now the jQuery (commented) code (on single product pages for variable products only):
add_action( 'wp_footer', 'move_variation_description' );
function move_variation_description(){
global $product;
// Only on single product pages for variable products
if ( ! ( is_product() && $product->is_type('variable') ) ) return;
// jQuery code
?>
<script type="text/javascript">
jQuery(function($){
a = '.woocommerce-variation-description', b = a+' p', c = 'input.variation_id',
d = '#tab-description .product-post-content', de = $(d).html();
// On load, adding a mandatory very small delay
setTimeout(function(){
// variation ID selected by default
if( '' != $(c).val() && $(a).text() != '' )
$(d).html($(a).html());
}, 300);
// On live event (attribute select fields change)
$('table.variations select').on( 'blur', function(){
// variation ID is selected
if( '' != $(c).val() && $(a).text() != '' ){
$(d).html($(a).html()); // We copy the variation description
}
// No variation ID selected
else {
$(d).html($(a).html()); // We set back the variable product description
}
console.log($('input.variation_id').val()); // TEST: Selected variation ID … To be removed
});
});
</script>
<?php
}
This code goes on function.php file of your active child theme (or active theme).
Tested and works.
Other related threads:
Replace the Variable Price range by the chosen variation price in WooCommerce 3
Get the selected product variation ID in WooCommerce variable products single pages
Variable product selectors: Getting the live selected values

Related

Get some product data in the body for WooCommerce Single product pages

I'm trying to pull the Product ID for the product being viewed in order to pass that ID into TrustPilot's widget to display reviews for that product.
I've tried creating a Snippet with this code:
global $product;
$id = $product->get_id()
But I got an error and - I'll be honest - I don't really understand what I was doing! I've been browsing for solutions for most of the day, so I'm hoping someone might be kind enough to help!
As mentioned, the aim is to be able to populate the data-sku="xxxxxxxx" field in a TrustPilot "TrustBox" - a javascript tool that pulls & presents reviews from trustpilot. The ID will need to be updated in the HTML for the element, I think.
The whole of the TrustPilot code is shown below:
Add this to Header
<!-- TrustBox script -->
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
<!-- End TrustBox script -->
Add this to body, where you want the widget to appear
<!-- TrustBox widget - Product Reviews SEO -->
<div class="trustpilot-widget" data-locale="en-GB" data-template-id="5717796816f630043868e2e8" data-businessunit-id="5d472f87ff988700011c73b8" data-style-height="700px" data-style-width="100%" data-theme="light" data-sku="487" data-name="10 Plantshake Pack" data-review-languages="en">
Trustpilot
</div>
<!-- End TrustBox widget -->
Updated
To get the product ID in single product pages, you can use: $product_id = get_the_ID();
To be sure to get the product object in single product pages, you can use:
$product = wc_get_product( get_the_ID() );
Or even better:
global $product;
if ( ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( get_the_ID() );
}
Now you can use a hooked function, to insert all the related code in single product pages. Your javscript will be populated dynamically with the product name and sku:
add_action('woocommerce_before_single_product', 'action_before_single_product', 5 );
function action_before_single_product(){
global $product;
$id = $product->get_id(); // Product Id
$sku = $product->get_sku(); // Product sku
$name = $product->get_name(); // Product name
?>
<!-- TrustBox widget - Product Reviews SEO -->
<div class="trustpilot-widget" data-locale="en-GB" data-template-id="5717796816f630043868e2e8" data-businessunit-id="5d472f87ff988700011c73b8" data-style-height="700px" data-style-width="100%" data-theme="light" data-sku="<?php echo $sku; ?>" data-name="<?php echo $name; ?>" data-review-languages="en">
Trustpilot
</div>
<!-- End TrustBox widget -->
<?php
}
Code goes in functions.php file of the active child theme (or active theme). It should work.

Set the product type to "variable" by default in admin new product pages

I'm creating a WooCommerce store with variable products only. When creating new products I always need to manually change the Product Data to Variable Product. When you have hundreds of products, this is becoming kind of a pain :)
I've searched around the Internet but wasn't able to find anything...
<php?
// Code here
?>
I'm looking for a PHP snippet to set Variable Product as default when creating new products, any ideas?
Updated - The following code will select "variable" by default on the product type selector in backend for new product pages:
add_action( 'admin_footer', 'product_type_selector_filter_callback' );
function product_type_selector_filter_callback() {
global $pagenow, $post_type;
if( $pagenow === 'post-new.php' && $post_type === 'product' ) :
?>
<script>
jQuery(function($){
$('select#product-type').val('variable').change();
});
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Output custom code on a defined sub category archive pages only in WooCommerce

Actually In WooCommerce, I am trying to display some custom code (a video), only in a defined product sub-category archive page.
This is the code I am using:
<?php if( has_term('viola-chan','product_cat' $tag_ID=18) ) { ?>
my video html code
<?php } ?>
But it doesn't work and it is showing up on other parent pages too.
Here is a live link
How can I output custom code on a defined sub category archive pages only in WooCommerce?
As this is related to display something on product category archives pages, you should use instead:
<?php if( is_product_category( 'viola-chan' ) ) { ?>
<div>my video html code here</div>
<?php } ?>
But "viola-chan" should be your sub-category here…
Related documentation: WooCommerce Conditional Tags: Product category page
Try third variable post id as shown below:
global $post;
if(has_term( 'viola-chan','product_cat', $post->ID )) {
//do something if post has viola-chan term
}else{
//do something else if the post don't have viola-chan term
}
Ref.- https://wordpress.stackexchange.com/questions/166576/if-product-is-in-sub-category-show-code

WooCommerce "CONTACT" button when out of stock

Trying to achieve something that should be simple, but I've tried 3 approaches with multiple code variations and I just can't make it work. I'm trying to create a button that will appear in place of the "ADD TO CART" button on single product pages when the item is out of stock. Clicking the button will fire a popup contact form.
Is creating an add action in functions the right way to go, or should I replace the normal button with an if statement? I've tried both, so help with coding either would be greatly appreciated.
You can either hook into woocommerce_loop_add_to_cart_args using a filter in your functions.php or edit the template file directly by pulling it into your theme. Either way will require a bit of PHP.
If doing it in your functions.php, it would look something like this (untested but should send you down the right path):
<?php
add_filter( 'woocommerce_loop_add_to_cart_link', 'my_out_of_stock_button' );
function my_out_of_stock_button( $args ){
global $product;
if( $product && !$product->is_in_stock() ){
return 'Contact us';
}
return $args;
}
I don't know what your button code should actually look like or what other information you need to capture, but this is how you could override the "Add to Cart" button and replace it if out of stock.
UPDATE
LoicTheAztec brought up a great point - the filter provided only affects the button on the archive, category, tag overview pages - not the individual product pages. There are no hooks for the individual product page buttons BUT you can copy the templates to your theme and override them.
You'll want to look at the files in templates/single-product/add-to-cart. Use a similar if statement as above:
#simple.php
<?php if ( $product->is_in_stock() ) : ?>
// Standard WooCommerce code
<?php else: ?>
// Your button code
<?php endif; ?>
Just add below code in functions.php file of your enabled theme reference
add_action('woocommerce_after_shop_loop_item', 'themelocation_change_outofstock_to_contact_us', 1);
// for shop page
function themelocation_change_outofstock_to_contact_us() {
global $product;
if (!$product->is_in_stock()) {
remove_action('woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart');
remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart');
//change the link to your contact us page
echo ' Contact Us ';
}
}
// for single page
add_filter('woocommerce_get_availability', 'wcs_custom_get_availability', 1, 2);
function wcs_custom_get_availability($availability, $_product) {
// Change In Stock Text
if ($_product->is_in_stock()) {
$availability['availability'] = __('Available!', 'woocommerce');
}
// Change Out of Stock Text
if (!$_product->is_in_stock()) {
$availability['availability'] = __(' Contact Us ', 'woocommerce');
}
return $availability;
}
I was looking for a way to show a contact button on bespoke products and
#ahwychkchih solution works great. One issue I had though is that schema markup will show as out of stock for those products which is not the case for beskpoke products is just they can't be purchased straight away so I've added this to force in_stock markup for my products. I'm aware that this solution would affect all products so you can always add a product id filter if needed
// Force In Stock schema markup
function fix_my_product_offers_schema ($markup_offer, $product) {
if (!$product->is_in_stock()) {
$markup_offer['availability'] = 'https://schema.org/InStock';
}
return $markup_offer;
}
add_filter('woocommerce_structured_data_product_offer', 'fix_my_product_offers_schema', 1, 2);

How to remove class using jQuery when the value is called from a custom field in WooCommerce

I am building a custom field for a user to allow custom sales badge in a WooCommerce powered site. The field is created and the value is also being echoed on the shop archive page, displaying the user input sales-badge value. But there is one problem. The default sale badge is also showing. How I can remove this?
This is how I am echoing the value
add_action('woocommerce_before_shop_loop_item', 'ac_wc_show_custom_fields', 20);
function ac_wc_show_custom_fields(){
global $post;
if (get_post_meta( $post->ID, 'ac_custom_badge', true )!==''){
echo '<div class="ac-loop-cus-badge">' .get_post_meta( $post->ID, 'ac_custom_badge', true ) . '</div>';
}
}
I then tried this and it's working but it's adding the property display:none to all .onsale class in the site
function ac_wc_show_custom_fields(){
global $post;
if (get_post_meta( $post->ID, 'ac_custom_badge', true )!==''){
echo '<div class="ac-loop-cus-badge">' .get_post_meta( $post->ID, 'ac_custom_badge', true ) . '</div>';
echo '<style>.onsale{display:none;}</style>';
}
}
I just want to apply the css or any other jQuery method to remove the .onsale span/class only when the custom field of sales badge retrieved. Hope I made sense.
Thanks
Addendum
I also tried to override the sales template using this function
add_action( 'woocommerce_before_shop_loop_item_title', 'custom_woocommerce_show_product_loop_sale_flash', 10 );
function custom_woocommerce_show_product_loop_sale_flash() {
global $post;
if (get_post_meta($post->ID, 'ac_custom_badge', true)) {
wc_get_template( 'files/sale-flash.php' );
}
}
But I end up with two markeups for the sales. Please see the image
For doing this in JavaScript (w/ jQuery), if you run this at the bottom of the page, I think you'll get what you are after.
<script type="text/javascript">
$(".ac-loop-cus-badge").removeClass("onsale");
</script>
Documentation for what how all this works can be found at removeClass.
If you need to apply this only to one page, or container div, you could add a class to your shop archive page. This would prevent it from removing the onsale class for other pages or divs.
<div class="shop-archive-page">
<!-- page contents do here-->
<img class="ac-loop-cus-badge"></img>
<!-- more page contents do here-->
</div>
<script type="text/javascript">
$(".shop-archive-page .ac-loop-cus-badge").removeClass("onsale");
</script>
UPDATE
For an example using :contains where Product A is in the DOM below its product image you could use the following.
$(".image-container span:contains('Product A')").parent().find(".ac-loop-cus-badge").removeClass("onsale");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="image-container">
<i class="ac-loop-cus-badge onsale">IMAGE WOULD BE HERE</i>
<br />
<span class="product-description">This is Product A, it's on sale right now!</span>
</div>

Categories