How to call multiple PHP functions through ajax, best practices? - javascript

I have some ajax calls in multiple JavaScript functions. Each one does a post / get to a functions.php file
The functions.php has multiple functions that should correspond with the ones from JavaScript.
For example in js I have:
function one() {
$.ajax({
type: 'POST',
url: 'http://www.example.com/functions.php',
data: vals,
dataType:'json',
success: function (data) {
alert(data);
}
});
}
function two() {
$.ajax({
type: 'POST',
url: 'http://www.example.com/functions.php',
data: othervals,
dataType:'json',
success: function (data) {
alert(data);
}
});
}
function three() {
$.ajax({
type: 'GET',
url: 'http://www.example.com/functions.php',
data: { get_param: 'user' },
dataType:'json',
success: function (data) {
alert(data);
}
});
}
In PHP I have something like this:
if (isset($_POST['city'])) { // this comes from the javascript vals json object
$city = $_POST['city'];
function one() {
// do something
return true;
}
}
function two() {
// do something
return true;
}
if (isset($_GET['get_param']) && $_GET['get_param'] == 'user'){
function three() {
// do something
return true;
}
}
Maybe the PHP side is a bit confusing the way I write it, but in the end I want the function one to only deal with the corespondent function from the PHP file. Obviously they don't need to have the same name.
The PHP functions can return true or false or 1 or 0, and that suppose to be the alerted data alert(data);.
If there is more confusion on what I want please let me know and I'll clarify.

Why not split the PHP functions in separate smaller scripts? They stand for different endpoints in your application and cannot be called together, so they should not be together.

Have you checked out a REST library style. It looks like your doing basically that but a bit more confusing. When i say REST library i do not mean a purely RESTful library, but instead each function in your back-end is navigable via url.
The way your doing it is fine (as long as the functions data does not depend on any other function data (as it could lead to some funny results)). Its just a lot easier to do more of a restful approach. Its fairly simple to set up a rest library.
I just find that doing the whole $_POST[...] and then keep doing it is just cumbersome over time and becomes harder and harder to manage, because there is always some new twist on what is needed then you end up with 100's of methods for taking care of calling back end functions.
MyApi = {
/**
* The API Version
*/
API_VERSION: "0.5",
SITE_URL: "http//myurl.com/",
/**
* The API URL
*/
apiURL: function(tokens) {
return MyApi.SITE_URL + MyApi.API_VERSION + "/api/" + MyApi.apiTokenizer(tokens);
},
/**
* The tokenizer for the API.
*/
apiTokenizer: function(tokens) {
var str = '';
$.each(tokens, function(key, value) {
str += value + "/";
})
return str;
}
}
Thats the javascript for producing new api links, then you could have something like
function callBackend(tokens) {
$.ajax({
type: 'POST',
url: MyLibrary.apiURL(tokens),
dataType:'json',
success: function (data) {
alert(data);
}
});
}
On your backend you would need an .htaccess file like so
RewriteRule 0\.5/(.*) api/_MyApi.php?v=0\.5&state=$1 [QSA]
Then you could write a back end switch statement that would take apart the state (delimiters would be "/") that would navigate you to the end time library call.
<?php
$state = MyParser::Parse($_REQUEST["state"]);
$output = array();
switch ($state) {
case "case1":
//do stuff
break;
}
echo json_encode($output);
?>
That way output is always handled the same way.
Just as a note. That was a very VERY simple and INCOMPLETE implementation, but i find that its a lot easier to maintain than a $_POST that goes to 1 of 100 different files that all have very similar output and all of that.
Cheers! Happy coding

Related

How to use one AJAX handler for multiple purposes with callbacks?

