#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 */
)
),
)
);
}
И это все!