Есть ли способ добавить пользовательский код в общедоступную статическую функцию? (woocommerce update_order_review)

#php #jquery #ajax #wordpress #woocommerce

Вопрос:

Я добавил сегмент корзины на свою страницу оформления заказа woocommerce. Теперь я хочу обновить корзину с помощью ajax. (Если кто-то сменит свою страну …)

До сих пор я пытался создать вызов ajax для «update_checkout», но проблема, с которой я столкнулся, заключалась в том, что я не смог получить новые данные из ajax.

Единственный способ, который работает, — это когда я редактирую основную woocommerce public static function update_order_review() . Есть ли какой-нибудь способ подключиться к нему с помощью моего плагина, чтобы добавить свой код в эту функцию? Мне нужно добавить:

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

И после этого этот фрагмент в функцию wp_send_json() :

         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,
                )
            ),
        )
    );
 

В этом вся функция:

     /**
 * 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() amp;amp; ! is_customize_preview() amp;amp; 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'] ) amp;amp; 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,
                )
            ),
        )
    );
}
 

Комментарии:

1. Если в этом месте нет do_action, это невозможно.

2. Возможно, вы могли бы попробовать сделать это в woocommerce_update_order_review_fragments фильтре, который применяется в самом конце wp_send_json вызова. В неизмененной версии, которая получает массив только с двумя переданными ключами, но вы можете добавить в него свой третий пользовательский ключ. woocommerce_order_cart() вызывается без каких-либо параметров, поэтому я ожидаю, что, вероятно, это может быть вызвано и в другом контексте.

Ответ №1:

Ладно, наконец-то у меня все получилось.

Во-первых, я создал функцию для вызова шаблона моей корзины (это в основном тот же способ, что и шаблон обзора заказа).

 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');
  };
}; 

После этого я скопировал свой сегмент корзины в этот файл.

 <?php
/**
 * BB Cart Summary
 *
 *
 * @see https://docs.woocommerce.com/document/template-structure/
 * @package WooCommerceTemplates
 * @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 amp;amp; $_product->exists() amp;amp; $cart_item['quantity'] > 0 amp;amp; 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 ) . 'amp;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() amp;amp; $_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> 

Хорошо. Теперь я могу запустить, чтобы получить шаблон корзины в моей кассе. Теперь мы переходим к более сложной части-обработчику ajax.

Я создал новый файл под названием «refresh_cart.js» и инициализировал его.

 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 ); 

В этот файл я скопировал важные части из 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 amp;amp; 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();
}); 

Теперь нам нужно добавить действие ajax для той функции, которую мы вызываем в refresh_cart.js файл.

 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' ); 

Теперь нам нужно только воссоздать public static function update_order_review() функцию, чтобы она заработала:

   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 */
          )
        ),
      )
    );
  } 

И это все!