I have a piece of code written in an object. You can see there is a customers object with a function for adding a new customer and a method for making AJAX calls
var sys = {
customers: {
addNew: function(ref, cb = null) {
if (!cb) { // so it can check if the call to this method was for requesting ajax request or handling its response . note i am sending the callback function reference same as the current
core.request({
d: $('form').serialize()
}, 'sys.customers.addNew', ref);
} else {
if (ref.status) {
$('.customers-list').append('<li>' + ref.customer.name + '</li>');
alert('success')
}
}
},
updateRowAfterAdd: function() {
// or i could use this for handling callback by passing its reference instead of the upper same function
}
},
request: function(p = {}, c = null, e = false) {
$.ajax({
url: "/to/my/server",
data: {
p: p
},
type: 'post',
dataType: 'json',
beforeSend: function() {
},
success: function(r) {
if (c != null)
(e ? eval("(" + c + "(r,e));") : eval("(" + c + "(r));"));
}
});
}
}
$(document).on('click', '.addNew', function() {
sys.customers.addNew($(this));
});
The idea in this example is to call the AJAX method by passing a callback function reference for handling the success response.
If you look at the addNew() method it is working in two ways. With the help of the second parameter, cb, it is determining that the call to this function was for sending an AJAX request or handling its response back.
I'm using eval() in the success callback which I know is evil, so I want to understand how I can do this without using eval()?
I have multiple things running on my page which need AJAX calls and I don't want to rewrite each of them.
I also need this for AJAX's beforeSuccess() method as well.
The design pattern you're using seems to be a needless abstraction which is causing more problems that it solves.
A better idea would be to have a central 'service' layer which makes the requests to your server side and handles the responses. If you wanted to abstract this further you could have other domain logic abstractions to handle AJAX requests and responses through a single class, however at that stage I would argue you're far better off using an existing framework to do this for you.
A strong recommendation would be to use Angular, given that its MVC pattern is where you're heading anyway.
If you did want to roll your own simplistic version, then a simple example would look something like this:
$(document).on('click', '.addNew', function() {
services.customers.save($('form').serialize());
});
// in a service layer JS file, far away from UI logic...
let services = {
customers: {
save: requestData => {
$.ajax({
url: '/to/my/server',
type: 'post',
dataType: 'json',
data: $('form').serialize(),
success: services.customers.renderUi
});
},
renderCustomerUi: customerData => {
// optional: extract the UI update logic to your UI layer and pass in the callback as an argument
if (customerData.status) {
$('.customers-list').append('<li>' + customerData.customer.name + '</li>');
}
}
}
}

Why is my Ajax callback being processed too soon?

I have a general ajax function which I'm calling from loads of places in my code. It's pretty standard except for some extra debugging stuff I've recently added (to try to solve this issue), with a global 'ajaxworking' variable:
rideData.myAjax = function (url, type, data, successfunc) {
var dataJson = JSON.stringify(data),
thisurl = quilkinUrlBase() + url;
if (ajaxworking.length > 0) {
console.log(thisurl + ": concurrent Ajax call with: " + ajaxworking);
}
ajaxworking = thisurl;
$.ajax({
type: type,
data: dataJson,
url: thisurl,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
success: function (response) {
ajaxworking = '';
successfunc(response);
},
error: webRequestFailed
});
};
Now, there's one section of my code where a second ajax call is made depending on the result of the first:
getWebRides = function (date) {
var rideIDs = [];
var intdays = bleTime.toIntDays(date);
rideData.myAjax("GetRidesForDate", "POST", intdays, function (response) {
rides = response;
if (rides.length === 0) {
$('#ridelist').empty(); // this will also remove any handlers
qPopup.Alert("No rides found for " + bleTime.DateString(date));
return null;
}
$.each(rides, function (index) {
rideIDs.push(rides[index].rideID);
});
GetParticipants(rideIDs);
});
},
'GetParticipants' (which also calls 'myAjax') works fine - most of the time. But in another part of my code, 'GetWebRides' is itself called directly after another ajax call - i.e. there are 3 calls, each successive one depending on the previous. The 'top-level' call is as follows:
rideData.myAjax("SaveRide", "POST", ride, function (response) {
// if successful, response should be just a new ID
if (response.length < 5) {
// document re-arrangement code snipped here for brevity
getWebRides(date);
}
else {
qPopup.Alert(response);
}
});
so, only when there are three successive calls like this, I'm getting the 'concurrent' catch in the third one:
GetParticipants: concurrent call with GetRidesForDate
and (if allowed to proceed) this causes a nasty probem at the server with datareaders already being open. But why is this only occurring when GetParticipants is called as the third in the chain?
I see, after some research. that there are now other ways of arranging async calls, e.g. using 'Promises', but I'd like to understand what's going on here.
Solved this.
Part of the 'document re-arrangement code' that I had commented out for this post, was in fact calling another Ajax call indirectly (very indirectly, hence it took a long time to find).

