Is there a way to add custom code into a public static function? (woocommerce update_order_review)

I've added a cart segment to my woocommerce checkout page. Now I want to update the cart with ajax. (If someone changes his country ...)

I've tried so far to create a ajax call on "update_checkout" but the problem I've encountered was that I wasn't able to get the new data from ajax.

The only way which is working, is when I'm editing the core woocommerce public static function update_order_review(). Is there any way I can hook into it with my Plugin to add my code to this function? I need to add:

    // Get cart fragment. CUSTOM
    ob_start();
    woocommerce_order_cart();
    $woocommerce_order_cart = ob_get_clean();

And after that this fragment into the wp_send_json() function:

        wp_send_json(
        array(
            'result'    => empty( $messages ) ? 'success' : 'failure',
            'messages'  => $messages,
            'reload'    => $reload_checkout,
            'fragments' => apply_filters(
                'woocommerce_update_order_review_fragments',
                array(
                    '.woocommerce-cart-form__contents' => $woocommerce_order_cart,/* CUSTOM */
                    '.woocommerce-checkout-review-order-table' => $woocommerce_order_review,
                    '.woocommerce-checkout-payment' => $woocommerce_checkout_payment,
                )
            ),
        )
    );

This is the whole function:

    /**
 * AJAX update order review on checkout.
 */
