Error adding standard WooCommerce product widget to blog post - php

I am using a code that changes the text and style of the Add to Cart button for a product added to the cart. (All CSS styles for my theme)
/* Change the text of the add to cart button for the archive and categories */
add_filter( 'woocommerce_product_add_to_cart_text', 'new_products_button_text', 20, 2 );
function new_products_button_text( $text, $product ) {
if(
$product->is_type( 'simple' )
&& $product->is_purchasable()
&& $product->is_in_stock()
&& WC()->cart->find_product_in_cart( WC()->cart->generate_cart_id( $product->get_id() ) )
) {
$text = 'Product in Cart';
}
return $text;
}
/* Change the add to cart button text for the product page */
add_filter( 'woocommerce_product_single_add_to_cart_text', 'new_single_product_button_text' );
function new_single_product_button_text( $text ) {
if( WC()->cart->find_product_in_cart( WC()->cart->generate_cart_id( get_the_ID() ) ) ) {
$text = 'Product in Cart';
}
return $text;
}
add_action( 'wp_footer', 'action_wp_footer' );
function action_wp_footer() {
?>
<script>
jQuery(document).ready(function($) {
var selector = '.add_to_cart_text:contains("Product in Cart")';
// Selector contains specific text
if ( $( selector ).length > 0 ) {
$( selector ).addClass( 'product-is-added' );
} else {
$( selector ).removeClass( 'product-is-added' );
}
});
</script>
<?php
}
add_action( 'wp_footer', 'ajax_button_text_js_script' );
function ajax_button_text_js_script() {
$text = __('Product in Cart', 'woocommerce');
?>
<script>
jQuery(function($) {
var text = '<?php echo $text; ?>', $this;
$(document.body).on('click', '.ajax_add_to_cart', function(event){
$this = $(this); // Get button jQuery Object and set it in a variable
});
$(document.body).on('added_to_cart', function(event,b,data){
var buttonText = '<span class="add_to_cart_text product-is-added">'+text+'</span><i class="cart-icon pe-7s-cart"></i>';
// Change inner button html (with text) and Change "data-tip" attribute value
$this.html(buttonText).attr('data-tip',text);
});
});
</script>
<?php
}
The code works correctly, but there was a problem with the blogs. In blog posts, I add a standard WooCommerce product widget. I use WPBakery Page Builder, the visual editor.
When publishing or saving a post with a standard WooCommerce widget, I get a critical error:
Fatal error: Uncaught Error: Call to a member function find_product_in_cart() on null in /public_html/wp-content/themes/functions.php:703 Stack trace: #0 /public_html/wp-includes/class-wp-hook.php(292): new_products_button_text('\xD0\x92 \xD0\xBA\xD0\xBE\xD1\x80\xD0\xB7\xD0\xB8\xD0\xBD...', Object(WC_Product_Simple)) #1 /public_html/wp-includes/plugin.php(212): WP_Hook->apply_filters('\xD0\x92 \xD0\xBA\xD0\xBE\xD1\x80\xD0\xB7\xD0\xB8\xD0\xBD...', Array) #2 /public_html/wp-content/plugins/woocommerce/includes/class-wc-product-simple.php(62): apply_filters('woocommerce_pro...', '\xD0\x92 \xD0\xBA\xD0\xBE\xD1\x80\xD0\xB7\xD0\xB8\xD0\xBD...', Object(WC_Product_Simple)) #3 /public_html/wp-content/themes/cores/nasa-woo-functions.php(393): WC_Product_Simple->add_to_cart_text() #4 /public_html/wp-content/themes/cores/nasa-woo- in /public_html/wp-content/themes/functions.php on line 703
Without a product widget, the post is published without any problems.
Line 703 is part of the above code:
&& WC()->cart->find_product_in_cart( WC()->cart->generate_cart_id( $product->get_id() ) )
As I understand it, the cart is not initialized in the admin panel, since it is not needed here.
How can this be fixed?
I will be glad for your help!

In exceptional cases or due to the combination of plugins/themes you can indeed run into error messages.
A solution for this could be to add multiple checks before executing the next piece of code, in this way you can prevent error messages.
For example, you can replace your existing new_products_button_text callback function with:
function new_products_button_text( $text, $product ) {
if ( is_admin() ) return $text;
if ( $product->is_type( 'simple' ) && $product->is_purchasable() && $product->is_in_stock() ) {
// WC Cart
if ( WC()->cart ) {
// Get cart
$cart = WC()->cart;
// If cart is NOT empty
if ( ! $cart->is_empty() ) {
// Find product in cart
if ( $cart->find_product_in_cart( $cart->generate_cart_id( $product->get_id() ) ) ) {
$text = 'Product in Cart';
}
}
}
}
return $text;
}
This can be written more compactly, but by spreading the conditions over multiple if statements, you can quickly determine where things are going wrong, if you still receive an error message.

