Woocommerce apply discount to cart's total price with ajax - ajax

My Goal : Apply a 10% discount on total cart's price using AJAX.
The context : This discount must be applied only for specific users coming from a given website. The idea is to get their membership number in an input field I added in cart's page. With an API I will check this number and apply the discount or not (considering the issue explained further, I won't detail this checking part here and focus only with the problem).
The idea :
Create a plugin which will add an input field in cart's page with a validation button.
Make an AJAX call when the button is clicked
Apply discount if the user membership number is good (I won't deal with API checking here since it's not the problem)
The issue : So I'm using AJAX to validate this number and then apply a 10% discount on the total cart's price. The problem is I can't find how to update the total.
My JS:
(function($) {
$("body").on("click", ".updateCart", function(e) {
e.preventDefault();
var form = $('#cartAjaxTcs');
var value = form.serialize();
$.ajax({
type:'POST',
data: {
action: 'test',
number: value
},
url: ajaxurl,
success: function(value) {
jQuery("[name='update_cart']").removeAttr('disabled');
jQuery("[name='update_cart']").trigger("click");
console.log(value);
},
error:function(){
console.log('error');
}
});
});
})( jQuery );
My PHP:
<?php
/*
Plugin Name: discount Plugin
Description: Apply 10% discount on cart's price
Author: Aurélien
Version: 1.0.0
*/
/**
* Check if WooCommerce is active
**/
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
class Discount_Plugin{
public static function init() {
add_action('wp_enqueue_scripts', __CLASS__ .'::callback_for_setting_up_scripts');
//Add input field
add_action('woocommerce_cart_collaterals', __CLASS__.'::order_comments_custom_cart_field');
}
public static function callback_for_setting_up_scripts() {
$path = '/wp-content/plugins/discount-plugin/assets/css/style.css';
wp_register_style( 'discountStyle', $path );
wp_enqueue_style( 'discountStyle' );
wp_enqueue_script( 'ajax-script', plugins_url( 'assets/js/discount-plugin.js', __FILE__ ), array('jquery'), '1.0', true );
wp_localize_script( 'ajax-script', 'ajaxurl', admin_url( 'admin-ajax.php' ) );
}
public static function order_comments_custom_cart_field() {
?>
<form id="cartAjaxTcs" class="discount_input_wrapper" method="post" action="/wp-admin/admin-post.php">
<div class="discount_form_field">
<input type="text" name="number" id="number" placeholder="<?php echo 'My number, ie : 4GG524G42';?>">
<label for="number"><?php echo 'Membership number';?></label>
</div>
<button class="updateCart" type="submit"><?php echo 'Update cart';?></button>
</form>
<?php
}
Discount_Plugin::init();
add_action( 'wp_ajax_test', 'test' );
add_action( 'wp_ajax_nopriv_test', 'test' );
function test()
{
//Solution 1 or solution 2
}
}
My 1st solution : I tried to directly reduce total price like this. It changes the total price but not display it, even in backend cart's report where the total price is still the same than before discount.
function test()
{
//This is displayed in console
echo 'test-init';
if (!DOING_AJAX){
return;
}
$total = (int) WC()->cart->get_totals()['total'];
$total *= 0.9;
WC()->cart->set_total($total);
wp_die();
}
My 2nd solution:
I tried to trigger the "update cart" button (already present on the page) and use woocommerce_before_calculate_totals hook to update the price but it is not even called.
Obviously, no hook or filter are exectuted inside my AJAX's action function. I have tested with a simple filter to add a class to the body and it doesn't work inside my action function but works perfectly outside. I put some flags to see if my function was called by AJAX and it is!
//This works well! If I put the same filter in my 'test' function it doesn't work
add_filter( 'body_class', function( $classes ) {
return array_merge( $classes, array( 'test-class' ) );
} );
function test()
{
//This is displayed in console
echo'test-init';
if (!DOING_AJAX){
return;
}
//This does't work
add_filter( 'body_class', function( $classes ) {
return array_merge( $classes, array( 'test-class2' ) );
} );
//The callback function isn't called
add_action( 'woocommerce_before_calculate_totals', 'update_price' );
//This is displayed in console
echo 'test';
wp_die();
}
//Not called
function update_price(){
echo 'update';
}
EDIT :
I re-wrote my explanations to make them a bit more understandable with additional code:
Here are some tries with a negative fee try using woocommerce_cart_calculate_fees hook:
The callback function of woocommerce_cart_calculate_fees hook works perfectly if I use add_action outside of my AJAX action function like this :
add_action( 'wp_ajax_test', 'test' );
add_action( 'wp_ajax_nopriv_test', 'test' );
add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
function woocommerce_custom_surcharge() {
global $woocommerce;
if ( ( is_admin() && ! defined( 'DOING_AJAX' )))
return;
$percentage = 0.1;
$surcharge = ( $woocommerce->cart->cart_contents_total + $woocommerce->cart->shipping_total ) * $percentage;
$woocommerce->cart->add_fee( 'discount', -$surcharge, true, '' );
}
function test(){
//My AJAX action function
}
So this work, the negatif fee is applied but when the page is loaded and that's not what I would like since I want to apply the negative fee when I trigger update cart button, so the idea is to integrate the add_action inside my AJAX action function like this :
add_action( 'wp_ajax_test', 'test' );
add_action( 'wp_ajax_nopriv_test', 'test' );
function woocommerce_custom_surcharge() {
global $woocommerce;
if ( ( is_admin() && ! defined( 'DOING_AJAX' )))
return;
$percentage = 0.1;
$surcharge = ( $woocommerce->cart->cart_contents_total + $woocommerce->cart->shipping_total ) * $percentage;
$woocommerce->cart->add_fee( 'discount', -$surcharge, true, '' );
}
//My AJAX action function
function test(){
echo 'test1'; //flag 1
add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
echo 'test2'; //flag 2
}
The callback function woocommerce_custom_surcharge isn't called at all. Both my flags are displayed in chrome console so it means my action function is correctly called. So my question is : how to make this add_action work inside my action function?