public static function update_order_review() {
    check_ajax_referer( 'update-order-review', 'security' );

    wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );

    if ( WC()->cart->is_empty() && ! is_customize_preview() && apply_filters( 'woocommerce_checkout_update_order_review_expired', true ) ) {
        self::update_order_review_expired();
    }

    do_action( 'woocommerce_checkout_update_order_review', isset( $_POST['post_data'] ) ? wp_unslash( $_POST['post_data'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

    $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
    $posted_shipping_methods = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array();

    if ( is_array( $posted_shipping_methods ) ) {
        foreach ( $posted_shipping_methods as $i => $value ) {
            $chosen_shipping_methods[ $i ] = $value;
        }
    }

    WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
    WC()->session->set( 'chosen_payment_method', empty( $_POST['payment_method'] ) ? '' : wc_clean( wp_unslash( $_POST['payment_method'] ) ) );
    WC()->customer->set_props(
        array(
            'billing_country'   => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null,
            'billing_state'     => isset( $_POST['state'] ) ? wc_clean( wp_unslash( $_POST['state'] ) ) : null,
            'billing_postcode'  => isset( $_POST['postcode'] ) ? wc_clean( wp_unslash( $_POST['postcode'] ) ) : null,
            'billing_city'      => isset( $_POST['city'] ) ? wc_clean( wp_unslash( $_POST['city'] ) ) : null,
            'billing_address_1' => isset( $_POST['address'] ) ? wc_clean( wp_unslash( $_POST['address'] ) ) : null,
            'billing_address_2' => isset( $_POST['address_2'] ) ? wc_clean( wp_unslash( $_POST['address_2'] ) ) : null,
        )
    );

    if ( wc_ship_to_billing_address_only() ) {
        WC()->customer->set_props(
            array(
                'shipping_country'   => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null,
                'shipping_state'     => isset( $_POST['state'] ) ? wc_clean( wp_unslash( $_POST['state'] ) ) : null,
                'shipping_postcode'  => isset( $_POST['postcode'] ) ? wc_clean( wp_unslash( $_POST['postcode'] ) ) : null,
                'shipping_city'      => isset( $_POST['city'] ) ? wc_clean( wp_unslash( $_POST['city'] ) ) : null,
                'shipping_address_1' => isset( $_POST['address'] ) ? wc_clean( wp_unslash( $_POST['address'] ) ) : null,
                'shipping_address_2' => isset( $_POST['address_2'] ) ? wc_clean( wp_unslash( $_POST['address_2'] ) ) : null,
            )
        );
    } else {
        WC()->customer->set_props(
            array(
                'shipping_country'   => isset( $_POST['s_country'] ) ? wc_clean( wp_unslash( $_POST['s_country'] ) ) : null,
                'shipping_state'     => isset( $_POST['s_state'] ) ? wc_clean( wp_unslash( $_POST['s_state'] ) ) : null,
                'shipping_postcode'  => isset( $_POST['s_postcode'] ) ? wc_clean( wp_unslash( $_POST['s_postcode'] ) ) : null,
                'shipping_city'      => isset( $_POST['s_city'] ) ? wc_clean( wp_unslash( $_POST['s_city'] ) ) : null,
                'shipping_address_1' => isset( $_POST['s_address'] ) ? wc_clean( wp_unslash( $_POST['s_address'] ) ) : null,
                'shipping_address_2' => isset( $_POST['s_address_2'] ) ? wc_clean( wp_unslash( $_POST['s_address_2'] ) ) : null,
            )
        );
    }

    if ( isset( $_POST['has_full_address'] ) && wc_string_to_bool( wc_clean( wp_unslash( $_POST['has_full_address'] ) ) ) ) {
        WC()->customer->set_calculated_shipping( true );
    } else {
        WC()->customer->set_calculated_shipping( false );
    }

    WC()->customer->save();

    // Calculate shipping before totals. This will ensure any shipping methods that affect things like taxes are chosen prior to final totals being calculated. Ref: #22708.
    WC()->cart->calculate_shipping();
    WC()->cart->calculate_totals();

    // Get order review fragment.
    ob_start();
    woocommerce_order_review();
    $woocommerce_order_review = ob_get_clean();

    // Get cart fragment. CUSTOM
    ob_start();
    woocommerce_order_cart();
    $woocommerce_order_cart = ob_get_clean();

    // Get checkout payment fragment.
    ob_start();
    woocommerce_checkout_payment();
    $woocommerce_checkout_payment = ob_get_clean();

    // Get messages if reload checkout is not true.
    $reload_checkout = isset( WC()->session->reload_checkout );
    if ( ! $reload_checkout ) {
        $messages = wc_print_notices( true );
    } else {
        $messages = '';
    }

    unset( WC()->session->refresh_totals, WC()->session->reload_checkout );

    wp_send_json(
        array(
            'result'    => empty( $messages ) ? 'success' : 'failure',
            'messages'  => $messages,
            'reload'    => $reload_checkout,
            'fragments' => apply_filters(
                'woocommerce_update_order_review_fragments',
                array(
                    '.woocommerce-cart-form__contents' => $woocommerce_order_cart,/* CUSTOM */
                    '.woocommerce-checkout-review-order-table' => $woocommerce_order_review,
                    '.woocommerce-checkout-payment' => $woocommerce_checkout_payment,
                )
            ),
        )
    );
}

Solutions

Alright, finally I've got it working.

Firstly I've created a function to call my cart template (It's basically the same way as the order review template)

if ( ! function_exists( 'woocommerce_order_cart' ) ) {
  /**
   * Output the Cart table for the checkout.
   *
   * @param bool $deprecated Deprecated param.
   */
  function bb_order_cart( $deprecated = false ) {
include(plugin_dir_path( __FILE__ ).'assets/templates/woocommerce/cart/bb-cart-summary-ajax.php');
  };
};

After that I've copied my cart segment into that file.

<?php
/**
 * BB Cart Summary
 *
 *
 * @see https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerce\Templates
 * @version 5.2.0
 */

defined( 'ABSPATH' ) || exit;
?> 
  <table class="shop_table shop_table_responsive cart woocommerce-cart-form__contents" cellspacing="0">
    <tbody>
      <?php do_action( 'woocommerce_before_cart_contents' );
      $i = 0;
      foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        $_product   = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
        $product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );

        if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
          $product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
          ?>
          <tr class="woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">

            <td class="product-thumbnail">
            <?php
            $thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key );

            if ( ! $product_permalink ) {
              echo $thumbnail; // PHPCS: XSS ok.
            } else {
              printf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $thumbnail ); // PHPCS: XSS ok.
            };?>
              <div class="item_quantity_container">
                <?php echo $cart_item['quantity'];?>
              </div>
            </td>

            <td class="product-name" data-title="<?php esc_attr_e( 'Product', 'woocommerce' ); ?>">
            <?php
            if ( ! $product_permalink ) {
              echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;' );
            } else {
              echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) );
            }

            $item_meta_bb = get_field( "wc_bb_prd_cart_input", $product_id );
            ?> <div class="prd_cart_item_meta"><?php
            echo $item_meta_bb;
            ?></div><?php 
            do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key );

            // Meta data.
            echo wc_get_formatted_cart_item_data( $cart_item ); // PHPCS: XSS ok.

            // Backorder notification.
            if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
              echo wp_kses_post( apply_filters( 'woocommerce_cart_item_backorder_notification', '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'woocommerce' ) . '</p>', $product_id ) );
            }
            ?>
            </td>
<!-- 
            <td class="product-price" data-title="<?php esc_attr_e( 'Price', 'woocommerce' ); ?>">
              <?php
                echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key ); // PHPCS: XSS ok.
              ?>
            </td> -->

            <td class="product-subtotal" data-title="<?php esc_attr_e( 'Subtotal', 'woocommerce' ); ?>">
              <?php
                echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // PHPCS: XSS ok.
              ?>
            </td>
          </tr>
          <?php
        }

      $i++; 
    }
      ?>

      <?php if ($i > '2'){?>
      <style type="text/css">.cart_table {width: 102%;}</style><?php };?>


      <?php do_action( 'woocommerce_cart_contents' ); ?>

      <?php do_action( 'woocommerce_after_cart_contents' ); ?>
    </tbody>
  </table>

