3

This may be a noob question so apologies in advance.

I have variable subscription products whose price varies by subscription length. I also have simple subscriptions that have a subscription_length = 0. When the cart contains both types, Woocommerce subscriptions creates two subscriptions.

I am trying to modify the subscription_length metadata of all the items in the cart to match the longest length in the cart so only one subscription will be created.

I just can't seem to find right way to update the cart. It might just be stupid human PHP tricks.

This is the latest variation of the statement that is not working right now:

$cart[$item_key]['data']->subscription_length = $maxlength;

I've tried a dozen or more variations on the theme to update the $cart data. some failed and some succeeded but the data in $cart did not change.

function csi_align_subscription_length() {
    $maxlength = 0;
    $cart = WC()->cart->get_cart();

    //find the longest length
    foreach ( $cart as $item_key => $item ){
        if ( WC_Subscriptions_Product::is_subscription( $item['data'] ) ) {
            $length = WC_Subscriptions_Product::get_length( $item['data'] );
            $maxlength = ($length > $maxlength) ?  $length : $maxlength ;   //get the longest length
        }
    }   

    //set all subscription lengths to the longest lengths
    foreach ( $cart as $item_key => $item ){
        if ( WC_Subscriptions_Product::is_subscription( $item['data'] ) ) {
            echo '<pre>';
            echo "cart-item before: ". var_dump($cart[$item_key]);
            $cart[$item_key]['data']->subscription_length = $maxlength;
            echo "cart-item after:  ".var_dump($cart[$item_key]);
            echo '</pre>';
        }
    }   
    WC()->cart->set_cart_contents($cart);
}
add_filter( 'woocommerce_subscription_cart_before_grouping', 'csi_align_subscription_length', 10, 1 );

I just need to get the subscriptions in the cart to align to a common group so I only have 1 order and 1 subscription per checkout. I am open to a different approach if I've gotten lost off on a tangent.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
AV8R
  • 303
  • 2
  • 14

2 Answers2

2

Finally resolved the issue. No setter for 'subscription_length'. Had to use update_meta_data method. The meta key is '_subscription_length'. the following code works.

function csi_align_subscription_length( $cart ) {
    if ( ( is_admin() && ! defined( 'DOING_AJAX' ) )  )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    $maxlength = (float) 0;

    // Find the longest subscription length
    foreach ( $cart->get_cart() as $cart_item ){
        if ( is_a( $cart_item['data'], 'WC_Product_Subscription' ) || is_a( $cart_item['data'], 'WC_Product_Subscription_Variation' ) ) {
            $length = (float) WC_Subscriptions_Product::get_length( $cart_item['data'] );
            $maxlength = ($length > $maxlength) ?  $length : $maxlength ;   //get the longest length
        }
    } 

    // set the subscription length for everything in the cart
    foreach ( $cart->get_cart() as $item_key => $cart_item ){
        if ( is_a( $cart_item['data'], 'WC_Product_Subscription' ) || is_a( $cart_item['data'], 'WC_Product_Subscription_Variation' )) {
            $cart_item['data']->update_meta_data( '_subscription_length', $maxlength );
        }
    }
}

add_action( 'woocommerce_before_calculate_totals', 'csi_align_subscription_length', 5, 1 );

AV8R
  • 303
  • 2
  • 14
  • Nice answer (+1)… So my code was a bit useful in some way. – LoicTheAztec May 01 '19 at 02:11
  • Yes, absolutely. I moved it from the woocommerce_subscription_cart_before_grouping filter to the woocommerce_before_calculate_totals action. It fits better here anyway. Thanks for the assist. – AV8R May 01 '19 at 03:32
1

Updated (work for simple subscriptions and subscription variations (of a variable subscription)

The hook woocommerce_before_calculate_totals allow to change product properties on cart items. Since woocommerce 3 and CRUD Objects, Object properties can't be accessed directly and you will have to use available getters and setters methods instead… Here we:

  1. get an array of length for all Subscriptions cart items only.
  2. get the max length value
  3. set the max length value for all Subscriptions cart items only.

The required code:

add_action( 'woocommerce_before_calculate_totals', 'before_calculate_totals_action_callback', 10, 1 );
function before_calculate_totals_action_callback( $cart ) {
    if ( ( is_admin() && ! defined( 'DOING_AJAX' ) )  )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    $maxlength = []; // Initializing

    // First loop (get)
    foreach ( $cart->get_cart() as $cart_item ){
        if ( in_array( $cart_item['data']->get_type(), array('subscription', 'subscription_variation') ) ) {
            $maxlength[] = (float) $cart_item['data']->get_length();
        }
    }

    // Get the highest value
    $maxlength = max($maxlength);

    // Second loop (set)
    foreach ( $cart->get_cart() as $cart_item ){
        if ( is_a( $cart_item['data'], 'WC_Product_Subscription' ) || is_a( $cart_item['data'], 'WC_Product_Subscription_Variation' ) ) {
            $cart_item['data']->set_length( $maxlength );
        }
    }
}

Code goes in function.php file of your active child theme (or active theme). Tested and works.

Note: When submitting the order, you will certainly need to set the cart item (custom) length as custom order item meta data.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • The offered solution doesn't work with variable subscriptions. get_length() returns an empty string. ` Pre set_length() obj->get_length() = string(0) "" class::get_length() = string(1) "6" Pre set_length() obj->get_length() = string(0) "" class::get_length() = int(0) Post set_length() obj->get_length() = string(0) "" class::get_length() = string(1) "6" Post set_length() obj->get_length() = string(1) "6" class::get_length() = int(0)` I altered the code to use the class::methods that return correct values but the cart values are still the same Pre/Post – AV8R Apr 30 '19 at 19:55
  • Ran out of room - The length values before the ->set_length call are the same as the values after the set_length call. Same problem I was having. – AV8R Apr 30 '19 at 20:00
  • @AV8R Sorry… Updated: Now works now for simple and variable subscriptions . – LoicTheAztec Apr 30 '19 at 21:20
  • I appreciate the attempt at a solution, however the original problem remains. The set_length() method in your example is inherited from the parent WC_Product class. There is no setter method for 'subscription_length'. The set_length() method is inherited from WC_Product and sets the dimensional length of the product (height, width, length). I have tried to use the update_meta_data() method but it fails as well. The method executes but the data is never changed. – AV8R May 01 '19 at 00:55