Sé que podemos agregar meta para el artículo del carrito de Woocommerce usando woocommerce_add_cart_item_data
gancho.
¿Hay alguna forma de actualizar el meta del artículo del carrito existente?
Sí, pero al parecer, solo accediendo directamente al carrito:
global $woocommerce;
$woocommerce->cart->cart_contents[$cart_item_key]['whatever_meta'] = 'testing';
$woocommerce->cart->set_session(); // when in ajax calls, saves it.
Recomendaría eliminar y volver a agregar el producto, ya que podrían perderse otros metadatos (como en la respuesta de Phong Tran).
Basado en la respuesta de @DanielSalcedos, manteniendo solo el mínimo absoluto requerido para responder la pregunta.
-
Me ahorraste unas horas de mi tiempo. Gracias. Esta es la respuesta correcta para esta pregunta.
– Soy la persona más estúpida
3 de diciembre de 2018 a las 9:40
daniel salcedo
Sé que ha pasado un tiempo, pero como todavía no tiene respuesta, y me costó mucho sudor y una pinta de sangre, comparto mi solución aquí.
Primero
Asumiré que sabe cómo agregar metadatos a un carrito y a un pedido. Si no, puede echar un vistazo a la solución de pwnewbie, pero le recomiendo el artículo completo en laboratorios de sabiduría
El método de Wisdm toma muchos pasos. Primero creas una variable de sesión de PHP a través de Ajax. Segundo, interceptas woocommerce_add_cart_item_data
filtro para agregar su variable de sesión a la sesión de woocommerce.
la cosa sobre woocommerce_add_cart_item_data
filtro es que se ejecuta en medio de la add to cart
proceso, entonces, si agrega su variable a la principal $wooocmmerce
objeto, en algún momento, se almacena como el evento de agregar al carrito. (Algo así como)
La idea
¿Qué pasa si quiero editar esos metadatos y no cualquiera de las propiedades estándar del carrito? Lo ideal sería conseguir un filtro o una acción que se ejecute en medio de guardar algo. El problema es que mientras no cambiemos nada más, no hay gancho para ejecutar (lo intenté con woocommerce_update_cart_action_cart_updated
gancho que se ejecuta después de que se hayan producido cupones, cantidades y retiros del carrito, pero nunca se disparó porque nunca pasé las validaciones)
Mi acercamiento
En un caparazón, reconstruya lo menos posible, tanto como sea necesario. Agregué un evento ajax síncrono al evento OnSubmit del formulario del carrito. (Quiero que mi interfaz de usuario se actualice con mis cambios, por lo que la recarga debe ocurrir después de mi actualización)
AJAX:
var myFlag33322805 = true;
$('form').submit(function(e){
if(myFlag33322805){
myFlag33322805 = false;
e.preventDefault(); // Flag and prevent default to syncronize submits
var kart = []; // Will retrieve every cart item's meta
$('.cartRow').each(function(){
//This object will store your meta data and be pushed into kart array
var kitm = {
'p' : $(this).data('product_id'),
'm' : $(this).find('select[name=myMetaData]').val(),
'k' : $(this).data('key')
};
kart.push(kitm);
});
var data = {
'action': 'Ajax_Update_My_MetaData_33322805',
'k': kart
};
$.post(VKVAjax.ajaxurl, data, function (response) {
// Might do something with the response here
});
$('form').submit(); // This time, the form will submit, but AJAX wont run because of myFlag33322805 = false
}
});
La magia:
La respuesta de php ajax es una función que recibirá mis metadatos para actualizar (en realidad, todos, actualizados o no), y tomará el global $woocommerce
objeto para insertar los metadatos Y guardarlo en la sesión:
PHP:
function fn_Update_My_MetaData_33322805(){
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$updt = Array();
foreach ($_POST['k'] AS $item){
$product = new stdClass();
$updtCL = new stdClass();
$product->{'id'} = $item['p']; //This is product id
$product->{'mymeta'} = $item['m']; //This is metadata
$updtCL->{'krtkey'} = $item['k']; //This is product key in cart
$updtCL->{'meta'} = $product;
$updt[] = $updtCL;
}
// cycle the cart replace the meta of the correspondant key
foreach ($cart as $key => $item) {
foreach($updt as $updtitem){
if($key == $updtitem->krtkey){ // if this kart item corresponds with the received, the meta data is updated
// Update the content of the kart
$woocommerce->cart->cart_contents[$key]['vkv_AlternCart_value'] = $updtitem->meta;
}
}
}
// This is the magic: With this function, the modified object gets saved.
$woocommerce->cart->set_session();
wp_die('{"e":"ok", "Updt": "'.count($arrupdt).'"}');
}
Por supuesto, esto debería engancharse como cualquier otro evento de ajax.
add_action('wp_ajax_nopriv_Ajax_Update_My_MetaData_33322805', 'fn_Ajax_Update_My_MetaData_33322805');
add_action('wp_ajax_Ajax_Update_My_MetaData_33322805', 'fn_Ajax_Update_My_MetaData_33322805');
conclusión
Puede actualizar los metadatos de un artículo del carrito con una llamada Ajax síncrona, sobrescribiendo el objeto directamente en el $woocommerce
variable global y guardándola con el $woocommerce->cart->set_session();
método.
notas al pie
No es el método ideal, y es bastante arriesgado trabajar directamente con el $woocommerce
global. Me encantaría saber de un mejor enfoque.
Phong Tran
Soy novato y mi inglés no es bueno, por lo que la respuesta puede ser un poco confusa.
Gracias vlad274 por el consejo.
Mi acercamiento:
Lo mismo con Daniel Salcedo, también intento cambiar los metadatos editando woocommerce_sessions, pero hay un problema. Al agregar un artículo al carrito, WooCommerce creará una clave_artículo_carrito única para él mediante md5 (product_id + … + metadatos), luego busque esta clave_artículo_carrito ya existe, si está disponible, este artículo se actualizará en cantidad, de lo contrario crear un nuevo elemento.
Esto significa que si cambia el valor meta del producto sombrero de azul a rojoluego agregue el producto del sombrero con azulen lugar de crear un nuevo elemento azul sombrero, sólo aumentará la cantidad de rojo sombrero, debe cambiar cart_item_key si desea que el carrito se actualice correctamente.
Cambiar cart_item_key es bastante arriesgado, en su lugar, simplemente podemos eliminar y volver a agregar el producto. Como esto
// get cart_item_key of item you want to change
$cart_item_key_old = $_POST['cart_item_key'];
// retrieve its information
$cart_item_old = WC()->cart->cart_contents[ $cart_item_key_old ];
$product_id_old = $cart_item_old['product_id'];
$quantity_old = $cart_item_old['quantity'];
$variation_id_old = $cart_item_old['variation_id'];
$variation_old = $cart_item_old['variation'];
// creating a cart_item_key with the same information except metadata
$cart_item_key_new = WC()->cart->generate_cart_id( $product_id_old, $variation_id_old, $variation_old, ['color'=>'red'] );
// check new cart_item_key already exists
$found = WC()->cart->find_product_in_cart( $cart_item_key_new );
// if true, update its quantity
if ($found != '') {
$new_quantity = $cart_item_old['quantity'] + WC()->cart->cart_contents[ $cart_item_key_new ]['quantity'];
WC()->cart->set_quantity( $cart_item_key_new, $new_quantity );
}
// else, re-add with new metadata
else {
WC()->cart->add_to_cart($product_id_old, $quantity_old, $variation_id_old, $variation_old, ['color'=>'red'] );
}
// finally delete the old item
WC()->cart->remove_cart_item($cart_item_key_old);
wp_die();
Nota: Si desea enviar el formulario del carrito en lugar de actualizar la página después de ejecutar el ajax anterior, es probable que el método set_quantity anule la cantidad del artículo cuando woocommerce update_cart. En este caso, solo necesita devolver new_quantity y cambiar el valor de entrada por js antes de enviar el formulario.
Código completo:
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
...
?>
<!-- place it anywhere within the foreach -->
<div class="box-type-field">
<select class="box-type" name="box-type" cart_item_key="<?php echo $cart_item_key ?>">
<option <?php echo $cart_item['box-type']=='boxes'?"selected":""; ?> value="boxes"><?php _e( 'Boxes', 'woocommerce' ); ?></option>
<option <?php echo $cart_item['box-type']=='bags'?"selected":""; ?> value="bags"><?php _e( 'Bags', 'woocommerce' ); ?></option>
</select>
</div>
<?php
...
}
AJAX:
$('.box-type-field .box-type').live('change', function () {
var cartItemKey = $(this).attr("cart_item_key");
var boxType = $(this).val();
$.ajax({
type : "post",
url : '<?php echo admin_url('admin-ajax.php');?>',
datatype: 'json',
data : {
action : "update_cart_boxtype",
cart_item_key : cartItemKey,
box_type : boxType,
},
success: function(cartItem) {
cartItemKey = cartItem[0];
cartItemQty = cartItem[1];
if (cartItem) $('input[name="cart['+cartItemKey+'][qty]"]').val(cartItemQty); // update quantity
$('.woocommerce-cart-form button[type="submit"]').click(); // submit form
}
})
})
PHP:
add_action( 'wp_ajax_update_cart_boxtype', 'update_cart_boxtype_init' );
add_action( 'wp_ajax_nopriv_update_cart_boxtype', 'update_cart_boxtype_init' );
function update_cart_boxtype_init() {
if ( ! WC()->cart->is_empty() ) {
$cart_item_key = (isset($_POST['cart_item_key']))?$_POST['cart_item_key'] : '';
$cart_item = WC()->cart->cart_contents[ $cart_item_key ];
$box_type = (isset($_POST['box_type']))?$_POST['box_type'] : '';
$cart_updated = false;
$cart_item_key_new = WC()->cart->generate_cart_id( $cart_item['product_id'], $cart_item['variation_id'], $cart_item['variation'], ['box-type'=>$box_type] );
$found = WC()->cart->find_product_in_cart( $cart_item_key_new );
if ($found != '') {
$new_qty = $cart_item['quantity'] + WC()->cart->cart_contents[ $cart_item_key_new ]['quantity'];
WC()->cart->remove_cart_item($cart_item_key);
wp_send_json_success([$cart_item_key_new, $new_qty]);
} else {
WC()->cart->add_to_cart($cart_item['product_id'], $cart_item['quantity'], $cart_item['variation_id'], $cart_item['variation'], ['box-type' => $box_type]);
$cart_updated = true;
WC()->cart->remove_cart_item($cart_item_key);
wp_send_json_success(false);
}
}
wp_die();
}
-
Esta respuesta podría beneficiarse de alguna explicación adicional sobre el enfoque/solución general propuesto. Además, dadas las otras respuestas detalladas, sería útil proporcionar más información sobre por qué esto es diferente a las soluciones existentes.
– Vlad274
10 de julio de 2018 a las 14:26
-
¡Gran enfoque!
– zen_1991
5 de enero de 2021 a las 16:39
Paso 1: agregue datos en una sesión personalizada, haga clic en el botón ‘Agregar al carrito’
Para aquellos de ustedes que han trabajado con WooCommerce, pueden saber que al hacer clic en el botón ‘Agregar al carrito’, la página del producto se actualiza y los datos del usuario se pierden. Por lo tanto, debemos agregar los datos personalizados de nuestra página de productos a una sesión personalizada creada con Ajax. Este código se invoca antes de que se cree la sesión de WooCommerce.
<?php
add_action('wp_ajax_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
add_action('wp_ajax_nopriv_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
function wdm_add_user_custom_data_options_callback()
{
//Custom data - Sent Via AJAX post method
$product_id = $_POST['id']; //This is product ID
$user_custom_data_values = $_POST['user_data']; //This is User custom value sent via AJAX
session_start();
$_SESSION['wdm_user_custom_data'] = $user_custom_data_values;
die();
}
Paso 2: agregue datos personalizados en la sesión de WooCommerce
En este paso, se ha creado la sesión de WooCommerce y ahora está disponible para que agreguemos nuestros datos personalizados. Usamos el siguiente código para agregar los datos personalizados de la sesión que hemos creado en la sesión de WooCommerce. En este paso, nuestra sesión también está desactivada ya que los datos en ella han sido capturados y ya no son necesarios.
add_filter('woocommerce_add_cart_item_data','wdm_add_item_data',1,2);
if(!function_exists('wdm_add_item_data'))
{
function wdm_add_item_data($cart_item_data,$product_id)
{
/*Here, We are adding item in WooCommerce session with, wdm_user_custom_data_value name*/
global $woocommerce;
session_start();
if (isset($_SESSION['wdm_user_custom_data'])) {
$option = $_SESSION['wdm_user_custom_data'];
$new_value = array('wdm_user_custom_data_value' => $option);
}
if(empty($option))
return $cart_item_data;
else
{
if(empty($cart_item_data))
return $new_value;
else
return array_merge($cart_item_data,$new_value);
}
unset($_SESSION['wdm_user_custom_data']);
//Unset our custom session variable, as it is no longer needed.
}
}
Paso 3: extraiga datos personalizados de la sesión de WooCommerce e insértelos en el objeto del carrito
En esta etapa, tenemos detalles del producto predeterminados junto con los datos personalizados en la sesión de WooCommerce. Los datos predeterminados se agregan al objeto del carrito debido a la funcionalidad proporcionada por el complemento. Sin embargo, necesitamos extraer explícitamente los datos personalizados de la sesión de WooCommerce e insertarlos en el objeto del carrito. Esto se puede implementar con el siguiente código.
add_filter('woocommerce_get_cart_item_from_session', 'wdm_get_cart_items_from_session', 1, 3 );
if(!function_exists('wdm_get_cart_items_from_session'))
{
function wdm_get_cart_items_from_session($item,$values,$key)
{
if (array_key_exists( 'wdm_user_custom_data_value', $values ) )
{
$item['wdm_user_custom_data_value'] = $values['wdm_user_custom_data_value'];
}
return $item;
}
}
Paso 4: Muestre los datos personalizados del usuario en el carrito y en la página de pago
Ahora que tenemos nuestros datos personalizados en el objeto del carrito, todo lo que tenemos que hacer ahora es mostrar estos datos en el carrito y en la página de pago. Así es como debería verse la página de su carrito después de que se hayan agregado los datos personalizados desde la sesión de WooCommerce a su carrito. Página de mi carrito