Related

How do I display price of selected bundle products instead of "From $"?

I have a product consisting of a bundle of 5 products. All of these products are optional but checked on default. Therefore the main product is displayed as "From 900 kr." instead of displaying the price of the selected items. I am using WooCommerces own plugin called "WooCommerce Product Bundles".
picture of the product page
The total price is already shown underneath the bundle options but I want that to be showed as the main price area.
I've already succeeded with this on variable product types and thought i could use the same code with a few changes. Sadly I couldn't make it work.
The code I used for the variable products:
// Show selected variation price in main price area
add_action('woocommerce_before_add_to_cart_form', 'selected_variation_price_replace_variable_price_range');
function selected_variation_price_replace_variable_price_range(){
global $product;
if( $product->is_type('variable') ):
?><style> .woocommerce-variation-price {display:none;} </style>
<script>
jQuery(function($) {
var p = 'p.price'
q = $(p).html();
$('form.cart').on('show_variation', function( event, data ) {
if ( data.price_html ) {
$(p).html(data.price_html);
}
}).on('hide_variation', function( event ) {
$(p).html(q);
});
});
</script>
<?php
endif;
}
Does anyone know how I can achieve this?
EDIT:
I've hidden the main price on the product page and styled the bundle price to look like the main price. The problem now lies on the category page where the price doesn't display the price of the default selected variation of the bundles. It shows the max possible price for the product and not the default bundle selection price.
Here's the code from the bundle plugin that results in a max price display:
/**
* 'woocommerce_bundle_force_old_style_price_html' filter.
*
* Used to suppress the range-style display of bundle price html strings.
*
* #param boolean $force_suppress_range_format
* #param WC_Product_Bundle $this
*/
if ( $suppress_range_price_html || apply_filters( 'woocommerce_bundle_force_old_style_price_html', false, $this ) ) {
$price = wc_price( $price_max );
$regular_price_min = $this->get_bundle_regular_price( 'max', true );
if ( $regular_price_min !== $price_max ) {
$regular_price = wc_price( $regular_price_max );
if ( $price_min !== $price_max ) {
$price = sprintf( _x( '%1$s%2$s', 'Price range: from', 'woocommerce-product-bundles' ), wc_get_price_html_from_text(), wc_format_sale_price( $regular_price, $price ) . $this->get_price_suffix() );
} else {
$price = wc_format_sale_price( $regular_price, $price ) . $this->get_price_suffix();
}
Since the price displays and effectively changes upon variation change within the product page that code must also be available to use for the shop loop I just can't seem to find it.
Right now you are only checking for product type variable. You have to check for bundle as well.
Simply you have to add more conidition with or $product->is_type('bundle')
Check the below code.
add_action('woocommerce_before_add_to_cart_form', 'selected_variation_price_replace_variable_price_range');
function selected_variation_price_replace_variable_price_range(){
global $product;
if( $product->is_type('variable') || $product->is_type('bundle') ):
?><style> .woocommerce-variation-price {display:none;} </style>
<script>
jQuery(function($) {
var p = 'p.price'
q = $(p).html();
$('form.cart').on('show_variation', function( event, data ) {
if ( data.price_html ) {
$(p).html(data.price_html);
}
}).on('hide_variation', function( event ) {
$(p).html(q);
});
});
</script>
<?php
endif;
UPDATE: try the below code. code will go in your active theme function.php file.
function override_woocommerce_bundle_default_variation_price_html( $price, $product ) {
if( $product->is_type('variable') || $product->is_type('bundle') ){
$bundled_items = $product->get_bundled_items();
if ( ! empty( $bundled_items ) ) {
foreach ( $bundled_items as $bundled_item ) {
foreach( $bundled_item->product->get_available_variations() as $pav ){
$def = true;
foreach( $bundled_item->product->get_variation_default_attributes() as $defkey => $defval ){
if( $pav['attributes']['attribute_'.$defkey] != $defval){
$def = false;
}
}
if( $def ){
$price = $pav['display_price'];
}
}
}
}
return wc_price($price);
}
return $price;
}
add_filter( 'woocommerce_get_price_html', 'override_woocommerce_bundle_default_variation_price_html', 10, 2 );

Add a body class for specific chosen shipping method in WooCommerce checkout

Based on Add a body class for specific selected shipping option in Woocommerce checkout answer code, I have been using the following code snippet for some time:
add_filter( 'wp_footer','weekend_selected' );
function weekend_selected(){
if( ! is_page( 8 ) ) return; // only on checkout
$method_id = "''";
// Find the Shipping Method ID for the Shipping Method label name 'Delivery price on request'
foreach( WC()->session->get('shipping_for_package_0')['rates'] as $rate_key => $rate ){
if( 'Saturdays DPD' == $rate->label ){
$method_id = $rate_key;
break;
}
}
?>
<script type="text/javascript">
(function($){
// variables initialization
var a = 'input[name^="shipping_method[0]"]',
b = a+':checked',
c = 'weekend-selected',
d = '<?php echo $method_id; ?>';
if( $(b).val() == d )
$('body').addClass(c);
else
$('body').removeClass(c);
$( 'form.checkout' ).on( 'change', a, function() {
if( $(b).val() == d )
$('body').addClass(c);
else
$('body').removeClass(c);
});
})(jQuery);
</script>
<?php
}
However I have noticed that it does not trigger anymore on page load, I have to refresh the page for it to trigger which is making it useless.
The use case is basically to load a body tag (weekend-selected) which then hides (with CSS) certain days on a calendar.
First I thought it was because the shipping options were not loading before an address was added so I prefilled required address fields to see if this was the problem but the same thing happens, I have to refresh the page for the code to trigger.
Try the following simple and light way (without using Jquery):
add_filter( 'body_class', 'add_shipping_classes_to_body_class' );
function add_shipping_classes_to_body_class( $classes ) {
// only on checkout page
if( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
return;
$chosen_method = WC()->session->get('chosen_shipping_methods')[0];
$shipping_rates = WC()->session->get('shipping_for_package_0')['rates'];
if( 'Saturdays DPD' === $shipping_rates[$chosen_method]->label ){
$classes[] = 'weekend-selected';
}
return $classes;
}
Code goes in functions.php file of the active child theme (or active theme). It should better works.
Related: Add shipping class to body class on WooCommerce checkout page

Change single add to cart text when product is out of stock in WooCommerce

In Woocommerce I am trying to change the add to cart text from "Add to cart" to "Out of stock" on single product pages when the product is out of stock, for simple products and the product variations on variable products.
We are currently not using any sort of stock management because all of our products are hand made & we do set stock status manually.
Is there any possible way to achieve that?
For all products except variable products, is quite simple… But for variable products and it's variations, it requires some more code and the usage of javascript/jQuery.
So the following code will change add to cart text to "Out of stock" when the current product is out of stock even for selected product variations on variable products:
// For all products except variable product
add_filter( 'woocommerce_product_single_add_to_cart_text', 'product_single_add_to_cart_text_filter_callback', 20, 2 );
function product_single_add_to_cart_text_filter_callback( $button_text, $product ) {
if( ! $product->is_in_stock() && ! $product->is_type('variable') ) {
$button_text = __("Out of stock", "woocommerce");
}
return $button_text;
}
// For all product variations (on a variable product)
add_action( 'woocommerce_after_add_to_cart_button', 'after_add_to_cart_button_action_callback', 0 );
function after_add_to_cart_button_action_callback() {
global $product;
if( $product->is_type('variable') ) :
$data = [];
// Loop through variation Ids
foreach( $product->get_visible_children() as $variation_id ){
$variation = wc_get_product( $variation_id );
$data[$variation_id] = $variation->is_in_stock();
}
$outofstock_text = __("Out of Stock", "woocommerce");
?>
<script type="text/javascript">
jQuery(function($){
var b = 'button.single_add_to_cart_button',
t = $(b).text();
$('form.variations_form').on('show_variation hide_variation found_variation', function(){
$.each(<?php echo json_encode($data); ?>, function(j, r){
var i = $('input[name="variation_id"]').val();
if(j == i && i != 0 && !r ) {
$(b).html('<?php echo $outofstock_text; ?>');
return false;
} else {
$(b).html(t);
}
});
});
});
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Tried to write as comment but seems code formatting not working, so writing as an answer.
For a single variation add to cart shortcode this hack will help.
// For variation product
add_filter( 'woocommerce_product_add_to_cart_text', 'product_variation_add_to_cart_text_filter_callback', 20, 2 );
function product_variation_add_to_cart_text_filter_callback( $button_text, $product ) {
if ( ! $product->is_in_stock() && $product->is_type( 'variation' ) ) {
$button_text = __( "Out of stock", "woocommerce" );
}
return $button_text;
}

Change WooCommerce Product Name to Include Attribute When Attribute Selected

I've referenced the answer from this question which works perfectly!
But I am displaying a product on the homepage using a woocommerce shortcode and can't figure out how to adjust so it also applies to the homepage.
I tried adjusting the line that selects only product pages to include the homepage, but it does not work:
// Only single product pages
if( ! is_product() && ! is_home() ) return $title;
Any advice would be appreciated!
This only work on variable products that have a Color product attribute (for variations) and it will append the selected Color attribute label value to the product title.
For your Home page you will use WordPress conditional tag is_front_page()…
You will have to set also the Product ID that you are using in your shortcode.
1) In the Home page I have this shortcode example (with a variable product ID):
[product_page id="40"]
2) To make it work for both single product pages and the home page (product shortcode) too:
add_filter( 'wp_footer','custom_product_title_script' );
function custom_product_title_script(){
// Only single product pages and home page
if( ! ( is_product() || is_front_page() ) ) return;
// HERE Set your home product ID
$shortcode_product_id = 40; // <===== ===== ===== HERE your Shortcode Product ID
$product_id = is_product() ? get_the_id() : $shortcode_product_id;
// get an instance of the WC_Product Object
$product = wc_get_product( $product_id );
// Only for variable products
if( ! $product->is_type( 'variable' ) ) return;
// Here set your specific product attributes in this array (coma separated):
$attributes = array('pa_color');
// The 1st loop for variations IDs
foreach($product->get_visible_children( ) as $variation_id ) {
// The 2nd loop for attribute(s)/value
foreach($product->get_available_variation( $variation_id )['attributes'] as $key => $value_id ){
$taxonomy = str_replace( 'attribute_', '', $key ); // Get the taxonomy of the product attribute
// Just for defined attributes
if( in_array( $taxonomy, $attributes) ){
// Set and structure data in an array( variation ID => product attribute => term name )
$data[ $variation_id ][$taxonomy] = get_term_by( 'slug', $value_id, $taxonomy )->name;
}
}
}
?>
<script type="text/javascript">
(function($){
// variables initialization
var variationsData = <?php echo json_encode($data); ?>,
productTitle = $('.product_title').text(),
color = 'pa_color';
// function that get the selected variation and change title
function update_the_title( productTitle, variationsData, color ){
$.each( variationsData, function( index, value ){
if( index == $('input.variation_id').val() ){
$('.product_title').text(productTitle+' - '+value[color]);
return false;
} else {
$('.product_title').text(productTitle);
}
});
}
// Once all loaded
setTimeout(function(){
update_the_title( productTitle, variationsData, color );
}, 300);
// On live event: select fields
$('select').blur( function(){
update_the_title( productTitle, variationsData, color );
});
})(jQuery);
</script>
<?php
}
This code goes in any plugin file with a constructor (but not in function.php file).
Tested and works.

Rename Add to Cart buttons for Out Of Stock Products in WooCommerce 3

I would like to rename 'Add to Cart' to 'Sold Out' when product is sold out in WooCommerce.
This is my code:
add_filter( 'add_to_cart_text', 'woo_custom_single_add_to_cart_text' );
add_filter( 'woocommerce_product_single_add_to_cart_text',
'woo_custom_single_add_to_cart_text' ); // 2.1 +
function woo_custom_single_add_to_cart_text() {
if( $product->get_stock_quantity() == 0 )
return __( 'Sold Out', 'woocommerce' );
} else {
return __( 'Add to cart ', 'woocommerce' );
}
But it's not working.
What I am doing wrong please?
UPDATE (Working now with variable products and variations)
There is some errors in your code:
1.You need to to set the hooks arguments in your function as $button_text and $product.
2.The add_to_cart_text is deprecated and replaced by woocommerce_product_add_to_cart_text.
In this updated code I have added a jQuery script that catch the selected variation for variable products only (on single product pages) and to change the button text. Now this is working for all cases.
add_filter( 'woocommerce_product_add_to_cart_text', 'customizing_add_to_cart_button_text', 10, 2 );
add_filter( 'woocommerce_product_single_add_to_cart_text', 'customizing_add_to_cart_button_text', 10, 2 );
function customizing_add_to_cart_button_text( $button_text, $product ) {
$sold_out = __( "Sold Out", "woocommerce" );
$availability = $product->get_availability();
$stock_status = $availability['class'];
// Only for variable products on single product pages
if ( $product->is_type('variable') && is_product() )
{
?>
<script>
jQuery(document).ready(function($) {
$('select').blur( function(){
if( '' != $('input.variation_id').val() && $('p.stock').hasClass('out-of-stock') )
$('button.single_add_to_cart_button').html('<?php echo $sold_out; ?>');
else
$('button.single_add_to_cart_button').html('<?php echo $button_text; ?>');
console.log($('input.variation_id').val());
});
});
</script>
<?php
}
// For all other cases (not a variable product on single product pages)
elseif ( ! $product->is_type('variable') && ! is_product() )
{
if($stock_status == 'out-of-stock')
$button_text = $sold_out.' ('.$stock_status.')';
else
$button_text.=' ('.$stock_status.')';
}
return $button_text;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works
It looks like you forgot to declare $product as a global.
Your filter function should start with global $product;

Categories