jQuery : How can I call $.ajax when a particular condition is met in the nested ajax calls scenario?

Updated Question with Code
I have a situation where I am calling two nested ajax calls one after another. The first ajax call submits a form without the attachment. The result of the first ajax call will create a requestId and using second ajax call I have to attach multiple attachments to the created requestId.
The result of below code, both first and second ajax calls are being called N times of attachment. For ex:- If there are 3 attachments, createRequestId ajax call(first ajax call) called 3 times which creates 3 requestIds. My issue is, createRequestId ajax call needs to be called only one time (first time) and during rest of the loop, only the second ajax call should be called. How can I achieve this in the below code?
Current situation
RequestId 1,Attachment 1
RequestId 2,Attachment 2
RequestId 3, Attachment 3
Expected output
RequestId 1, Attachment 1, Attachment 2, Attachment 3
//loop through number of attachments in the form
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//FileSize Validation
if(fileDetail !=undefined && fileDetail !=null)
{
if(fileDetail.size > 5*Math.pow(1024,2))
{
alert("Please upload the attachment which is less than 5 MB");
return false
}
}
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId'
type:'POST'
data: stringify(formData)
success: function(resObj){
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
var requestId = resObj.requestId;
if(requestId>1 && fileDetail !=undefined && fileDetail !=null) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' +requestId,
type:'POST',
data: fileDetail,
success: function(resObj){
alert("Attachment Successful");
}
error : function(data) {
alert("Failed with the attachment");
}
});
}
},
error: funciton(resObj) {
alert("Some Error Occured");
}
});
});
I know this doesn't really answer your question in full, but if you don't mind me offering a little constructive code review. It's hard to really manage and debug code when it's all thrown into one big function with many lines, especially if you're nesting async calls (you're getting close to nested callback hell). There's a reason code like this can get hard to maintain and confusing.
Lets incorporate some Clean Code concepts which is to break these out into smaller named functions for readability, testability, and maintainability (and able to debug better):
First you don't need all those !== and undefined checks. Just do:
if (fileDetail)
and
if(requestId>1 && fileDetail)
that checks for both null and undefined on fileDetail.
Then I’d start to break out those two ajax calls into several named functions and let the function names and their signatures imply what they actually do, then you can remove unnecessary comments in code as well as once you break them out, typically you can find repeated code that can be removed (such as redundant post() code), and you will find that code you extracted out can be tested now.
I tend to look for behavior in my code that I can try to extract out first. So each one of those ​if​ statements could easily be extracted out to their own named function because any if statement in code usually translates to "behavior". And as you know, behavior can be isolated into their own modules, methods, classes, whatever...
so for example that first if statement you had could be extracted to its own function. Notice I got rid of an extra if statement here too:
function validateFileSize(fileDetail)
if(!fileDetail && !fileDetail.size > 5*Math.pow(1024,2)){
alert("Please upload the attachment which is less than 5 MB");
return false
};
};
So here's how you could possibly start to break things out a little cleaner (this could probably be improved even more but here is at least a start):
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fileName = fObj.attr("name"),
file = document.getElementById(fileName).files[0];
validateFileSize(file);
post(file, 'http://..../createRequestId');
});
// guess what, if I wanted, I could slap on a test for this behavior now that it's been extracted out to it's own function
function validateFileSize(file){
if(!file && !file.size > 5*Math.pow(1024,2)){
alert("Please upload the attachment which is less than 5 MB");
return false
};
};
function post(url, data){
$.ajax({
url: url,
type:'POST',
data: stringify(data),
success: function(res){
showSuccess();
var id = res.requestId;
if(id > 1 && file){
var url = 'http://..../doAttach?fileName=' + file.name + '&requestId=' + id;
postData(file, url);
}
},
error: function(err) {
alert("Some Error Occurred: " + err);
}
});
// I didn't finish this, and am repeating some stuff here so you could really refactor and create just one post() method and rid some of this duplication
function postData(file, url){
$.ajax({
url: url,
type:'POST',
data: file,
success: function(res){
alert("Attachment Successful");
},
error : function(data) {
alert("Failed with the attachment");
}
});
};
// this is behavior specific to your form, break stuff like this out into their own functions...
function showSuccess() {
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
};
I'll leave it here, next you could get rid of some of the duplicate $ajax() code and create a generic post() util method that could be reused and move any other behavior out of those methods and into their own so that you can re-use some of the jQuery ajax call syntax.
Then eventually try to incorporate promises or promises + generators chain those async calls which might make it a little easier to maintain and debug. :).
I think your loop is simply in the wrong place. As it is, you're iterating files and making both AJAX calls once.
Edit: I now show the appropriate place to do extra validations before the first AJAX call. The actual validation was not part of the question and is not included, but you can refer to JavaScript file upload size validation.
var fileSizesValid = true;
$("#myDiv").find("input[type=file]").each(function(index, obj) {
// First loop checks file size, and if any file is > 5MB, set fileSizesValid to false
});
if (fileSizesValid) {
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId',
type: 'POST',
data: stringify(formData),
success: function(resObj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//loop through number of attachments in the form
$("#myDiv").find("input[type=file]").each(function(index, obj) {
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
var requestId = resObj.requestId;
if (requestId > 1 && fileDetail != undefined && fileDetail != null) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' + requestId,
type: 'POST',
data: fileDetail,
success: function(resObj) {
alert("Attachment Successful");
},
error: function(data) {
alert("Failed with the attachment");
}
});
}
})
},
error: function(resObj) {
alert("Some Error Occured");
}
});
}
As a side note, take care where you place your braces. In JavaScript your braces should always be at the end of the line, not the start. This is not a style preference thing as it is most languages, but an actual requirement thanks to semicolon insertion.
Try following code (Just a re-arrangement of your code and nothing new):
//loop through number of attachments in the form
var requestId;
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//FileSize Validation
if(fileDetail !=undefined && fileDetail !=null)
{
if(fileDetail.size > 5*Math.pow(1024,2))
{
alert("Please upload the attachment which is less than 5 MB");
return false
} else if(!requestId || requestId <= 1){
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId'
type:'POST'
data: stringify(formData)
success: function(resObj){
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
requestId = resObj.requestId;
secondAjaxCall(fileDetail);
},
error: funciton(resObj) {
alert("Some Error Occured");
}
});
} else if(requestId>1) {
secondAjaxCall(fileDetail);
}
}
});
function secondAjaxCall(fileDetail) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' +requestId,
type:'POST',
data: fileDetail,
success: function(resObj){
alert("Attachment Successful");
}
error : function(data) {
alert("Failed with the attachment");
}
});
}