You missed the usage a WC Session variable to set some data and use it in your discount function… The First function below handle your settings and it's loaded everywhere on other functions.
// Settings
function get_membership_settings(){
$discount_percentage = 1; // the discount percentage: 1% here
return array(
'percentage' => $discount_percentage,
'field_key' => 'membership_number', // Field "name" key (or id)
'field_type' => 'text',
'field_label' => __('Membership number', 'woocommerce'),
'button_text' => __('Apply membership number', 'woocommerce'),
'discount_text' => sprintf( __('Membership discount%s', 'woocommerce'), ' ('.$discount_percentage.' %)' ), // for negative fee
'valid_message' => __('Number is valid (text message).', 'woocommerce'),
'unvalid_message' => __('Number not valid (text message).', 'woocommerce'),
'empty_field_text' => __('Please enter your membership number.', 'woocommerce'),
);
}
// Settings + Membership number
function get_membership_data(){
$settings = get_membership_settings();// Load settings
$field_key = $settings['field_key']; // The field Id
$user_value = get_user_meta( get_current_user_id(), $field_key, true ); // Get "membership number" from user data
$session_value = WC()->session->get($field_key); // Get "membership number" from session variable
// Set "membership number" in the array
$settings['field_value'] = empty($session_value) ? $user_value : $session_value;
return $settings;
}
The displayed field on cart (see the screenshot at the end):
// Display a text input field on cart (+ javascript)
add_action('woocommerce_cart_contents', 'display_field_membership_number', 100 );
function display_field_membership_number(){
extract(get_membership_data()); // Load data and settings
echo '<tr><td colspan="6" class="membership" style="padding:0;border-top:16px solid #FFF;">
<style>.message.off,label.hidden{display:none}.membership .message{margin-left:20px}</style>
<div id="'.$field_key.'-wrapper">
<label for="'.$field_key.'" class="hidden"> '.$field_label.' <abbr class="required" title="required">*</abbr></label>
<input type="'.$field_type.'" class="input-'.$field_type.'" name="'.$field_key.'" id="'.$field_key.'" placeholder="'.$field_label.'" value="'.$field_value.'">
<button type="button" class="button">'.$button_text.'</button>
<span class="message off"></span>
</div>
</td></tr>
<tr><td colspan="6" style="padding:0"></td></tr>';
}
The jQuery / Ajax code:
// Function that send the Ajax request | jQuery + Ajax
add_action('wp_footer', 'membership_number_js_script');
function membership_number_js_script() {
if( ! is_cart() ) return; // Only on cart
$field_key = get_membership_settings()['field_key']; // Load field key id
// jQuery Ajax code
?>
<script type="text/javascript">
jQuery( function($){
if (typeof woocommerce_params === 'undefined')
return false;
var s = '#<?php echo $field_key; ?>-wrapper';
$(s+' button').click( function(){
var value = $(s+' input').val();
// Function that handle the display of the message
function handleDisplayMessage( selector, response, error = false ) {
if ( ! error ) {
$.each( $.parseJSON(response), function(index, value){
displayMessage( selector, value, index );
});
} else {
displayMessage( selector, response, 0 );
}
}
// Function that display a message
function displayMessage( selector, response, type ) {
$(selector).hide('0').removeClass('off').html(response).css('color', (type == 1 ? '#03C03C' : '#DC143C')).show();
setTimeout(function() {
$(selector).hide();
}, 3000 );
}
$.ajax({
type: 'POST',
url: wc_cart_params.ajax_url,
data: {
'action': '<?php echo $field_key; ?>',
'<?php echo $field_key; ?>': value,
},
success: function (response) {
handleDisplayMessage( (s+' .message'), response );
$(document.body).trigger("added_to_cart"); // refresh cart
},
error:function(error){
handleDisplayMessage( (s+' .message'), ('A problem occured (error: '+error+')'), true );
}
});
});
});
</script>
<?php
}
The PHP WordPress Ajax receiver function:
// Get the ajax request and set value to WC session (and the field validation)
add_action( 'wp_ajax_membership_number', 'set_membership_number_and_validation' );
add_action( 'wp_ajax_nopriv_membership_number', 'set_membership_number_and_validation' );
function set_membership_number_and_validation() {
extract(get_membership_settings()); // Load and extract settings
if( isset($_POST[$field_key]) && ! empty($_POST[$field_key]) ) {
## HERE BELOW, SET YOUR CODE (that checks membership number for validation)
$validation = true; // "true" when validated (or false if not)
// Set membership number to a WC session variable
WC()->session->set($field_key, $_POST[$field_key]);
// Send response back
echo json_encode( ($validation ? [1 => $valid_message] : [0 => $unvalid_message]) );
} else {
// Send response back
echo json_encode( [0 => $empty_field_text] );
}
die();
}
The discount part:
there are multiple ways to make a discount (here are two of them, using different hooks):
1. Discount based cart subtotal + shipping tolal (negative fee):
// 1. Percentage discount for Membership (with a negative fee)
add_action( 'woocommerce_cart_calculate_fees', 'add_membership_discount' );
function add_membership_discount( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
extract(get_membership_data()); // Load and extract settings + membership numver
if( ! empty($field_value) ) {
// Calculation
$discount = ( $cart->get_cart_contents_total() + $cart->get_shipping_total() ) * $percentage / 100;
$cart->add_fee( $discount_text, -$discount ); // Add a discount
}
}
2. Discount on cart item price:
// 2. Percentage discount for Membership (on cart items price)
add_action( 'woocommerce_before_calculate_totals', 'add_membership_cart_item_discount' );
function add_membership_cart_item_discount( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
extract(get_membership_data()); // Load and extract settings + membership numver
if( ! empty($field_value) ) {
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
// Get the real WC_Product Object
$product = wc_get_product($cart_item['data']->get_id());
$price = $product->get_price(); // The real product price
$discounted_price = $price * ( 1 - ( $percentage / 100 ) ); // Calculation
$cart_item['data']->set_price($discounted_price); // Set the discounted price
}
}
}
All code goes in functions.php file of your active child theme (or active theme). Tested and works.
Similar threads:
First order discount for guest customers checking Woocommerce orders billing email
Dynamic discount using AJAX and Fee API in Woocommerce checkout page