Alright. Now I'm able to run to get the cart template inside my checkout. Now we are comming to the harder part, the ajax handler.

I've created a new file called "refresh_cart.js" and initialized it.

function changeCart() {
  if ( is_checkout() ) {
    wp_enqueue_script( 'wc-update-checkout-cart', plugins_url( '/js/refresh_cart.js', __FILE__ ), array('jquery'), '', true);
    wp_localize_script( 'wc-update-checkout-cart', 'refresh_cart',
            array( 
              'ajax_url' => admin_url( 'admin-ajax.php' ), 
            ) );
  }  
}
add_action( 'wp_footer', 'changeCart', 10 );

Into that file I've copied the important parts from the wc js.

/* global wc_checkout_params */
jQuery( function( $ ) {

  // wc_checkout_params is required to continue, ensure the object exists
  if ( typeof wc_checkout_params === 'undefined' ) {
    return false;
  }

  $.blockUI.defaults.overlayCSS.cursor = 'default';

  var wc_checkout_form = {
    updateTimer: false,
    dirtyInput: false,
    selectedPaymentMethod: false,
    xhr: false,
    $order_review: $( '#order_review' ),
    $checkout_form: $( 'form.checkout' ),
    init: function() {
      $( document.body ).on( 'update_checkout', this.update_checkout );
      $( document.body ).on( 'init_checkout', this.init_checkout );


      // Update on page load
      if ( wc_checkout_params.is_checkout === '1' ) {
        $( document.body ).trigger( 'init_checkout' );
      }
      if ( wc_checkout_params.option_guest_checkout === 'yes' ) {
        $( 'input#createaccount' ).on( 'change', this.toggle_create_account ).trigger( 'change' );
      }
    },
        reset_update_checkout_timer: function() {
      clearTimeout( wc_checkout_form.updateTimer );
    },
    update_checkout: function( event, args ) {
      // Small timeout to prevent multiple requests when several fields update at the same time
      wc_checkout_form.reset_update_checkout_timer();
      wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.update_checkout_action, '5', args );
    },
    update_checkout_action: function( args ) {
      if ( wc_checkout_form.xhr ) {
        wc_checkout_form.xhr.abort();
      }

      if ( $( 'form.checkout' ).length === 0 ) {
        return;
      }

      args = typeof args !== 'undefined' ? args : {
        update_shipping_method: true
      };

      var data = {
        post_data       : $( 'form.checkout' ).serialize()
      };

      $( '.woocommerce-checkout-payment, .woocommerce-checkout-review-order-table, .woocommerce-cart-form__contents' ).block({
        message: null,
        overlayCSS: {
          background: '#fff',
          opacity: 0.6
        }
      });


      var country      = $( '#billing_country' ).val(),
        state      = $( '#billing_state' ).val(),
        postcode     = $( ':input#billing_postcode' ).val(),
        city       = $( '#billing_city' ).val(),
        address      = $( ':input#billing_address_1' ).val(),
        address_2    = $( ':input#billing_address_2' ).val(),
        s_country    = country,
        s_state      = state,
        s_postcode     = postcode,
        s_city       = city,
        s_address    = address,
        s_address_2    = address_2,
        $required_inputs = $( wc_checkout_form.$checkout_form ).find( '.address-field.validate-required:visible' ),
        has_full_address = true;

      if ( $required_inputs.length ) {
        $required_inputs.each( function() {
          if ( $( this ).find( ':input' ).val() === '' ) {
            has_full_address = false;
          }
        });
      }

      if ( $( '#ship-to-different-address' ).find( 'input' ).is( ':checked' ) ) {
        s_country    = $( '#shipping_country' ).val();
        s_state      = $( '#shipping_state' ).val();
        s_postcode     = $( ':input#shipping_postcode' ).val();
        s_city       = $( '#shipping_city' ).val();
        s_address    = $( ':input#shipping_address_1' ).val();
        s_address_2    = $( ':input#shipping_address_2' ).val();
      }

      var data = {
        action: 'bb_update_order_review',
        security: wc_checkout_params.update_order_review_nonce,
        country         : country,
        state           : state,
        postcode        : postcode,
        city            : city,
        address         : address,
        address_2       : address_2,
        s_country       : s_country,
        s_state         : s_state,
        s_postcode      : s_postcode,
        s_city          : s_city,
        s_address       : s_address,
        s_address_2     : s_address_2,
        has_full_address: has_full_address,
        post_data       : $( 'form.checkout' ).serialize()
      };


      wc_checkout_form.xhr = $.ajax({
        type: "POST",
        url: wc_checkout_params.ajax_url,
        data: data,
        success:  function( data ) {
        console.log(data);

          // Always update the fragments
          if ( data && data.fragments ) {
            $.each( data.fragments, function ( key, value ) {
              if ( ! wc_checkout_form.fragments || wc_checkout_form.fragments[ key ] !== value ) {
                $( key ).replaceWith( value );
              }
              $( key ).unblock();
            } );
            wc_checkout_form.fragments = data.fragments;
          }
          // Fire updated_checkout event.
          $( document.body ).trigger( 'updated_checkout', [ data ] );
        }

      });
    },
   
  };

  wc_checkout_form.init();
});

