I use mPdf to create pdf's of the items in my woocommerce cart and I need a way to sort the items in the cart table using drag and drop and also output the new order of the items as $cart_contents for the pdf to recognize the order of the items. My idea is that I can use jquery sortable to make the cart table drag and dropable and then add a positon number to each item in the cart table using cart item data. I can then use the update: function event and update the position number according to it's position in the cart. After that I can output the sorted cart items using asort and the position number assigned to them. I'm not sure if I'm making this way more difficult than it should be since I'm new to jquery and php. I've been searching for an answer to this all over the internet for quite some time but to no avail so any pointers or help with the answer would be greatly appreciated.
Here I make the cart items sortable by drag and drop and then sends the product id's as a string in the order they are placed trough ajax:
function wc_sortable_cart_items() {
if ( class_exists( 'WooCommerce' ) ) {
if( is_cart() && (count(WC()->cart->get_cart()) > 0) ){
?>
<script>
jQuery(function($) {
$('table.shop_table.cart tbody').sortable({
items: 'tr.cart_item',
cursor: 'move',
axis: 'y',
update: function(event, ui) {
$('#loading-animation').show();
opts = {
url: ajaxurl,
type: 'POST',
async: true,
cache: false,
dataType: 'json',
data:{
action: 'item_sort',
order: $(this).sortable('toArray').toString(),
},
success: function(response) {
$('#loading-animation').hide();
return;
},
error: function(xhr,textStatus,e) {
alert(e);
alert('There was an error saving the updates');
$('#loading-animation').hide();
return;
}
};
$.ajax(opts);
}
});
});
</script>
<?php
}
}
}
add_action( 'wp_footer', 'wc_sortable_cart_items' );
Here I recive the product id's and update the menu_order meta trough $wpdb.
function my_save_item_order() {
global $wpdb;
$counter = 0;
$order = explode(',', $_POST['order']);
foreach ($order as $item_id) {
$wpdb->update($wpdb->posts, array( 'menu_order' => $counter ), array( 'ID' => $item_id) );
$counter++;
}
wp_die(1);
}
add_action('wp_ajax_item_sort', 'my_save_item_order');
add_action('wp_ajax_nopriv_item_sort', 'my_save_item_order');
Here I sort the cart using the menu_order meta:
add_action('woocommerce_cart_loaded_from_session', 'sort_cart_items_by_customfield', 100);
function sort_cart_items_by_customfield() {
$items_to_sort = $cart_contents = array();
foreach (WC()->cart->cart_contents as $item_key => $cart_item) {
$product = $cart_item['data'];
$menu_order = $product->get_menu_order();
$order_number = intval($menu_order);
$items_to_sort[$item_key] = $order_number;
}
asort($items_to_sort);
foreach ($items_to_sort as $cart_item_key => $order_number) {
$cart_contents[$cart_item_key] = WC()->cart->cart_contents[$cart_item_key];
}
WC()->cart->cart_contents = $cart_contents;
}
All of this is working but since I'm using the menu_order meta to sort the products quite a few problems can arise. For example if you have two of the same product in the cart on seperate table rows or if two people have the same item in their carts at the same time then this method won't work at all.
Does anyone here have any experience doing something similar but using a unique identifier such as the cart item key and storing the position in the session data instead? Or perhaps I could take advantage of a hidden input field and the index of the cart items and update them accordingly?
I'm running out of ideas and would really love some help here.
This is my first time asking a question here so if you need more information or if something is not clear please tell me.
Thanks in advance!
So I manged to solve the problem eventually.
I'm creating this answer for anyone that might come by looking for a similar solution in the future.
<tr class="woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>"id="<?php echo $cart_item['key']; ?>">
wp_localize_script( 'admin-js', 'dtAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php')));
function enqueue_script_jquery() {
if( is_cart() && (count(WC()->cart->get_cart()) > 0) ){
wp_enqueue_script( 'jquery-ui');
wp_enqueue_script( 'jquery-ui-sortable');
}
}
add_action('wp_enqueue_scripts', 'enqueue_script_jquery');
function wc_sortable_cart_items() {
if ( class_exists( 'WooCommerce' ) ) {
if( is_cart() && (count(WC()->cart->get_cart()) > 0) ){
?>
<script async>
jQuery(document).ready(function($){
$(document).ajaxComplete(function() {
$('table.shop_table.cart tbody').sortable({
items: 'tr.cart_item',
cursor: 'move',
axis: 'y',
opacity: 0.6,
delay: 75,
containment: "document",
update: function(event, ui) {
var cart_keys = $(".cart_item[id]").map(function(){ return this.id; }).get();
opts = {
url: ajaxurl,
type: 'POST',
async: true,
cache: false,
dataType: 'json',
data:{
security: $('#woocommerce-cart-nonce').val(),
action: 'item_sort',
order: cart_keys,
},
};
$.ajax(opts);
}
});
});
});
</script>
<?php
}
}
}
add_action( 'wp_footer', 'wc_sortable_cart_items' );
function my_save_item_order() {
if( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'woocommerce-cart' ) ) {
wp_send_json( array( 'nonce_fail' => 1 ) );
exit;
}
$order = $_POST['order'];
WC()->session->set( 'sort_order', $order );
wp_send_json( [
'sort_order' => $order,
] );
wp_die(1);
}
add_action('wp_ajax_item_sort', 'my_save_item_order');
add_action('wp_ajax_nopriv_item_sort', 'my_save_item_order');
function sort_cart_items_by_dd() {
$sort_order = WC()->session->get( 'sort_order' ) ;
if( empty( $sort_order ) )
return;
WC()->session->__unset( 'sort_order' );
$cart_contents = array();
foreach( $sort_order as $cart_key )
{
foreach( WC()->cart->cart_contents as $item_key => $cart_item )
{
if( $item_key == $cart_key )
{
$cart_contents[$item_key] = $cart_item;
}
}
}
WC()->cart->cart_contents = $cart_contents;
}
add_action('woocommerce_cart_loaded_from_session', 'sort_cart_items_by_dd', 100);
And that's it. Now you can sort the cart items in the the cart table using drag and drop.