parent_product_type = $this->product_type; $this->product_type = 'variable-subscription'; // Load all meta fields $this->product_custom_fields = get_post_meta( $this->id ); // Convert selected subscription meta fields for easy access if ( ! empty( $this->product_custom_fields['_subscription_price'][0] ) ) { $this->subscription_price = $this->product_custom_fields['_subscription_price'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_sign_up_fee'][0] ) ) { $this->subscription_sign_up_fee = $this->product_custom_fields['_subscription_sign_up_fee'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_period'][0] ) ) { $this->subscription_period = $this->product_custom_fields['_subscription_period'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_period_interval'][0] ) ) { $this->subscription_period_interval = $this->product_custom_fields['_subscription_period_interval'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_length'][0] ) ) { $this->subscription_length = $this->product_custom_fields['_subscription_length'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_trial_length'][0] ) ) { $this->subscription_trial_length = $this->product_custom_fields['_subscription_trial_length'][0]; } if ( ! empty( $this->product_custom_fields['_subscription_trial_period'][0] ) ) { $this->subscription_trial_period = $this->product_custom_fields['_subscription_trial_period'][0]; } $this->subscription_payment_sync_date = 0; $this->subscription_one_time_shipping = ( ! isset( $this->product_custom_fields['_subscription_one_time_shipping'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_one_time_shipping'][0]; $this->subscription_limit = ( ! isset( $this->product_custom_fields['_subscription_limit'][0] ) ) ? 'no' : $this->product_custom_fields['_subscription_limit'][0]; } /** * Get the min or max variation (active) price. * * This is a copy of WooCommerce < 2.4's get_variation_price() method, because 2.4.0 introduced a new * transient caching system which assumes asort() on prices yields correct results for min/max prices * (which it does for prices alone, but that's not the full story for subscription prices). Unfortunately, * the new caching system is also hard to hook into so we'll just use the old system instead as the * @see self::variable_product_sync() uses the old method also. * * @param string $min_or_max - min or max * @param boolean $display Whether the value is going to be displayed * @return string */ public function get_variation_price( $min_or_max = 'min', $display = false ) { $variation_id = get_post_meta( $this->id, '_' . $min_or_max . '_price_variation_id', true ); if ( $display ) { if ( $variation = wc_get_product( $variation_id ) ) { if ( 'incl' == get_option( 'woocommerce_tax_display_shop' ) ) { $price = wcs_get_price_including_tax( $variation ); } else { $price = wcs_get_price_excluding_tax( $variation ); } } else { $price = ''; } } else { $price = get_post_meta( $variation_id, '_price', true ); } return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display ); } /** * Get an array of all sale and regular prices from all variations re-ordered after WC has done a standard sort, to reflect subscription terms. * The first and last element for each price type is the least and most expensive, respectively. * * @see WC_Product_Variable::get_variation_prices() * @param bool $include_taxes Should taxes be included in the prices. * @return array() Array of RAW prices, regular prices, and sale prices with keys set to variation ID. * @since 2.2.0 */ public function get_variation_prices( $display = false ) { $price_hash = $this->get_price_hash( $this, $display ); $this->prices_array[ $price_hash ] = parent::get_variation_prices( $display ); $children = array_keys( $this->prices_array[ $price_hash ]['price'] ); sort( $children ); $min_max_data = wcs_get_min_max_variation_data( $this, $children ); $min_variation_id = $min_max_data['min']['variation_id']; $max_variation_id = $min_max_data['max']['variation_id']; // Reorder the variable price arrays to reflect the min and max values so that WooCommerce will find them in the correct order foreach ( $this->prices_array as $price_hash => $prices ) { // Loop over sale_price, regular_price & price values to update them on main array foreach ( $prices as $price_key => $variation_prices ) { $min_price = $prices[ $price_key ][ $min_variation_id ]; $max_price = $prices[ $price_key ][ $max_variation_id ]; unset( $prices[ $price_key ][ $min_variation_id ] ); unset( $prices[ $price_key ][ $max_variation_id ] ); // append the minimum variation and prepend the maximum variation $prices[ $price_key ] = array( $min_variation_id => $min_price ) + $prices[ $price_key ]; $prices[ $price_key ] += array( $max_variation_id => $max_price ); $this->prices_array[ $price_hash ][ $price_key ] = $prices[ $price_key ]; } } $this->subscription_price = $min_max_data['min']['price']; $this->subscription_period = $min_max_data['min']['period']; $this->subscription_period_interval = $min_max_data['min']['interval']; $this->max_variation_price = $min_max_data['max']['price']; $this->max_variation_period = $min_max_data['max']['period']; $this->max_variation_period_interval = $min_max_data['max']['interval']; $this->min_variation_price = $min_max_data['min']['price']; $this->min_variation_regular_price = $min_max_data['min']['regular_price']; return $this->prices_array[ $price_hash ]; } /** * Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters. * DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique. * * @since 2.2.0 * @param WC_Product * @param bool $display Are prices for display? If so, taxes will be calculated. * @return string */ protected function get_price_hash( $display = false ) { global $wp_filter; if ( $display ) { $price_hash = array( get_option( 'woocommerce_tax_display_shop', 'excl' ), WC_Tax::get_rates() ); } else { $price_hash = array( false ); } $filter_names = array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ); foreach ( $filter_names as $filter_name ) { if ( ! empty( $wp_filter[ $filter_name ] ) ) { $price_hash[ $filter_name ] = array(); foreach ( $wp_filter[ $filter_name ] as $priority => $callbacks ) { $price_hash[ $filter_name ][] = array_values( wp_list_pluck( $callbacks, 'function' ) ); } } } $price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $display ) ) ); return $price_hash; } /** * Sync variable product prices with the childs lowest/highest prices. * * @access public * @return void */ public function variable_product_sync( $product_id = '' ) { WC_Product_Variable::variable_product_sync( $product_id ); $child_variation_ids = $this->get_children( true ); if ( $child_variation_ids ) { $min_max_data = wcs_get_min_max_variation_data( $this, $child_variation_ids ); update_post_meta( $this->id, '_min_price_variation_id', $min_max_data['min']['variation_id'] ); update_post_meta( $this->id, '_max_price_variation_id', $min_max_data['max']['variation_id'] ); update_post_meta( $this->id, '_price', $min_max_data['min']['price'] ); update_post_meta( $this->id, '_min_variation_price', $min_max_data['min']['price'] ); update_post_meta( $this->id, '_max_variation_price', $min_max_data['max']['price'] ); update_post_meta( $this->id, '_min_variation_regular_price', $min_max_data['min']['regular_price'] ); update_post_meta( $this->id, '_max_variation_regular_price', $min_max_data['max']['regular_price'] ); update_post_meta( $this->id, '_min_variation_sale_price', $min_max_data['min']['sale_price'] ); update_post_meta( $this->id, '_max_variation_sale_price', $min_max_data['max']['sale_price'] ); update_post_meta( $this->id, '_min_variation_period', $min_max_data['min']['period'] ); update_post_meta( $this->id, '_max_variation_period', $min_max_data['max']['period'] ); update_post_meta( $this->id, '_min_variation_period_interval', $min_max_data['min']['interval'] ); update_post_meta( $this->id, '_max_variation_period_interval', $min_max_data['max']['interval'] ); update_post_meta( $this->id, '_subscription_price', $min_max_data['min']['price'] ); update_post_meta( $this->id, '_subscription_sign_up_fee', $min_max_data['subscription']['signup-fee'] ); update_post_meta( $this->id, '_subscription_period', $min_max_data['min']['period'] ); update_post_meta( $this->id, '_subscription_period_interval', $min_max_data['min']['interval'] ); update_post_meta( $this->id, '_subscription_trial_period', $min_max_data['subscription']['trial_period'] ); update_post_meta( $this->id, '_subscription_trial_length', $min_max_data['subscription']['trial_length'] ); update_post_meta( $this->id, '_subscription_length', $min_max_data['subscription']['length'] ); $this->subscription_price = $min_max_data['min']['price']; $this->subscription_period = $min_max_data['min']['period']; $this->subscription_period_interval = $min_max_data['min']['interval']; $this->subscription_sign_up_fee = $min_max_data['subscription']['signup-fee']; $this->subscription_trial_period = $min_max_data['subscription']['trial_period']; $this->subscription_trial_length = $min_max_data['subscription']['trial_length']; $this->subscription_length = $min_max_data['subscription']['length']; if ( function_exists( 'wc_delete_product_transients' ) ) { wc_delete_product_transients( $this->id ); } else { WC()->clear_product_transients( $this->id ); } } else { // No variations yet $this->subscription_price = ''; $this->subscription_sign_up_fee = ''; $this->subscription_period = 'day'; $this->subscription_period_interval = 1; $this->subscription_trial_period = 'day'; $this->subscription_trial_length = 1; $this->subscription_length = 0; } } /** * Returns the price in html format. * * @access public * @param string $price (default: '') * @return string */ public function get_price_html( $price = '' ) { if ( ! isset( $this->subscription_period ) || ! isset( $this->subscription_period_interval ) || ! isset( $this->max_variation_period ) || ! isset( $this->max_variation_period_interval ) ) { $this->variable_product_sync(); } // Only create the subscription price string when a price has been set if ( $this->subscription_price !== '' ) { $price = ''; if ( $this->is_on_sale() && isset( $this->min_variation_price ) && $this->min_variation_regular_price !== $this->get_price() ) { if ( ! $this->min_variation_price || $this->min_variation_price !== $this->max_variation_price ) { $price .= wcs_get_price_html_from_text( $this ); } $variation_id = get_post_meta( $this->id, '_min_price_variation_id', true ); $variation = wc_get_product( $variation_id ); $tax_display_mode = get_option( 'woocommerce_tax_display_shop' ); $sale_price_args = array( 'qty' => 1, 'price' => $variation->get_sale_price() ); $regular_price_args = array( 'qty' => 1, 'price' => $variation->get_regular_price() ); if ( 'incl' == $tax_display_mode ) { $sale_price = wcs_get_price_including_tax( $variation, $sale_price_args ); $regular_price = wcs_get_price_including_tax( $variation, $regular_price_args ); } else { $sale_price = wcs_get_price_excluding_tax( $variation, $sale_price_args ); $regular_price = wcs_get_price_excluding_tax( $variation, $regular_price_args ); } $price .= $this->get_price_html_from_to( $regular_price, $sale_price ); } else { if ( $this->min_variation_price !== $this->max_variation_price ) { $price .= wcs_get_price_html_from_text( $this ); } $price .= wc_price( $this->get_variation_price( 'min', true ) ); } // Make sure the price contains "From:" when billing schedule differs between variations if ( false === strpos( $price, wcs_get_price_html_from_text( $this ) ) ) { if ( $this->subscription_period !== $this->max_variation_period ) { $price = wcs_get_price_html_from_text( $this ) . $price; } elseif ( $this->subscription_period_interval !== $this->max_variation_period_interval ) { $price = wcs_get_price_html_from_text( $this ) . $price; } } $price .= $this->get_price_suffix(); $price = WC_Subscriptions_Product::get_price_string( $this, array( 'price' => $price ) ); } return apply_filters( 'woocommerce_variable_subscription_price_html', $price, $this ); } /** * Provide the WC_Data::get_meta() function when WC < 3.0 is active. * * @param string $meta_key * @param bool $single * @param string $context * @return object WC_Product_Subscription or WC_Product_Subscription_Variation */ function get_meta( $meta_key = '', $single = true, $context = 'view' ) { return get_post_meta( $this->get_id(), $meta_key, $single ); } /** * get_child function. * * @access public * @param mixed $child_id * @return object WC_Product_Subscription or WC_Product_Subscription_Variation */ public function get_child( $child_id ) { return wc_get_product( $child_id, array( 'product_type' => 'Subscription_Variation', 'parent_id' => $this->id, 'parent' => $this, ) ); } /** * Get default attributes. * * @since 2.2.0 * @param string $context * @return array */ public function get_default_attributes( $context = 'view' ) { return $this->get_variation_default_attributes(); } }