Now we need to add the ajax action for that function which we are calling in the refresh_cart.js file.

add_action( 'wp_ajax_bb_update_order_review', 'bb_update_order_review' );
add_action( 'wp_ajax_nopriv_bb_update_order_review', 'bb_update_order_review' );

Now we only need to recreate the public static function update_order_review() function to get that working:

  function bb_update_order_review() {
    WC()->customer->set_props(
      array(
        'billing_country'   => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null,
      )
    );

    if ( wc_ship_to_billing_address_only() ) {
      WC()->customer->set_props(
        array(
          'shipping_country'   => isset( $_POST['country'] ) ? wc_clean( wp_unslash( $_POST['country'] ) ) : null,
        )
      );
    } else {
      WC()->customer->set_props(
        array(
          'shipping_country'   => isset( $_POST['s_country'] ) ? wc_clean( wp_unslash( $_POST['s_country'] ) ) : null,
        )
      );
    }

    WC()->customer->save();

  // Calculate shipping before totals. This will ensure any shipping methods that affect things like taxes are chosen prior to final totals being calculated. Ref: #22708.
  WC()->cart->calculate_shipping();
  WC()->cart->calculate_totals();

    // Get cart fragment. BEERBALLER
    ob_start();
    bb_order_cart();
    $bb_order_cart = ob_get_clean();


/*    $reload_checkout = isset( WC()->session->reload_checkout );
    WC()->session->reload_checkout;
    unset( WC()->session->refresh_totals, WC()->session->reload_checkout );
*/
    wp_send_json(
      array(
        'result'    => empty( $messages ) ? 'success' : 'failure',
        'messages'  => $messages,
        'reload'    => $reload_checkout,
        'fragments' => apply_filters(
          'woocommerce_update_order_review_fragments',
          array(
            '.woocommerce-cart-form__contents' => $bb_order_cart,/* BEERBALLER */
          )
        ),
      )
    );
  }

And that's that!

Tags: PHP / Jquery / Ajax

Similar questions

How to call a static function in add_action() inside another static function?
In my plugin, I would like to load css only when the front-end current page is using one of my plugin shortcodes. All my shortcodes are classes extending Shortcode class. Shortcode class helps me to automate the callback function. Shortcode abstract class FooShortcode class How can I call correcty the static function set_bootstrap() in add_action()...
call a variable in a static function to another static function from same class in php
I have a class with a couple of static functions. One of my functions build a variable and I want to use that variable in another static function. How can I call that variable?
Is there a more efficient way to code this conditional statement? Is there a max number of conditionals in PHP?
I have a list of about 100 place names and about the first 10 names begin with a numeral, which of course here is considered a string. I want to seperate the numbers and each letter. I tried this but it always returned true even if the name began with a letter. The code below works but seems inefficient to me, anyone have a more concise way to writ...
Call public static method from ajax 'wpcf7_mail_sent' hook
I'm trying to call action from 'wpcf7_mail_sent' hook (form submitted from frontend from not logged in user). It's not working. If I call same method from hook, that attached to 'save_post' hook - it works. I update post from admin panel. Code samples: plugin #1 (This part works) plugin #1 (this part throws 500 error) plugin #2
Is there a way to create alias without using htacess, permalinks or delving into code?
I would like to create an alias on my site so that specific URLs will redirect to a specific page I have already created. For example, if the user types in www.example.com/blog/products it will redirect to www.example.com/blog/?page=10 Is what I want to do possible with WordPress? I do not want to modify my htacess. I do not want to modify the temp...
Is there any way to put the Google Tag manager Identification code into the child theme?
I know how to create child theme for wordpress theme but having problem in installing Google Tag Manager into the child theme. Normally there are two files in the child theme directory (one is stylesheet css and another one is function.php) however, Google suggests to put the identification code into the header.php file, so in this case is there an...

Also ask

We use cookies to deliver the best possible experience on our website. By continuing to use this site, accepting or closing this box, you consent to our use of cookies. To learn more, visit our privacy policy.