Multiple ajax calls fired simultaneously not working properly

I created a site which load every few seconds data from multiple sources via AJAX. However I experience some strange behavior. Here is the code:
function worker1() {
var currentUrl = 'aaa.php?var=1';
$.ajax({
cache: false,
url: currentUrl,
success: function(data) {
alert(data)
},
complete: function() {
setTimeout(worker1, 2000);
}
});
}
function worker2() {
var currentUrl = 'aaa.php?var=2';
$.ajax({
cache: false,
url: currentUrl,
success: function(data) {
alert(data)
},
complete: function() {
setTimeout(worker2, 2000);
}
});
}
The problem is that many times, one of the workers returns NaN. If I change the frequency of calls for, lets say, 2000 and 1900, then everything is working ok and I got almost no NaN results. When those frequencies are same, I get over 80% NaN results for one of the calls. It seems like the browser cannot handle two requests called at exact same time. I use only those two workers, so the browser shouldn't be overloaded by AJAX requests. Where is the problem?
Note that the aaa.php works with the mySql database and do some simple queries base on parameters in url.
All you need is $.each and the two parameter form of $.ajax
var urls = ['/url/one','/url/two', ....];
$.each(urls, function(i,u){
$.ajax(u,
{ type: 'POST',
data: {
answer_service: answer,
expertise_service: expertise,
email_service: email,
},
success: function (data) {
$(".anydivclass").text(data);
}
}
);
});
Note: The messages generated by the success callback will overwrite
each other as shown. You'll probably want to use
$('#divid').append() or similar in the success function.
Maybe, don't use these workers and use promises instead like below? Can't say anything about the errors being returned though without looking at the server code. Below is working code for what it looks like you are trying to do.
This is a simple example but you could use different resolvers for each url with an object ({url:resolverFunc}) and then iterate using Object.keys.
var urls = [
'http://jsonplaceholder.typicode.com/users/1',
'http://jsonplaceholder.typicode.com/users/2',
'http://jsonplaceholder.typicode.com/users/3',
'http://jsonplaceholder.typicode.com/users/4',
'http://jsonplaceholder.typicode.com/users/5',
'http://jsonplaceholder.typicode.com/users/6',
'http://jsonplaceholder.typicode.com/users/7'
]
function multiGet(arr) {
var promises = [];
for (var i = 0, len = arr.length; i < len; i++) {
promises.push($.get(arr[i])
.then(function(res) {
// Do something with each response
console.log(res);
})
);
}
return $.when(promises);
}
multiGet(urls);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Trouble working sync with async javascript

