有没有办法将自定义代码添加到公共静态函数中? (woocommerce update_order_review)

Posted

技术标签:

【中文标题】有没有办法将自定义代码添加到公共静态函数中? (woocommerce update_order_review)【英文标题】:Is there a way to add custom code into a public static function? (woocommerce update_order_review) 【发布时间】:2021-10-26 23:25:16 【问题描述】:

我已在我的 woocommerce 结帐页面中添加了购物车部分。现在我想用 ajax 更新购物车。 (如果有人改变了他的国家......)

到目前为止,我已经尝试在“update_checkout”上创建一个 ajax 调用,但我遇到的问题是我无法从 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() && ! 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,
                )
            ),
        )
    );

【问题讨论】:

如果这个地方没有do_action是不可能的。 您也许可以尝试在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 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>

好的。现在我可以运行以在我的结帐中获取购物车模板。现在我们进入更难的部分,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 && 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();
);

现在我们需要为我们在 refresh_cart.js 文件中调用的函数添加 ajax 操作。

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

就是这样!

【讨论】:

以上是关于有没有办法将自定义代码添加到公共静态函数中? (woocommerce update_order_review)的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法将(破解)自定义 SQL 添加到核心数据提取中?

有没有办法将自定义 QGroupBox 添加到任何小部件/布局?

将自定义属性添加到 asp.net-mvc 中的页面指令

将自定义中间件添加到 Laravel Passport 端点

UIAlertController - 将自定义视图添加到操作表

将自定义 Web 部件添加到页面 SharePoint 2013 c# csom