Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Woocommerce: Invalid argument supplied for foreach() in class-wc-product-variable.php file

I'm creating a type of variable product customized for my theme, I've managed to appear in the dropdown of product data and that can be saved without causing conflict. The problem is that seeing the product in frontend gives me the following error:

Warning: Invalid argument supplied for foreach() in /Applications/MAMP/htdocs/canopy/wp-content/plugins/Woocommerce/includes/class-wc-product-variable.php on line 83

I have traced the problem to the file Wordpress/includes/data-stores/class-wc-product-variable-data-store-cpt.php but the truth is that I do not know what else to do.

I leave the code that I wrote for this:

WC_Product_variable_canopytour.php

class WC_Product_Variable_CanopyTour extends WC_Product_Variable {
    public function __construct( $product ) {
        $this->product_type = 'variable_canopytour';
        parent::__construct( $product );
    }

    public function get_type() {
        return 'variable_canopytour';
    }
}

class-woocommerce-custom-product.php

class WCB_Woocommerce_CanopyTour_Product_Type {
    private $wcb;
    private $version;

    public function __construct( $wcb, $version ) {
        $this->wcb = $wcb;
        $this->version = $version;
    }

    public function register_canopytour_product_type() {
        remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );

        include_once(get_template_directory() . 'woocommerce/WC_Product_variable_canopytour.php');
    }

    public function add_canopytour_product( $types ) {
        $types[ 'variable_canopytour' ] = __( 'Variable Canopy Tour', $this->wcb );
        return $types;
    }

    public function get_tour_product_class($classname, $product_type) {
        if ( $product_type === "variable_canopytour" ) {
            $classname = 'WC_Product_Variable_CanopyTour';
        }
        return $classname;
    }

    public function wcb_admin_footer() {
        if ( 'product' != get_post_type() ) :
            return;
        endif;

        ?><script type='text/javascript'>
            jQuery( document ).ready( function() {
                jQuery( '.options_group.pricing' ).addClass( 'show_if_variable_canopytour show_if_simple show_if_external' ).show();
                jQuery( 'li.general_options.general_tab' ).addClass( 'show_if_variable_canopytour show_if_simple show_if_external' ).show();
                jQuery( '.variations_options.variations_tab' ).addClass( 'show_if_variable_canopytour' ).show();
                jQuery( '.enable_variation' ).addClass( 'show_if_variable_canopytour' ).show();
            });
        </script><?php
    }

    function hide_wcb_data_panel( $tabs) {
        // Other default values for 'attribute' are; general, inventory, shipping, linked_product, variations, advanced
        $tabs['shipping']['class'][] = 'hide_if_variable_canopytour';
        return $tabs;
    }
}

functions.php

include_once get_template_directory() . 'includes/woocommerce/class-woocommerce-custom-product.php';
$woo_ct = new WCB_Woocommerce_CanopyTour_Product_Type( "wcb", "1.0" );

add_action( 'init', array($woo_ct, 'register_canopytour_product_type') );
add_filter( 'product_type_selector', array($woo_ct, 'add_canopytour_product') );
add_filter( 'woocommerce_product_class', array($woo_ct, 'get_tour_product_class'), 10, 2 ); 
add_action( 'admin_head', array($woo_ct, 'wcb_admin_head') );
add_filter( 'woocommerce_product_data_tabs', array($woo_ct, 'hide_wcb_data_panel') );

I hope someone can help me understand what happens :(

Thank you!

like image 912
Jesús Magallón Avatar asked Oct 04 '18 19:10

Jesús Magallón


1 Answers

The Problem

You got that warning because the read_price_data() method is (as of writing) only available in the WC_Product_Variable_Data_Store_CPT class and not the default one that's used for custom product type, which is WC_Product_Data_Store_CPT.

However, despite the default class doesn't have the read_price_data() method, calling it from the class's instance doesn't result in an exception/error because that class is instantiated through the WC_Data_Store class, which has the magic method __call().

// Case 1: In WC_Product_Variable::get_variation_prices().
// $this->data_store is an instance of WC_Product_Variable_Data_Store_CPT
$prices = $this->data_store->read_price_data( $this, $for_display ); // array

// Case 2: In WC_Product_Variable_CanopyTour::get_variation_prices().
// $this->data_store is an instance of WC_Product_Data_Store_CPT
$prices = $this->data_store->read_price_data( $this, $for_display ); // null

But since the read_price_data() method is expected to return an array and yet it doesn't (or that it returns null), that's why PHP throws the "Invalid argument supplied for foreach() ..." warning:

// The following code throws a warning.
foreach ( null as $key => $value ) {
    ...
}

The Solution

You need to use the proper data store class for your custom (variable) product type.

I.e. Add an item to the WC_Data_Store::$stores array, where the key is the (variable) product type prefixed with product- (so in your case, it would be product-variable_canopytour), and the value is the class that handles the data store (e.g. WC_Product_Variable_Data_Store_CPT as in the below example).

You can do so via the woocommerce_data_stores filter, like so: (add to the active theme's functions.php file)

add_filter( 'woocommerce_data_stores', function( $stores ){
    $stores['product-variable_canopytour'] = 'WC_Product_Variable_Data_Store_CPT';
    return $stores;
} );

Additional Note

The constructor for WC_Product_Variable_CanopyTour should be defined the same way the parent class defines it, where the $product is optional and that it defaults to zero (0), like so:

class WC_Product_Variable_CanopyTour extends WC_Product_Variable {
    public function __construct( $product = 0 ) {
        $this->product_type = 'variable_canopytour';
        parent::__construct( $product );
    }

    ...
}
like image 106
Sally CJ Avatar answered Oct 02 '22 22:10

Sally CJ