I'm working in my own rss reader using JS, JQuery and PHP for serving the data as JSON. What I'm doing basically is making async calls to my server to get JSONs with the posts, then on 'success' I parse them using a '$.each' and with JQuery load the content in the DOM.
All of this operations were made async, but now I need to call them in a certain order, and when everithin is done THEN calling a function to process the data.
To give you some background on my task, what I'm doing is a query over a small list of RSS sources to get just the very latest post. With them I concat a string and this string is passed to a text-to-speech service.
I've managed to make it work using an arbitrary setTimeout value of 10 seconds, but my goal is to call the function when all the sources have been processed.
This is a basic version of my parser:
function urgent_posts(url) {
$.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
}
What I did to 'make it work' was the following:
$('#test_button').on('click',function(){
urgent_posts(source_1);
urgent_posts(source_2);
urgent_posts(source_3);
//and so on...
urgent_posts(source_n);
setTimeout(function(){
text_to_speech(get_urgent_post_string);
},10000);
});
I tried with no result to make use of the deferred object y JQuery like this:
function urgent_posts(url) {
var deferred = $.Deferred();
$.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
return deferred.promise();
}
And chaining everything together:
$('#test_button').on('click',function(){
urgent_posts(source_1)
.then(urgent_posts(source_2))
.then(urgent_posts(source_3))
.then(function(){
text_to_speech(get_urgent_post_string);
});
});
I'd apreciatte your comments and suggestions.
First, your deferred object is never resolved. You have to add the deferred.resolve() somewhere. Just after the $.each loop looks like a nice place.
Second, $.ajax already returns a promise. So you can just write this :
return $.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
I manage to solve the problem using this article: link
The refactored code looks like this now:
function urgent_posts_feed_1(callback) {
return $.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(feed_1),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
}
I repeat myself (I know it's not cool to do so) and write the following functions manually setting the url:
urgent_posts_feed_2
urgent_posts_feed_3
urgent_posts_feed_4
...
urgent_posts_feed_n
And finally...
urgent_post_feed_1()
.then(urgent_post_feed_2)
.then(urgent_post_feed_3)
//...
.then(urgent_post_feed_n)
.then(function(){
text_to_speech(get_urgent_post_string);
});
This way it works like a charm. Now I have to figure out how to pass parameters to the function and not interfer with the callback.

Categories

Resources