I'm using an API to calculate the shipping cost based on the user's location. The user fills in their location on a Google Places Autocomplete text input field. On filling in, the location's latitude and longitude is captured and sent to the API to calculate the shipping cost. This is all set up and working
The next bit is grabbing the returned cost and pass it via an Ajax call to update the checckout page.
Here's a breakdown of my code so far:
Ajax call for pulling shipping cost from the API:
var input = document.getElementById('sendy_location');
var autocomplete = new google.maps.places.Autocomplete(input);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
var place = autocomplete.getPlace();
document.getElementById('city2').value = place.name;
document.getElementById('cityLat').value = place.geometry.location.lat();
document.getElementById('cityLng').value = place.geometry.location.lng();
//sendy api call
var name = $('#billing_first_name').val()+' '+$('#billing_last_name').val();
var phone = $('#billing_phone').val();
var email = $('#billing_email').val();
var customerLocation = $('#city2').val();
var cityLat = $('#cityLat').val();
var cityLng = $('#cityLng').val();
var amountTotal = $('#amounttotal').val();
var pickupdate = $('#pickupdate').val();
var requestToken = $('#randomvalue').val();
$.ajax({
data:{
"action": 'foo',
"security": "wc_checkout_params.update_order_review_nonce",
"command": "request",
"data": {
"api_key": "my_api_key",
"api_username": "my_api_username",
"vendor_type": 2,
"from": {
"from_name": "client_shop_location",
"from_lat": "client_shop_lat",
"from_long": "client_shop_long",
"from_description": ""
},
"to": {
"to_name": customerLocation,
"to_lat": cityLat,
"to_long": cityLng,
"to_description": ""
},
"recepient": {
"recepient_name": name,
"recepient_phone": phone,
"recepient_email": email,
"recepient_notes": ""
},
"delivery_details": {
"pick_up_date": pickupdate,
"collect_payment": {
"status": false,
"pay_method": 0,
"amount": amountTotal
},
"return": true,
"note": " Sample note",
"note_status": true,
"request_type": "quote"
}
},
"request_token_id": requestToken
},
changeOrigin: true,
type: 'POST',
url:ajaxurl,
success: function(response){
var responseData = JSON.parse(''+response+'');
if(responseData.success == false)
{
$("html, body").animate({ scrollTop: 0 }, "slow");
$('.sendy-errors').html('An error occured. Please refresh the page and try again.');
}
else
{
console.log('success');
location.reload();
console.log(responseData.response.body);
}
},
dataType: 'html',
error: function(response){
$("html, body").animate({ scrollTop: 0 }, "slow");
$('.sendy-errors').html('An error occured. Please refresh the page and try again.');
}
});
});
So basically, i have a text input field with id sendy_location that is bound to Google's Places API to autocomplete locations. On input, grab the latitude and longitude and pass them to the API.
This is the server side code in my functions.php file handling the Ajax:
function foo(){
if ( empty( $_POST['data']['to']["to_lat"] ) || empty( $_POST['data']
['to']["to_long"] ) )
{
wp_die( 'You have to select from the list suggested for you' );
}
$post_data = wp_unslash( array( 'data' => $_POST['data'] ) );
$post_data['command'] = wp_unslash( $_POST['command'] );
$post_data['security'] = wp_unslash( $_POST['security'] );
$post_data['request_token_id'] = wp_unslash( $_POST['request_token_id']
);
// var_dump(json_encode( $post_data ));exit;
$api_url = 'https://apitest.sendyit.com/v1/';
$response = wp_remote_post( $api_url, array(
'headers' => array(
'Content-Type' => 'application/json',
),
'body' => json_encode( $post_data ),
) );
if ( is_wp_error( $response ) )
{
// var_dump(wp_send_json( $response->get_error_message() ));exit;
echo json_encode(array( 'success' => false, 'response' => $response-
>get_eror_message() ));
exit;
}
else
{
$data = json_decode($response["body"]);
$shippingCost = $data->data->amount;
session_start();
$_SESSION['shippingCost'] = $shippingCost;
// var_dump($shippingCost);exit;
echo json_encode(array( 'success' => true, 'response' => $response ));
}
wp_die();
}
Here i grab the shipping cost returned by the API: $_SESSION['shippingCost'] = $shippingCost; and store it in a session. Then i pass it to the woocommerce_cart_calculate_fees hook:
//shipping cost from sendy
add_action('woocommerce_cart_calculate_fees', 'woo_sendy_shipping_cost');
function woo_sendy_shipping_cost()
{
#session_start();
$shippingCost = $_SESSION['shippingCost'];
WC()->cart->add_fee('Shipping ', $shippingCost);
}
So what happens so far is once the user fills in the input field powered by Google's Places API, the shipping cost is pulled and the page is reloaded and the shipping cost reflects on the checkout page, but since the page reloads, the input field is empty(though the shipping cost has been pulled). So is there a better, cleaner way of pulling my shipping cost and updating the checkout page via Ajax?
I have searched around and seen a woocommerce Ajax method:
$( document.body ).bind( 'update_checkout' );
if this is what i need in what context will i use it in my scenario?
UPDATE
Following Sally's comment, i'll attach some screenshots to help explain my use case:
The above screenshot is the immediate view a user will be presented with when they hit the checkout page:
They'll be presented a location input field where they'll fill in the pickup location. This input field is powered by Google Places autocomplete API and on filling the coordinates are captured and sent to the Location API i'm using to calculate the shipping cost(The Ajax script i posted above handles this)
Next, the above screenshot depicts what happens when a user enters their location. The shipping cost is returned and since i've yet to figure out how to update the shipping cost via ajax, for now, i have to store the shipping cost returned in a session and then reload the page as shown by my success callback: location.reload();. Since i can't update the shipping page via ajax, the shipping cost stored in the session:
$data = json_decode($response["body"]);
$shippingCost = $data->data->amount;
session_start();
$_SESSION['shippingCost'] = $shippingCost;
is passed to the shipping page after the reload. Notice now that my input location field is empty despite the fact that i've already received the shipping cost from the API. So if i try to complete my order, I'll get a validation error on the location input field. All this can be solved by an Ajax update on the page.
I hope my edit has provided more information of where i'm at and what i want to achieve. i.e. grab my shipping cost from my API and update the shipping page. Thanks
Related
I can send the data to admin-ajax.php and see that the data exists via my dev tools, however, I cannot seem to be able to take that data and update an ACF field, or perhaps my function to do that is not running.
Here is my PHP:
// define the actions for the two hooks created, first for logged in users and the next for logged out users
add_action( 'wp_ajax_add_dog_to_favorites', 'add_dog_to_favorites' );
add_action( 'wp_ajax_nopriv_add_dog_to_favorites', 'login_to_add_to_favorites' );
// define the function to be fired for logged in users
function add_dog_to_favorites() {
// nonce check for an extra layer of security, the function will exit if it fails
if (!wp_verify_nonce( $_REQUEST['nonce'], 'add_dog_to_favorites_nonce' )) {
exit('Woof Woof Woof');
}
// Get the post_id and user_id value from the form and update the ACF form "test_field".
$post_id = $_REQUEST["post_id"];
$userID = $_REQUEST('user_id');
$current_field = get_field('test_field', $userID);
update_field('test_field', $post_id, $userID);
if ($current_field === false) {
$result['type'] = 'error';
} else {
$result['type'] = 'success';
}
// Check if action was fired via Ajax call. If yes, JS code will be triggered, else the user is redirected to the post page
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
$result = json_encode($result);
echo $result;
} else {
header("Location: " . $_SERVER['HTTP_REFERER']);
}
// don't forget to end your scripts with a die() function - very important
die();
}
// define the function to be fired for logged out users
function login_to_add_to_favorites() {
echo 'You must log in to like';
die();
}
// used here only for enabling syntax highlighting. Leave this out if it's already included in your plugin file.
// Fires after WordPress has finished loading, but before any headers are sent.
add_action( 'init', 'enqueue_add_dog_to_favorites_script' );
function enqueue_add_dog_to_favorites_script() {
// Register the JS file with a unique handle, file location, and an array of dependencies
wp_register_script( 'add_favorites_script', plugin_dir_url( __FILE__ ) . 'add_favorites_script.js', array('jquery') );
// localize the script to your domain name, so that you can reference the url to admin-ajax.php file easily
wp_localize_script( 'add_favorites_script', 'myAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
// enqueue jQuery library and the script you registered above
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'add_favorites_script' );
}
and my JS:
jQuery(document).ready( function() {
jQuery(".not-favorite").click( function(e) {
e.preventDefault();
post_id = jQuery(this).attr("data-post_id");
nonce = jQuery(this).attr("data-nonce");
user_id = jQuery(this).attr("data-user_id");
jQuery.ajax({
type : "post",
dataType : "json",
url : myAjax.ajaxurl,
data : {action: "add_dog_to_favorites", post_id : post_id, nonce: nonce, user_id : user_id},
success: function(response) {
if(response.type == "success") {
console.log('success!');
}
else {
alert("Your like could not be added");
}
}
});
});
});
I have a console.log('success!'); line that should trigger if successful, but it is not triggering. The failure alert is also not triggering, which leads me to believe the function is not running at all? I feel like it's pretty close to working, but just can't get there.
Any help would be greatly appreciated!
Update: Ok. I found that the function is actually exiting because of the nonce verification:
if (!wp_verify_nonce( $_REQUEST['nonce'], 'add_dog_to_favorites_nonce' )) {
exit('Woof Woof Woof');
}
I have tried commenting this block of code out to see what would happen and I get a critical error.
Update: Fixed an error in my code that allows it to work without the nonce check.
$userID = $_REQUEST('user_id'); needs to be $userID = $_REQUEST['user_id'];
I still will need to get the nonce check working, but progress is being made. If anyone knows why that is not working, that would wrap this up!
I have a WordPress store using the Woocommerce plugin. I am currently able to add fees dynamically at checkout using the $woocommerce->cart->add_fee() function, assigned to the woocommerce_cart_calculate_fees hook. However, I would also like to be able to remove fees at checkout as well, but I haven't managed to make it work. I am attempting to trigger a PHP function via AJAX that will then clear the fees using this method.
When I simply echo 'success' from the clearfees() function, the AJAX call completes successfully. However, when I try calling $WC()->cart->remove_all_fees() AJAX fails with a 500 error.
Remove fees AJAX call from Javascript
function clear_fees() {
$.ajax({
type: 'GET',
url: entrada_params.admin_ajax_url,
data: { action : 'clear_fees' }
}).done( function( data ) {
console.log(data);
} )
.fail( function( jqXHR, textStatus, errorThrown ) { // HTTP Error
console.error( errorThrown );
} );
}
The clearfees function in my theme's functions.php
function clearfees() {
$WC()->cart->remove_all_fees();
wp_die();
}
// creating Ajax call for WordPress
add_action('wp_ajax_clear_fees', 'clearfees');
add_action('wp_ajax_nopriv_clear_fees', 'clearfees');
In my searching I've found very little information on the remove_all_fees() function in practice but it seems like the logical solution if I can get it to work.
i am doing this as i am apply fees in function.php
add_action( 'woocommerce_cart_calculate_fees', 'custom_fee_based_on_cart_total', 10, 1 );
function custom_fee_based_on_cart_total( $cart_object ) {
if(isset($_GET['implementation'])){
$charges = (int)$_GET['charges'];
$cart_total = (int)$cart_object->cart_contents_total;
$fees = $cart_total + $charges;
$applyfee = $_SESSION['applyfee'] ? $_SESSION['applyfee'] : 'true';
if($applyfee == 'true'){
$cart_object->add_fee( __( "Implementation Charges", "woocommerce" ), $charges, false );
}else{
$charges = 0;
$cart_object->add_fee( __( "Implementation Charges", "woocommerce" ), $charges, false );
}
}
}
and if i select remove fees option
function clearfees() {
$_SESSION['applyfee'] = 'false';
}
// creating Ajax call for WordPress
add_action('wp_ajax_clear_fees', 'clearfees');
add_action('wp_ajax_nopriv_clear_fees', 'clearfees');
and at last refresh cart page as i get success responce.
I have followed Matt Van Andel's Custom List Table Example to create an admin table which displays enquiries to a website via an external MySql database.
Having implemented the displaying of my data correctly, I have added a select box column which will allow the admin to update the 'status' of the enquiry (Awaiting Response, Responded etc.) and I need this to update my database via AJAX.
I need a change in these select boxes to trigger an AJAX call which will update the database with the new value but I seem to be struggling to link my external AJAX file to my plugins .php file correctly.
I have reached a point where (in the Network tab) I can see I am loading the .js file like so:
Code in list-table.php:
function ajax_test_enqueue_scripts() {
wp_enqueue_script( 'list-table', plugins_url( 'js/list-table.js', __FILE__ ), array('jquery'));
}
add_action( 'admin_enqueue_scripts', 'ajax_test_enqueue_scripts' );
And my AJAX:
jQuery('.status-select').on( 'change', function ajaxSubmit() {
alert("IT WORKED!");
$.ajax({
url: ajaxurl,
type: "POST",
cache: false,
data: this.val()
})
});
At the moment the file is showing but the 'on change' part doesn't seem to be firing (hence the 'alert' in the .js).
Apologies if this question is worded or organised poorly, it is my first time posting!
Hope someone can explain what/ where I am going wrong.
This is quite a specific requirement but for anyone else using custom tables in WordPress and wanting to update an external myqsl database via AJAX - here's how I did it.
The AJAX side of things -
<script>
jQuery('select.status').on('change', function() {
var $statusSelect = jQuery( this );
var $statusSelectCell = $statusSelect.parent();
var enquiryStatusValue = $statusSelect.val();
var currentBackgroundColor = $statusSelectCell.parent().css("backgroundColor");
var ajaxData = {
'action': 'update_status_db',
'currentId': $statusSelect.attr('id'),
'data': enquiryStatusValue
}
jQuery.ajax({
type: "POST",
url: "/wp-admin/admin-ajax.php",
data: ajaxData,
success: function( response ) {
console.log("Data returned: " + response );
$statusSelectCell.parent().css({"background-color": "#b3e6b3"});
$statusSelectCell.parent().animate({backgroundColor: currentBackgroundColor}, 1200);
},
error: function() {
alert("FAILED TO POST DATA!!");
}
});
})
</script>
Note, the users success confirmation in this case is for the specific row to flash green. This is optional.
Next, the PHP to process the AJAX request. This is to be written outside the tables class.
wp_enqueue_script('jquery');
add_action( 'wp_ajax_update_status_db', 'update_status_db_callback' );
function update_status_db_callback(){
global $wpdb;
$newStatus = $_POST['data'];
$currentId = $_POST['currentId'];
$table = 'wp_enquiryinfo';
$result = $wpdb->update( $table, array( 'status' => $newStatus ), array( 'id' => $currentId ));
echo $_POST['data'];
if (!$result) {
echo "FAILED TO UPDATE";
} else {
$result;
echo "WILL UPDATE SUCCESSFULLY - CALL RESULT FUNCTION";
};
wp_die();
}
Here are a couple of the things I was getting wrong originally:
Firstly, the callback function HAS to end with _callback. Secondly, I didn't call the wp_die function at the end of this - this again is required.
Hopefully this may be of use to someone in the future.
I am trying to make it so when I select from a dropdown box at checkout, a fee will be added.
I have the ajax request which returns the information with the fee calculated in, which also updates the current checkout page. But once I place the order, the fee is gone. What is the correct way to add in the fee after an ajax request?
My ajax request:
var data = {
action: 'woocommerce_update_order_review',
security: wc_checkout_params.update_order_review_nonce,
add_order_fee: '55.00',
post_data: $( 'form.checkout' ).serialize()
};
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: data,
success: function( response ) {
if ( response ) {
var order_output = $(response);
$( '#order_review' ).html( $.trim( response ) );
$('body').trigger('updated_checkout');
console.log(response)
}
},
error: function(code){
console.log('ERROR');
},
dataType: 'html'
});
and my plugin function
add_action( 'woocommerce_cart_calculate_fees', 'woo_add_cart_fee' );
function woo_add_cart_fee(){
global $woocommerce;
if(isset($_POST['add_order_fee']))
{
$woocommerce->cart->add_fee( 'Same Day Shipping', '55.00', true, 'standard' );
}
}
I have a similar code running in one of my plugins that adds fees on Checkout. Your code seems to execute fine. I removed the $_POST['add_order_fee'] condition & it added the charges on the checkout page as well as on the page where the order is placed.
I noticed one thing:
$('body').trigger('updated_checkout');
I am using in my plugin:
jQuery("body").trigger("update_checkout");
Notice the 'update_checkout' v/s 'updated_checkout' in your code. Perhaps that could be causing the issue.
I already have a similar problem.
When you send first ajax, system will add fee and second ajax by call "update_checkout" fee will not add because didn't have $_POST.
so my solution is add WC fees to a sessions and add it when "update_checkout" called. This is code that I am using in my plugin:
add_action( 'woocommerce_cart_calculate_fees', array($this,'calFee') );
public function calFee() {
if ( isset( $_SESSION[ $this->id . '_cart_fees' ] ) ) {
WC()->cart->fees = $_SESSION[ $this->id . '_cart_fees' ];
}
}
Please notice you must enable session_start.
add_action('init', array($this,'register_my_session'));
function register_my_session()
{
if( !session_id() )
{
session_start();
}
}
I have a view with one argument, and a set of exposed filters. When the user filters the view, the form is submitted using Ajax, and the filters are appended to the url using location.hash.
My goal is to filter the view upon initial page load, if the filters are present in the location.hash.
Currently, I'm loading the view through an Ajax callback, which works perfectly fine. But the big problem is that Ajax for the view doesn't work.
This is the callback that loads the View.
// Load the view object.
$view = views_get_view('taxonomy_term');
$view->set_display('page');
$view->set_use_ajax(TRUE);
// Pass the current tid as the argument.
$view->set_arguments(array($tid));
// Set the current page.
$view->set_current_page($page);
// Set the exposed filters.
$view->get_exposed_input();
// Execute.
return $view->execute_display();
When I navigate directly to that callback, everything works. But not when I load it through Ajax.
Any ideas?
Update:
It seems that Drupal.behaviors.ViewsAjaxView() doesn't execute for some reason. If I execute it manually, everything works.
Ok, so I've found the answer.
Instead of loading the View from my own callback, I'm now loading the View from the regular ajax callback.
On my page, I create the view object, and add the configuration to Drupal.settings.
$view = views_get_view('taxonomy_term');
$view->set_display('page');
$view->set_use_ajax(TRUE);
$view->set_arguments(array($tid));
$settings = array(
'views' => array(
'ajax_path' => url('views/ajax'),
'ajaxViews' => array(
array(
'view_name' => $view->name,
'view_display_id' => $view->current_display,
'view_args' => check_plain(implode('/', $view->args)),
'view_path' => check_plain($_GET['q']),
'view_base_path' => $view->get_path(),
'view_dom_id' => 1,
'pager_element' => $view->pager['element'],
),
),
),
);
drupal_add_js($settings, 'setting');
views_add_js('ajax_view');
Then I load my js, which adds the current filter from the location.hash to the settings. And finally, loads the View.
var data = {};
// Add view settings to the data.
for (var key in Drupal.settings.views.ajaxViews[0]) {
data[key] = Drupal.settings.views.ajaxViews[0][key];
}
// Get the params from the hash.
if (location.hash) {
var q = decodeURIComponent(location.hash.substr(1));
var o = {'f':function(v){return unescape(v).replace(/\+/g,' ');}};
$.each(q.match(/^\??(.*)$/)[1].split('&'), function(i,p) {
p = p.split('=');
p[1] = o.f(p[1]);
data[p[0]] = data[p[0]]?((data[p[0]] instanceof Array)?(data[p[0]].push(p[1]),data[p[0]]):[data[p[0]],p[1]]):p[1];
});
}
$.ajax({
url: Drupal.settings.views.ajax_path,
type: 'GET',
data: data,
success: function(response) {
var viewDiv = '.view-dom-id-' + data.view_dom_id;
$('#content > div.limiter').html(response.display);
// Call all callbacks.
if (response.__callbacks) {
$.each(response.__callbacks, function(i, callback) {
eval(callback)(viewDiv, response);
});
}
},
error: function(xhr) {
$('#content > div.limiter').html('<p id="artist-load-error">Error text.</p>');
$('#block-request-0').hide();
},
dataType: 'json'
});
This way, the view loads through the regular flow, and everything works as expected =)