I am trying to customise the Woocommerce myaccount page, in particular the edit address page.
I want to display both the shipping + billing address forms on a single page. Ideally, in a single form with a one save button. I also need to remove a lot of the fields, so that it's a much simpler form of just an address (no name, company, etc).
I have implemented the code found on This Answer. It works nicely in that it shows both forms. However, I cannot remove the fields from the forms. If I try code like this:
add_filter( 'woocommerce_billing_fields' , 'custom_override_billing_fields' );
add_filter( 'woocommerce_shipping_fields' , 'custom_override_shipping_fields' );
function custom_override_billing_fields( $fields ) {
unset($fields['billing_country']);
unset($fields['billing_company']);
unset($fields['billing_first_name']);
unset($fields['billing_last_name']);
unset($fields['billing_phone']);
unset($fields['billing_email']);
return $fields;
}
function custom_override_shipping_fields( $fields ) {
unset($fields['shipping_country']);
unset($fields['shipping_company']);
unset($fields['shipping_first_name']);
unset($fields['shipping_last_name']);
return $fields;
}
It doesn't work, the fields are no longer shown but the form does not save on click... it just redirects to /my-account/edit-address/billing/ - and doesn't save. (the same form shown on this page doesn't save either).
I've also tried:
foreach ( $billing_fields as $key => $field ) :
if($key != 'billing_first_name' && $key != 'billing_last_name') :
woocommerce_form_field( $key, $field, $userMeta[$key][0] );
endif;
endforeach;
This removes the field from displaying, BUT the validation still exists - and any filter code I add to functions using
woocommerce_checkout_fields to remove the validation doesn't seem to affect this form at all.
Is there a way to either:
Remove fields from this form generated by woocommerce_form_field including the validation?
Create a custom form that allows me to set the input fields manually in the code, and update any fields that are there, ignoring the validation from Woocommerce completely?
This should work 100%. You need to state whether the fields you are removing is from billing or shipping and this is done by adding the ['billing'] or ['shipping'], whichever it is.
After this, adding the function directly to woocommerce_checkout_fields will apply both for billing and shipping.
For phone and company fields you can disable it in admin panel itself, do it.
Edit: And yes, all validation that was involved with the fields in the past will be removed. You can then apply any validation you need.
add_filter( 'woocommerce_checkout_fields' , 'brandimagemarketer_remove_billing_fields_checkout' );
function brandimagemarketer_remove_billing_fields_checkout( $fields ) {
unset($fields['billing']['billing_country']);
unset($fields['billing']['billing_first_name']);
unset($fields['billing']['billing_last_name']);
unset($fields['billing']['billing_email']);
unset($fields['shipping']['shipping_country']);
unset($fields['shipping']['shipping_first_name']);
unset($fields['shipping']['shipping_last_name']);
unset($fields['shipping']['shipping_email']);
return $fields;
}
i'm trying to solve this issue for weeks, have tried everything.
I have a Custom Order Action in my order admin panel like this:
function manage_actions( $actions ) {
$actions['wc_my_custom_action'] = 'My Custom Action';
return $actions;
}
add_action( 'woocommerce_order_actions', 'manage_actions');
function custom_action_function( $order ) {
update_post_meta( $order->id, 'Custom_Meta', 'Value' );
}
add_action( 'woocommerce_order_action_my_custom_action', 'custom_action_function' );
This works fine, if i open an order and then click on the 'My Custom Action' in the actions menu the 'Custom_Meta' key value pair is added to the order.
Now i have an Order Update WebHook (using API v3) set up and unfortunately it triggers before the 'custom_action_function' function runs so i always get the payload of the order without the new meta tag. I have tried everything i can to add the meta data prior to the webhook being fired, does anyone have an idea on how to accomplish this? Thanks
I use conditional checkout fields as given at Conditionally unset checkout field in woocommerce .
But, It doesn't remove required validation fields?
How can I pass conditional statement within "if (true)" to remove required validation ?
At the other words, how can I check which option is selected?
Regards
if( true ){ // pass conditional statement here
unset($fields['billing']['add_house_name']); // remove field
$fields['billing']['add_building_name']['required'] = false; // remove required validation
}
return $fields;
You can override checkout fields using this code:
// Hook in
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );
// Our hooked in function - $address_fields is passed via the filter!
function custom_override_default_address_fields( $address_fields ) {
$address_fields['address_1']['required'] = false;
return $address_fields;
}
You can add this hook to a condition where you check inputs based on which you want to trigger validation.
I know we can add meta for woocommerce cart item using woocommerce_add_cart_item_data hook.
Is there any way to update existing cart item meta.?
Yes, but it seems, only via accessing the cart directly:
global $woocommerce;
$woocommerce->cart->cart_contents[$cart_item_key]['whatever_meta'] = 'testing';
$woocommerce->cart->set_session(); // when in ajax calls, saves it.
I would recommend to remove and re-add the product, as other meta data could be lost (as in Phong Tran's answer).
Based on #DanielSalcedos answer, only keeping the absolute minimum required to answer the question.
I know It's been a while, but as it's still not answered, and it took me lots of sweat and a pint of blood, I share my solution here.
First
I'll assume you know how to add metadata to a cart and into an order. If not, you can have a look at pwnewbie's solution but I recommend you the full article at Wisdm labs
Wisdm's method takes many steps. First you create a PHP's session variable trough Ajax. Second, you intercept woocommerce_add_cart_item_data filter to add your Session variable to woocommerce session.
The thing about woocommerce_add_cart_item_data filter is that it gets executed in the middle of the add to cart process, so, if you add your variable to the main $wooocmmerce object, at some point, it get's stored as the add-to-cart event. (Sort of)
The idea
What if I want to edit that metadata and not any of the standard cart properties. The ideal would be to get a filter or an action that runs in the middle of saving something. The problem is that as long as we don't change anything else, there's no hook to run (I tried with woocommerce_update_cart_action_cart_updated hook that runs after coupons, quantities and removals from cart had happened, but it never fired as I never passed the validations)
My approach
In a shell, rebuild as little as possible, as much as needed. I added a synchronous ajax event to the cart form OnSubmit event. (I want my UI to be updated with my changes, so the reload must happen after my update)
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
}
});
The magic:
The php ajax response is a function that will receive my meta data to update (actually, all of it, updated or not), and will take the global $woocommerceobject to insert the meta data AND save it to the session:
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).'"}');
}
Of course, this should be hooked as any other ajax event.
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');
Conclussion
You can update the meta data of a cart item with a synchronous Ajax call, overwritting the object directly to the $woocommerce global variable and saving it with the $woocommerce->cart->set_session(); method.
Footnotes
It's not the ideal method, and is quite risky to work directlly with the $woocommerce global. I would love to know of a better approach.
I'm newbie and my English is not good so the answer may be a little confusing.
Thank vlad274 for the advice.
My approach:
Same with Daniel Salcedo, I also try to change the metadata by editing woocommerce_sessions, but there is a problem. When adding an item to the cart, the woocommerce will create a unique cart_item_key for it by md5 (product_id + ... + metadata), then look for this cart_item_key already exists, if available, this item will be updated in quantity, otherwise will create a new item.
This means that if you change the meta value of the hat product from blue to red, then add the hat product with blue, instead of creating a new item blue hat, it will only increase the quantity of red hat, you need to change the cart_item_key if you want the cart to be updated correctly.
Changing cart_item_key is quite risky, instead we can simply remove and re-add the product. Like this
// 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();
Note: If you want to submit cart form instead of page refreshes after running the above ajax, the quantity of item is likely to be overridden by the set_quantity method when woocommerce update_cart. In this case you just need to return new_quantity and change the input value by js before submitting the form.
Full code:
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();
}
Step 1: Add Data in a Custom Session, on ‘Add to Cart’ Button Click
For those of you who have worked with WooCommerce might know that on the click of the ‘Add to Cart’ button the product page gets refreshed and the user data is lost. Hence, we should add the custom data from our product page to a custom session created using Ajax. This code is invoked before the WooCommerce session is created.
<?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();
}
Step 2: Add Custom Data in WooCommerce Session
At this step, the WooCommerce session has been created and is now available for us to add our custom data. We use the following code to add the custom data from the session we have created into the WooCommerce session. At this step, our session is also unset since the data in it has been captured and it is not needed anymore.
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.
}
}
Step 3: Extract Custom Data from WooCommerce Session and Insert it into Cart Object
At this stage, we have default product details along with the custom data in the WooCommerce session. The default data gets added to the cart object owing to the functionality provided by the plugin. However, we need to explicitly extract the custom data from the WooCommerce session and insert it into the cart object. This can be implemented with the following code.
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;
}
}
Step 4: Display User Custom Data on Cart and Checkout page
Now that we have our custom data in the cart object all we need to do now is to display this data in the Cart and the Checkout page. This is how your cart page should look after the custom data has been added from the WooCommerce session to your Cart.
My-Cart-Page
I am working on a Wordpress plugin and I want to fire a different action based on each option value change. so how can I do that ?
Example:
$options = get_option('ACP_settings'); if $options['acp-select'] has changed from previous value than I want to fire different action based on selection
Please note that $option['acp-select'] is retrieved from select/option html form and has following values for selection:'book','air','SW','HW' and etc....
I hope I have posted my question clearly.
thanks for help
You can use wordpress hooks in the update_option() function.
In your case you can use updated_option or update_option_{$option} hooks.
The code for ACP_settings option would look like:
add_action( 'update_option_ACP_settings', function($old_value, $value, $option){
if( $old_value !== $value ){
// Your option was updated
// Run your code here
}
}, 10, 3 );