Related

Custom admin order button to add an item works, but item does not appear

Adding an item takes eight clicks, in the WC admin order page, so I thought I would add custom buttons for some of my most commonly-added products to do it all at once. I found several hints that get me most of the way there -- the item is indeed added to the order, but it doesn't appear until you reload the page, or hit Recalculate. Calling $('.calculate-action').trigger('click'); at that point does work but that requires yet another click ("OK"), an annoyance I am trying to avoid. The regular "Add Item(s)" and then "Add products" ... "Add" (with all the faffing about to select the product) just gets on with it at this point. How?
Is there a different approach, or maybe one more call I need to add?
scna_orderadmin_add_product.js:
(function( $ ) {
'use strict';
$('.inside').on('click','#scna_item_button', function(){
// get the order_id from the button tag
var order_id = $(this).data('order_id');
// get the product_id from the button tag
var product_id = $(this).data('product_id');
// send the data via ajax to the sever
$.ajax({
type: 'POST',
url: mb_script_var.ajaxurl,
dataType: 'json',
data: {
action: 'scna_orderadmin_add_product',
order_id: order_id,
product_id: product_id
},
success: function (data, textStatus, XMLHttpRequest) {
// if(data.error == 0)
// {
// trigger the "Recalculate" button to recalculate the order price
// and to show the new product in the item list
// $('.calculate-action').trigger('click');
// }
// show the control message
alert(data.msg);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
})( jQuery );
functions.php:
/** Add a custom button to the admin order page to add SCNA Membership.
*
* To save clicks when creating a new order from a paper form.
*/
function scna_orderadmin_add_product_buttons($order)
{
// Add a button that adds SCNA 1- and 2-year memberships, to save steps.
// the button tag gets as data attributes your product id and the order id
// later we will grab this data and send them to the server
echo '<button id="scna_item_button" type="button" class="button add-special-item" data-order_id="'. esc_attr($order->get_id()) .'" data-product_id="112" >Add SCNA 1-year</button>';
echo '<button id="scna_item_button" type="button" class="button add-special-item" data-order_id="'. esc_attr($order->get_id()) .'" data-product_id="113" >Add SCNA 2-year</button>';
}
/**
* hook to add the button
*/
add_action('woocommerce_order_item_add_action_buttons', 'scna_orderadmin_add_product_buttons');
/**
* Add javascript
*/
function scna_orderadmin_add_product_js_file()
{
wp_enqueue_script( 'scna_orderadmin_add_product_js', get_stylesheet_directory_uri() ."/js/scna_orderadmin_add_product.js", array('jquery'), NULL, true );
// send the admin ajax url to the script
wp_localize_script( 'scna_orderadmin_add_product_js', 'mb_script_var', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
}
/**
* hook to add the javascript file
*/
add_action( 'admin_enqueue_scripts', 'scna_orderadmin_add_product_js_file' );
/**
* Ajax callback
*/
function scna_orderadmin_add_product()
{
/////////////////////////////////////////////////////////////////////////////
/// Attention, to keep the example simple we will not check any ajax data. //
/////////////////////////////////////////////////////////////////////////////
//
// the data from the ajax call
$order_id = intval($_POST['order_id']);
$product_id = intval($_POST['product_id']);
//getting order Object
$order = wc_get_order($order_id);
// gettting the product
$product = wc_get_product($product_id);
$back_data = array('error' => 0, 'msg' => '');
if($order !== false AND $product !== false)
{
// Add the product to the order
$order->add_product($product, 1);
// Save the order data
$order->calculate_totals();
//$order->save();
$back_data['msg'] = 'The item was added';
}
else
{
$back_data['error'] = 1;
$back_data['msg'] = 'No item was added';
}
wp_send_json( $msg );
}
/**
* hook to add the ajax callback
*/
add_action( 'wp_ajax_scna_orderadmin_add_product', 'scna_orderadmin_add_product' );

Why loadmore button is dissapearing aftre click instead of showing posts?

Here I've found a tutorial how to make the load more button with ajax for woocommerce. I copy almost all, except of styles.
Step 1. Load more button
global $wp_query; // you can remove this line if everything works for you
// don't display the button if there are not enough posts
if ( $wp_query->max_num_pages > 1 )
echo '<div class="misha_loadmore">More posts</div>'; // you can use <a> as well
My variant with a hook looks like that
add_action('woocommerce_after_shop_loop','cosmetic_load_more',9);
function cosmetic_load_more(){?>
<div class="d-flex justify-content-center loadmore-wrapper">
<?php
global $wp_query; // you can remove this line if everything works for you
// don't display the button if there are not enough posts
if ( $wp_query->max_num_pages > 1 )
echo '<button class="loadmore misha_loadmore">See more</button>'; // you can use <a> as well
?>
</div>
<?php }
Step 2. Enqueue jQuery and myloadmore.js. Pass query parameters to the script
function misha_my_load_more_scripts() {
global $wp_query;
// In most cases it is already included on the page and this line can be removed
wp_enqueue_script('jquery');
// register our main script but do not enqueue it yet
wp_register_script( 'my_loadmore', get_stylesheet_directory_uri() . '/myloadmore.js', array('jquery') );
// now the most interesting part
// we have to pass parameters to myloadmore.js script but we can get the parameters values only in PHP
// you can define variables directly in your HTML but I decided that the most proper way is wp_localize_script()
wp_localize_script( 'my_loadmore', 'misha_loadmore_params', array(
'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php', // WordPress AJAX
'posts' => json_encode( $wp_query->query_vars ), // everything about your loop is here
'current_page' => get_query_var( 'paged' ) ? get_query_var('paged') : 1,
'max_page' => $wp_query->max_num_pages
) );
wp_enqueue_script( 'my_loadmore' );
}
add_action( 'wp_enqueue_scripts', 'misha_my_load_more_scripts' );
Step 3. myloadmore.js – what is inside?
jQuery(function($){ // use jQuery code inside this to avoid "$ is not defined" error
$('.misha_loadmore').click(function(){
var button = $(this),
data = {
'action': 'loadmore',
'query': misha_loadmore_params.posts, // that's how we get params from wp_localize_script() function
'page' : misha_loadmore_params.current_page
};
$.ajax({ // you can also use $.post here
url : misha_loadmore_params.ajaxurl, // AJAX handler
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
button.text('Loading...'); // change the button text, you can also add a preloader image
},
success : function( data ){
if( data ) {
button.text( 'More posts' ).prev().before(data); // insert new posts
misha_loadmore_params.current_page++;
if ( misha_loadmore_params.current_page == misha_loadmore_params.max_page )
button.remove(); // if last page, remove the button
// you can also fire the "post-load" event here if you use a plugin that requires it
// $( document.body ).trigger( 'post-load' );
} else {
button.remove(); // if no data, remove the button as well
}
}
});
});
});
Step 4. wp_ajax_
function misha_loadmore_ajax_handler(){
// prepare our arguments for the query
$args = json_decode( stripslashes( $_POST['query'] ), true );
$args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
$args['post_status'] = 'publish';
// it is always better to use WP_Query but not here
query_posts( $args );
if( have_posts() ) :
// run the loop
while( have_posts() ): the_post();
// look into your theme code how the posts are inserted, but you can use your own HTML of course
// do you remember? - my example is adapted for Twenty Seventeen theme
get_template_part( 'template-parts/post/content', get_post_format() );
// for the test purposes comment the line above and uncomment the below one
// the_title();
endwhile;
endif;
die; // here we exit the script and even no wp_reset_query() required!
}
add_action('wp_ajax_loadmore', 'misha_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'misha_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}
Does it for for you? Where can I be wrong?

How to update billing fields on Woocommerce checkout with AJAX

So, I want to unset a few billing fields depending on shipping method, however it doesn't work unless I reload the whole checkout page. How to reload the billing fields using AJAX?
/*
* Remove fields for chosen shipping method
*/
add_filter( 'woocommerce_checkout_fields', 'awoohc_override_checkout_fields' );
function awoohc_override_checkout_fields( $fields ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( 'local_pickup:4' === $chosen_methods[0] ) {
unset( $fields['billing']['billing_address_1'] );
unset( $fields['billing']['billing_address_2'] );
}
return $fields;
}

Woocommerce Ajax add to cart programmatically

I have this button:
bla bla
This button is located on my homebage and is generated by a page builder. What I want to accomplish is when somebody click on the button, it will take them on the checkout, and add a product to the cart. I know that this can be accomplished via a URL, but I need to have this button do other things as well(client idea).
So right now I'm stucked here:
JQuery
jQuery(document).ready(function($){
$(".remove-all").click(function(){
$.ajax({
url: "wp-admin/admin-ajax.php",
data: 'myajax'
});
});
});
PHP
add_action('wp_ajax_myajax', 'myajax');
add_action('wp_ajax_nopriv_myajax', 'myajax');
function myajax() {
global $woocommerce;
$product_id = 264;
$woocommerce->cart->add_to_cart($product_id);
die();
}
I'm a javascript noob, so can you please point me to the right direction, or maybe give me a hint on what I'm doing wrong.
Thanks in advance!
As I mentioned in the comments, you can pretty much borrow from core WooCommerce functions.
First, here's the button we'll be trying to ajaxify:
bla bla
Secondly, we'll load our custom script and pass it important variables such as the admin ajax and checkout urls.
add_action( 'wp_enqueue_scripts', 'so_load_script', 20 );
function so_load_script(){
wp_enqueue_script( 'so_test', plugins_url( 'js/test.js', __FILE__ ) );
$i18n = array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'checkout_url' => get_permalink( wc_get_page_id( 'checkout' ) ) );
wp_localize_script( 'so_test', 'SO_TEST_AJAX', $i18n );
}
Now we will write our ajax callbacks, which is copied almost verbatim from WooCommerce core with only a few small modifications:
add_action('wp_ajax_myajax', 'myajax_callback');
add_action('wp_ajax_nopriv_myajax', 'myajax_callback');
/**
* AJAX add to cart.
*/
function myajax_callback() {
ob_start();
//$product_id = 264;
$product_id = 34;
$quantity = 1;
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
$product_status = get_post_status( $product_id );
if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity ) && 'publish' === $product_status ) {
do_action( 'woocommerce_ajax_added_to_cart', $product_id );
wc_add_to_cart_message( $product_id );
} else {
// If there was an error adding to the cart, redirect to the product page to show any errors
$data = array(
'error' => true,
'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
);
wp_send_json( $data );
}
die();
}
And finally the contents of test.js:
jQuery(document).ready(function($){
$(".test-button").click(function(e){
e.preventDefault(); // Prevent the click from going to the link
$.ajax({
url: wc_add_to_cart_params.ajax_url,
method: 'post',
data: {
'action': 'myajax'
}
}).done( function (response) {
if( response.error != 'undefined' && response.error ){
//some kind of error processing or just redirect to link
// might be a good idea to link to the single product page in case JS is disabled
return true;
} else {
window.location.href = SO_TEST_AJAX.checkout_url;
}
});
});
});
You likely need to cancel/prevent the default action of clicking the link, and then redirect once the AJAX call finishes (this is just one way to do it):
jQuery(document).ready(function($){
$(".remove-all").click(function(e){
e.preventDefault(); // Prevent the click from going to the link
var $redirect = $(this).attr('href');
$.ajax({
url: "wp-admin/admin-ajax.php",
data: 'myajax',
success: function () {
window.location.href = $redirect;
}
});
});
});
PHP
add_action('wp_ajax_myajax', 'myajax');
add_action('wp_ajax_nopriv_myajax', 'myajax');
function myajax() {
$product_id = 264;
// Avoid using the global $woocommerce object
WC()->cart->add_to_cart($product_id);
die();
}

CodeIgniter 2.1 form_validation library not working with ajax

I had a form which when submitted, either returned an error if there was one, or it subbmited the data to the DB table if validation passed, and it worked fine.
Now I wanna do the same thing with ajax, but the validation seems to always return false, no matter if I type in something or not.
My controller:
function add_new_category_ajax()
{
// make sure it's the admin, else redirect
if ( ! $this->session->userdata('is_admin') ) {
redirect('admin/login');
exit();
}
// load form validation library
$this->load->library('form_validation');
// validate fields
$this->form_validation->set_rules('category_title', 'Category Title', 'trim|required');
// if validation failed
if ($this->form_validation->run($this) == FALSE)
{
// dummy var
$data['dummy'] = '';
// generate error message
$data['error'] = '<p class="errormsg">You must enter a category title.</p>';
// load the view
$this->load->view('new_category_view', $data);
}
else
// validation passed, add new category
{
// get category_title
$ct = $this->input->post('category_title');
// check if category title exists
$cte = $this->mdl_categories->category_title_exists($ct);
if ( $cte )
{
$data['msg'] = '<p class="errormsg">That category title already exists, please choose another.</p>';
}
else // it does not, proceed with insert
{
// prepare data
$data = array(
'title' => $ct
);
// add new page
$this->db->insert('categories', $data);
// show success message and add another page link
$flag = $this->db->affected_rows();
if ($flag == 1)
{
$data['msg'] = '<p class="successmsg">The category has been added!</p>';
$data['aac'] = '<br><p><a href='.base_url().'admin/add-new-category/>Add another category +</a></p>';
}
}
$data['msg'] = '<p class="successmsg">The category has been added!</p>';
// load the view
$this->load->view('new_category_view', $data);
} // end else
}
My ajax:
$('body').on('click', 'a.submitnewcat', function(e) {
e.preventDefault();
// alert('code');return;
$('#ajaximg img').addClass('act');
setTimeout(function() {
$("#ajax").load('<?php echo site_url('admin/add-new-category-ajax'); ?>');
$('#ajaximg img').removeClass('act');
}, 200)
});
Linking to admin/add-new-category-ajax is not a mistake, routes are set up that way.
Why does it always return a failed validation?
As I said in the comment, you need an extra parameter to make load uses POST method instead of GET method :
$("#ajax").load('<?php echo site_url('admin/add-new-category-ajax'); ?>', { "category_title " : "myTitle" });
The run method doesn't take a parameter if I'm not wrong :
if ($this->form_validation->run() == FALSE)

Resources