I'm trying to validate a custom checkout field against a regex expression so that it is a very specifically formatted date.
Here's my code:
add_action('woocommerce_after_checkout_validation', array($this, 'bkf_dd_checkout_validation'), 10, 2 );
add_action('wp_footer', array($this, 'bkf_dd_checkout_validation_js'));
function bkf_dd_checkout_validation( $fields, $errors ){
if( ! preg_match( '/[a-zA-Z]{6,9}\,\ \d{1,2}\ [a-zA-Z]{3,9}\ \d{4}/', $fields['delivery_date'] ) ){
$errors->add( 'validation', 'Please select a <strong>Delivery Date</strong> via the datepicker.' );
}
}
function bkf_dd_checkout_validation_js(){
if( ! is_checkout() ) {
return;
}
?>
<script>
jQuery(function($){
$( 'body' ).on( 'blur change', '#delivery_date', function(){
const wrapper = $(this).closest( '.form-row' );
if( ! /[a-zA-Z]{6,9}\,\ \d{1,2}\ [a-zA-Z]{3,9}\ \d{4}/.test( $(this).val() ) ) {
wrapper.addClass( 'woocommerce-invalid' );
wrapper.removeClass( 'woocommerce-validated' );
} else {
wrapper.addClass( 'woocommerce-validated' );
}
});
});
</script>
<?php
}
The JS is working, but the error is being set regardless of the value, ie. this displays even if field is valid (and set as green by the JS)
(the date format: Friday, 30 December 2022)
Any ideas?
Solution as provided by Vijay Hardaha
Replace $fields['delivery_date'] with $_POST['delivery_date']
Related
I have payment needed to wait for JS callback token, so on the checkout page, the flow will have the customer check the token button, then click and get the token will apply to the custom field,
in the end, if the custom field is not empty show the order button.
custom field
function techiepress_payleo_description_fields( $description, $payment_id ) {
if( 'payleos' !== $payment_id) {
return $description;
};
// $json = json_encode(WC()->cart);
ob_start();
echo '<div id="Payment">'; // it will create get token button
echo '</div>';
woocommerce_form_field( 'payment_number', array(
'type' => 'text',
'label' => __("credit card", "payleo_payment_plus"),
'required' => true,
),'');
$description .= ob_get_clean();
return $description;
}
hidden order button
add_filter('woocommerce_order_button_html', 'remove_place_order_button_for_specific_payments' );
function remove_place_order_button_for_specific_payments( $button ) {
// HERE define your targeted payment(s) method(s) in the array
$targeted_payments_methods = array('payleos');
$chosen_payment_method = WC()->session->get('chosen_payment_method'); // The chosen payment
// For matched payment(s) method(s), we remove place order button (on checkout page)
if( in_array( $chosen_payment_method, $targeted_payments_methods ) && ! is_wc_endpoint_url() ) {
$button = '';
}
return $button;
}
// jQuery - Update checkout on payment method change
add_action( 'wp_footer', 'custom_checkout_jquery_script' );
function custom_checkout_jquery_script() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
$('form.checkout').on('change', 'input[name="payment_method"]', function(){
$(document.body).trigger('update_checkout');
});
});
</script>
<?php
endif;
}
I would like to know how can show the place order button when the custom field has value?
I have a custom filed for specific shipping methods (see screenshot).
Is there a hook/action to validate this field before proceeding the checkout. When I set the field to required the validation will fire at the checkout page but I want it at the cart page.
Validation:
//Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_company_checkout_validation');
function carrier_company_checkout_validation() {
Load settings and convert them in variables
extract( custom_field_settings() );
if( isset( $_POST[$field_id] ) && empty( $_POST[$field_id] ) )
wc_add_notice(
sprintf( __("Please select a %s as it is a required field.","woocommerce"),
'<strong>' . $label_name . '</strong>'
), "error" );
}
** UPDATE: **
like #LoicTheAztec mentioned here the complete code.
Everything is working fine. The custom input value from cart will be shown on checkout and saved in the DB after order confirmation. But I don't know where I have to put the validation on the cart page when the custom input is empty because everything on cart is on ajax.
// ##########################################
// Add custom fields to a specific selected shipping method
// ##########################################
// Custom function that handle your settings
function delivery_date_settings(){
return array(
'field_id' => 'delivery_date', // Field Id
'field_type' => 'text', // Field type
'field_label' => 'label text', // Leave empty value if the first option has a text (see below).
'label_name' => __("Lieferdatum","woocommerce"), // for validation and as meta key for orders
);
}
// Display the custom checkout field
add_action( 'woocommerce_after_shipping_rate', 'carrier_company_custom_select_field', 20, 2 );
function carrier_company_custom_select_field( $method, $index ) {
if( $method->id == 'flat_rate:2' || $method->id == 'free_shipping:1') {
extract( delivery_date_settings() ); // Load settings and convert them in variables
$chosen = WC()->session->get('chosen_shipping_methods'); // The chosen methods
$value = WC()->session->get($field_id);
$value = WC()->session->__isset($field_id) ? $value : WC()->checkout->get_value('_'.$field_id);
$options = array(); // Initializing
$chosen_method_id = WC()->session->chosen_shipping_methods[ $index ];
if($chosen_method_id == 'flat_rate:2' || $method->id == 'free_shipping:1' ){
echo '<div class="custom-date-field">';
woocommerce_form_field( $field_id, array(
'type' => $field_type,
'label' => $field_label, // Not required if the first option has a text.
'class' => array('form-row-wide datepicker ' . $field_id . '-' . $field_type ),
'required' => false,
), $value );
echo '</div>';
// Jquery: Enable the Datepicker
?>
<script language="javascript">
jQuery( function($){
$('.datepicker input').datepicker({
dateFormat: 'dd.mm.yy', // ISO formatting date
});
});
</script>
<?php
}
}
}
// jQuery code (client side) - Ajax sender
add_action( 'wp_footer', 'carrier_company_script_js' );
function carrier_company_script_js() {
// Only cart & checkout pages
if( is_cart() || ( is_checkout() && ! is_wc_endpoint_url() ) ):
// Load settings and convert them in variables
extract( lieferdatum_settings() );
$js_variable = is_cart() ? 'wc_cart_params' : 'wc_checkout_params';
// jQuery Ajax code
?>
<script type="text/javascript">
jQuery( function($){
if (typeof <?php echo $js_variable; ?> === 'undefined')
return false;
$(document.body).on( 'change', 'input#<?php echo $field_id; ?>', function(){
var value = $(this).val();
$.ajax({
type: 'POST',
url: <?php echo $js_variable; ?>.ajax_url,
data: {
'action': 'delivery_date',
'value': value
},
success: function (result) {
console.log(result); // Only for testing (to be removed)
}
});
});
});
</script>
<?php
endif;
}
// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_delivery_date', 'set_carrier_company_name' );
add_action( 'wp_ajax_nopriv_delivery_date', 'set_carrier_company_name' );
function set_carrier_company_name() {
if ( isset($_POST['value']) ){
// Load settings and convert them in variables
extract( delivery_date_settings() );
if( empty($_POST['value']) ) {
$value = 0;
$label = 'Empty';
} else {
$value = $label = esc_attr( $_POST['value'] );
}
// Update session variable
WC()->session->set( $field_id, $value );
// Send back the data to javascript (json encoded)
echo $label;
die();
}
}
// Save custom field as order meta data
add_action( 'woocommerce_checkout_create_order', 'save_carrier_company_as_order_meta', 30, 1 );
function save_carrier_company_as_order_meta( $order ) {
// Load settings and convert them in variables
extract( delivery_date_settings() );
if( isset( $_POST[$field_id] ) && ! empty( $_POST[$field_id] ) ) {
$order->update_meta_data( '_'.$field_id, esc_attr($_POST[$field_id]) );
WC()->session->__unset( $field_id ); // remove session variable
}
}
// Display custom field in admin order pages
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'admin_order_display_carrier_company', 30, 1 );
function admin_order_display_carrier_company( $order ) {
// Load settings and convert them in variables
extract( delivery_date_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
// Display
echo '<p><strong>' . $label_name . '</strong>: ' . $carrier . '</p>';
}
}
// Display carrier company after shipping line everywhere (orders and emails)
add_filter( 'woocommerce_get_order_item_totals', 'display_carrier_company_on_order_item_totals', 1000, 3 );
function display_carrier_company_on_order_item_totals( $total_rows, $order, $tax_display ){
// Load settings and convert them in variables
extract( delivery_date_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
$new_total_rows = [];
// Loop through order total rows
foreach( $total_rows as $key => $values ) {
$new_total_rows[$key] = $values;
// Inserting the carrier company under shipping method
if( $key === 'shipping' ) {
$new_total_rows[$field_id] = array(
'label' => $label_name,
'value' => $carrier,
);
}
}
return $new_total_rows;
}
return $total_rows;
}
I think the hook you're looking for is woocommerce_check_cart_items, that's the one that validates the cart items before proceeding to checkout.
I'm not 100% sure whether you'll be able to get the field in the same way using $_POST but it's worth a go.
How to redirect page to shop page when last item removed from cart in woocommerce using ajax?
I am tried below code:
function cartitem_after_remove_product($cart_item_key) {
global $woocommerce;
$total_count = $woocommerce->cart->cart_contents_count;
if($total_count == 0)
{
wp_safe_redirect( get_permalink( woocommerce_get_page_id( 'shop' ) ) );
}
}
add_action( 'woocommerce_cart_item_removed', 'cartitem_after_remove_product' );
You have to add this code into you function.php file (your theme):
function cart_empty_redirect_to_shop() {
global $woocommerce, $woocommerce_errors;
if ( is_cart() && sizeof($woocommerce->cart->cart_contents) === 0) {
wp_safe_redirect( get_permalink( wc_get_page_id( 'shop' ) ) );
exit;
}
}
add_action( 'template_redirect', 'cart_empty_redirect_to_shop' );
You can do this via Javascript. WooCommerce has several custom events built in. Your own script can listen to these events and run your own code when they are triggered. One idea would be to check for the class "cart-empty" on updated_cart_totals.
$( document.body ).on( 'updated_cart_totals', function(){
// Check for empty class
if ( $('div.woocommerce-info').hasClass( "cart-empty" ) ) {
window.location.href = "http://www.example.com/shop/";
}
});
I am trying to add a new field on my cart.php file.
I actually want to insert a URL field, so user can set a URL for each order item.
I tried to use a code from another post here but I can't get it to work.
The first and the second functions are working but when it comes to the third one, 'woocommerce_get_item_data' the $cart_item['url'] doesn't contain anything even if I add something in the field and I press Update Cart.
$cart_totals[ $cart_item_key ]['url'] from the first function is outputting the right value when the page load.
I don't know what to do now, thanks for any help.
Here is the code
Add the field
cart/cart.php
<td class="product-url">
<?php
$html = sprintf( '<div class="url"><input type="text" name="cart[%s][url]" value="%s" size="4" title="Url" class="input-text url text" /></div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
functions.php
// get from session your URL variable and add it to item
add_filter('woocommerce_get_cart_item_from_session', 'cart_item_from_session', 99, 3);
function cart_item_from_session( $data, $values, $key ) {
$data['url'] = isset( $values['url'] ) ? $values['url'] : '';
return $data;
}
// this one does the same as woocommerce_update_cart_action() in plugins\woocommerce\woocommerce-functions.php
// but with your URL variable
// this might not be the best way but it works
add_action( 'init', 'update_cart_action', 9);
function update_cart_action() {
global $woocommerce;
if ( ( ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) ) {
$cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : '';
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
if ( isset( $cart_totals[ $cart_item_key ]['url'] ) ) {
$woocommerce->cart->cart_contents[ $cart_item_key ]['url'] = $cart_totals[ $cart_item_key ]['url'];
}
}
}
}
}
// this is in Order summary. It show Url variable under product name. Same place where Variations are shown.
add_filter( 'woocommerce_get_item_data', 'item_data', 10, 2 );
function item_data( $data, $cart_item ) {
if ( isset( $cart_item['url'] ) ) {
$data['url'] = array('name' => 'Url', 'value' => $cart_item['url']);
}
return $data;
}
// this adds Url as meta in Order for item
add_action ('woocommerce_add_order_item_meta', 'add_item_meta', 10, 2);
function add_item_meta( $item_id, $values ) {
woocommerce_add_order_item_meta( $item_id, 'Url', $values['url'] );
}
Add a textarea field to a WooCommerce cart item
First, we just need to add the textarea field. We use the woocommerce_after_cart_item_name hook so our textarea will appear after the product name.
<?php
/**
* Add a text field to each cart item
*/
function prefix_after_cart_item_name( $cart_item, $cart_item_key ) {
$notes = isset( $cart_item['notes'] ) ? $cart_item['notes'] : '';
printf(
'<div><textarea class="%s" id="cart_notes_%s" data-cart-id="%s">%s</textarea></div>',
'prefix-cart-notes',
$cart_item_key,
$cart_item_key,
$notes
);
}
add_action( 'woocommerce_after_cart_item_name', 'prefix_after_cart_item_name', 10, 2 );
/**
* Enqueue our JS file
*/
function prefix_enqueue_scripts() {
wp_register_script( 'prefix-script', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'update-cart-item-ajax.js', array( 'jquery-blockui' ), time(), true );
wp_localize_script(
'prefix-script',
'prefix_vars',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' )
)
);
wp_enqueue_script( 'prefix-script' );
}
add_action( 'wp_enqueue_scripts', 'prefix_enqueue_scripts' );
´´´
At the moment, the user will be able to enter text into the field but the text won’t save. We are going to use some AJAX to save the text.
The code above not only adds the textarea to the cart item, it also enqueues a JavaScript file ready for our AJAX.
It’s assumed that you’re using the code on this page to create a new plugin. If so, you should create a new JS file with the code below and place the file in the root directory of your plugin.
However, if you’ve added the PHP above to your theme functions.php or as a snippet on your site, you’ll need to change the location of the JS file by updating line 21 of the snippet above to identify the location of the JS file.
´´´
(function($){
$(document).ready(function(){
$('.prefix-cart-notes').on('change keyup paste',function(){
$('.cart_totals').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
var cart_id = $(this).data('cart-id');
$.ajax(
{
type: 'POST',
url: prefix_vars.ajaxurl,
data: {
action: 'prefix_update_cart_notes',
security: $('#woocommerce-cart-nonce').val(),
notes: $('#cart_notes_' + cart_id).val(),
cart_id: cart_id
},
success: function( response ) {
$('.cart_totals').unblock();
}
}
)
});
});
})(jQuery);
´´´
Now, when the user types anything, the contents of the text field get sent back to the server ready to be saved as meta data to the cart item.
´´´
<?php
/**
* Update cart item notes
*/
function prefix_update_cart_notes() {
// Do a nonce check
if( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'woocommerce-cart' ) ) {
wp_send_json( array( 'nonce_fail' => 1 ) );
exit;
}
// Save the notes to the cart meta
$cart = WC()->cart->cart_contents;
$cart_id = $_POST['cart_id'];
$notes = $_POST['notes'];
$cart_item = $cart[$cart_id];
$cart_item['notes'] = $notes;
WC()->cart->cart_contents[$cart_id] = $cart_item;
WC()->cart->set_session();
wp_send_json( array( 'success' => 1 ) );
exit;
}
add_action( 'wp_ajax_prefix_update_cart_notes', 'prefix_update_cart_notes' );
function prefix_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
foreach( $item as $cart_item_key=>$cart_item ) {
if( isset( $cart_item['notes'] ) ) {
$item->add_meta_data( 'notes', $cart_item['notes'], true );
}
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'prefix_checkout_create_order_line_item', 10, 4 );
´´´
The prefix_update_cart_notes function does a security check using the WooCommerce cart nonce then saves the content of the textarea as meta data in the cart item. You can check out this article for more information about updating cart meta for items that have already been added to the cart.
Add the custom text to the order meta
Finally, we want to pass our meta data to the order so that we can use it after the customer has checked out. The prefix_checkout_create_order_line_item function takes care of that, iterating through each item and saving notes when it finds them.
https://pluginrepublic.com/how-to-add-an-input-field-to-woocommerce-cart-items/
Using the plugin # http://products.solvercircle.com/woocommerce-one-page-quick-shop/quick-shop/
It seems like if I'm logged in as a regular user in the admin, I can add items to the cart fine. If I add to cart as a guest however it does the ajax request and returns a '1' but the item is never added to the cart.
Does anyone know why it might be so? The other 2 plugins I have on the site I deactivated and can still reproduce this, so I believe this is a bug in this plugin.
EDIT: I confirmed that 'guests can checkout' settings box is applied, so that doesn't seem to be it.
EDIT #2: Here's the add to cart fn:
function wqo_add_prod(pid,vid){
var qty= jQuery('#product_qty_'+vid).val();
if(qty==0){
jQuery('#wqo_alert_info').text('Out of Stock');
jQuery('#wqo_alert_info').show()
setTimeout(function(){jQuery('#wqo_alert_info').hide()}, 1500);
return false;
}
if(vid==0){
qty= jQuery('#product_qty_'+pid).val();
}
var ajax_url = 'http://DOMAIN.com/wp-admin/admin-ajax.php';
jQuery.ajax({
type: "POST",
url:ajax_url,
data : {
'action': 'wqo_addtocart',
'wqo_prod_id': pid,
'wqo_prod_var_id': vid,
'wqo_prod_qty': qty
},
success: function(response){
if(response==1){
jQuery('#wqo_alert_info').text('Added to your cart');
}else{
jQuery('#wqo_alert_info').text(response);
}
jQuery.ajax({
type: "POST",
url:ajax_url,
data : {'action': 'wqo_cart_amount'},
success: function(data){
jQuery('#wqo_cart_price').html(data);
}
});
jQuery('#wqo_alert_info').show()
setTimeout(function(){jQuery('#wqo_alert_info').hide()}, 2000);
}
});
}
EDIT #3: Source for the php callback
function wqo_addtocart() {
global $woocommerce;
$vid=$_POST['wqo_prod_var_id'];
$pid=$_POST['wqo_prod_id'];
$vid=$_POST['wqo_prod_var_id'];
$pqty=$_POST['wqo_prod_qty'];
if($vid==0){
$product = WC_Product_Factory::get_product($pid);
}else{
$product = WC_Product_Factory::get_product($vid);
}
$stock=$product->get_stock_quantity();
$availability = $product->get_availability();
if($availability['class']=='out-of-stock'){
echo 'Out of stock';
exit;
}
if($stock!=''){
foreach($woocommerce->cart->cart_contents as $cart_item_key => $values ) {
$c_item_id='';
$c_stock='';
if($values['variation_id']!=''){
$c_item_id=$values['variation_id'];
}else{
$c_item_id=$values['product_id'];
}
$c_stock=$values['quantity']+$pqty;
if($vid==0 && $pid==$c_item_id && $c_stock>$stock){
$product = WC_Product_Factory::get_product($pid);
echo 'You have cross the stock limit';
exit;
}else if($vid==$c_item_id && $c_stock>$stock){
$product = WC_Product_Factory::get_product($vid);
echo 'You have cross the stock limit';
exit;
}
}
}
if($vid==0){
$z=$woocommerce->cart->add_to_cart($pid,$pqty,null, null, null );
}else{
$z=$woocommerce->cart->add_to_cart($pid, $pqty, $vid, $product->get_variation_attributes(),null);
}
echo '1';
exit;
}
Registers at the bottom of woo-quick-order/includes/wqo-view.php ( also where the above fns are stolen from )
675 add_action( 'wp_ajax_nopriv_wqo_addtocart','wqo_addtocart' );
676 add_action( 'wp_ajax_wqo_addtocart', 'wqo_addtocart' );
EDIT #4: I think it's a session issue. In the first ajax call which calls wqo_addtocart if I do:
var_dump($woocommerce->cart->cart_contents);
It returns the array and the main key is the hash that $z returns.
On the second ajax request however, this is lost:
59 function wqo_cart_amount(){
60 global $woocommerce;
61 var_dump($woocommerce->cart->cart_contents);
62 echo $woocommerce->cart->get_cart_total();
63 exit;
64 }
This returns an empty array. So in between the first and second ajax requests it gets lost.
There is the answer :),
// Hook after add to cart
add_action( 'woocommerce_add_to_cart' , 'repair_woocommerce_2_2_8_session_add_to_cart');
function repair_woocommerce_2_2_8_session_add_to_cart( ){
if ( defined( 'DOING_AJAX' ) ) {
wc_setcookie( 'woocommerce_items_in_cart', 1 );
wc_setcookie( 'woocommerce_cart_hash', md5( json_encode( WC()->cart->get_cart() ) ) );
do_action( 'woocommerce_set_cart_cookies', true );
}
}
WooCommerce have made some change on the add_to_cart function
The problem is here :
https://github.com/woothemes/woocommerce/blob/master/includes/class-wc-cart.php#L978
the right code should be :
if ( did_action( 'wp' ) || defined( 'DOING_AJAX' ) ) {
$this->set_cart_cookies( sizeof( $this->cart_contents ) > 0 );
}