Submit multiple POST values in AJAX request - php

I have a TON of AJAX requests that are submitted to my functions.php file using "actions". Then in my functions.php file, I have many code blocks like the one below.
When I look at the developer tools, my functions.php file is being called many times as well (one for each AJAX POST). I'm thinking about using Amazon S3 when I go live and they charge a fee based upon how many POST requests are sent. The other concern I have is that I'm not sure that the way I've coded my functions.php file with all these blocks of code and associated AJAX requests is the best way to do it. Could I put these code blocks in a function or class and somehow combine the AJAX POSTS request into only a few POST requests?
if(isset($_POST['action']) && ($_POST['action'] == 'delete_account')) {
require("config.php");
require("database.php");
$deleteAccount = $_POST['account'];
try {
$results = $db->prepare("DELETE FROM account WHERE account_id_PK = ?");
$results->bindValue(1, $deleteAccount);
$results->execute();
} catch(Exception $e) {
echo "ERROR: Data could not be removed from the database. " . $e;
exit;
}
echo $deleteAccount;
Can you do something like...
$.AJAX({
data: {action= "delete_account", ... action="add_account"...}
});
obviously the bit above is not my actual ajax code, just illustrating my question as best as possible.

You're on the right track yes. However you need to make a couple changes.
First off replace = with : in your Javascript object.
Then you need to make sure you are using unique key names. So either do action1 and action2, or better yet create an array of actions like this:
data: { actions : ["delete_account", "add_account"] }
Then server-side realize that you have an array $_POST['actions'].
So you could do something like this:
if(is_array($_POST['actions']) && in_array("delete_account", $_POST['actions'])) {
// process the delete
Side note: regarding S3 pricing, you do realize that they charge you one penny for two thousand POST requests right? I'm all for making code concise. But if it really complicates things for you, you may rethink this. S3 pricing for POST requests is ridiculously cheap.

Related

JQuery AJAX Post and Refresh Page Content without reload page

Now I know that I am at risk here of asking a duplicate question, however I don't really know what to search for because I am a complete AJAX jQuery noob. Believe me, I have tried searching what I think is the obvious, with no luck so please go easy on me.
I have a php wordpress site which shows prices in GBP as default. At the top, is a select box with onchange="this.form.submit()" which allows the user to change the default currency that all prices are quoted in.
<form method="post" action="">
<select name="ChangeCurrency" onChange="this.form.submit()">
<option value="GBP">GBP</option>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
</select>
</form>
On the home page, are several, what I call "shortcode widgets", each one containing products and price tables. A dashboard if you like.
How it currently works (inefficient):
User changes select.
Form submitted
Homepage reloaded with updated prices in selected currency.
This is not good, because whenever somebody changes currency, the whole page is reloaded (this takes time, transfers approx 1mb without caching, not to mention unnecessary load on the server).
What I want (more efficient):
When the select box is changed, I wish to asynchronously post the form which changes the currency session variable.
Each "shortcode widget" is updated one by one without having to reload the entire page.
Is this something that jquery can do? where do I start?
Just in case it makes any difference so that you can see what I mean, here is the URL so that you can see what I am talking about... http://bit.ly/10ChZys
PART 2:
I have used jQuery and ajax to update the fixTable thanks to a mashup of answers below... I am using session variables to store the users choice, that way, if they return to the site, the option will be saved.
I am having problems with my code because the session variable stored within http://goldealers.co.uk/wp-content/plugins/gd/tables.php?currency=GBP&table=fixTable appears to have a different session_id to the user's session id because the option is no longer stored.
Is there a way of telling the server that they are one and the same session?
SOLUTION
I used Ribot's Solution to start with which worked and solved the initial problem, then extended with NomikOS's solution...
NOTE: this answer show some ideas about the php backend for the AJAX process. It is a complement for the other answers talking about the frontend process.
1.- a mockup to manage AJAX request in WP, just some ideas, ok?
add_action('init', 'process_ajax_callback');
function process_ajax_callback()
{
if ( ! $_REQUEST['go_ajax'])
{
return;
}
try
{
if (isset($_REQUEST['nonce_my_ajax']))
{
$nonce = $_REQUEST['nonce_my_ajax'];
if ( ! wp_verify_nonce($nonce = $_REQUEST['nonce_my_ajax'], 'nonce_my_ajax'))
{
throw new Exception("Nonce token invalid."); // security
}
}
}
catch (Exception $e)
{
$output['result'] = false;
$output['message'] = $e->getMessage();
echo json_encode($output);
exit;
}
$result = true;
$message = '';
switch ($_REQUEST['action'])
{
case 'update_price':
try
{
// update price
// price value comes in $_REQUEST['price']
}
catch (Exception $e)
{
$result = false;
$message = $e->getMessage();
}
break;
case 'other_actions':
break;
}
$output['result'] = $result ? true : false;
$output['message'] = $message;
echo json_encode($output);
exit;
}
2.- don't forget security
// nonce_my_ajax is passed to javascript like this:
wp_localize_script('my_js_admin', 'myJsVars', array(
'nonce_my_ajax' => wp_create_nonce('nonce_my_ajax')
));
3.- in general the needed in the frontend (to use with the backend mockup showed above) is something like:
$("select[name='ChangeCurrency']").live("change", function() {
var price = $(this).val();
$.post(
window.location.href,
{
go_ajax : 1, // parse ajax
action : 'update_price', // what to do
price : price, // vars to use in backend
nonce_my_ajax : myJsVars.nonce_my_ajax // security
},
function(output) {
if ( output.result == true )
// update widgets or whatever
// $("#my_div").html("we happy, yabadabadoo!");
// or do nothing (AJAX action was successful)
else
alert(output.message)
}, 'json');
});
4.- You can use $.get() or $.post() to send/process data to/in server but .load() is not good when you update DB because you can't manage returning messages of failures with the precision of a json response (for example: multiples validation error messages). Just use .load() to load HTML views.
UPDATE:
Set session_id() where can be executed both for normal requests and for ajax requests and at the early stage as possible. I hope you are using a class to wrap your plugin, if not now is the right moment to do it... example:
class my_plugin {
function __construct()
{
if ( ! session_id())
{
session_start();
}
add_action('init', array($this, 'process_ajax_callback'));
// ...
}
function process_ajax_callback()
{
// ...
}
}
UPDATE 2:
About nonce based security:
A security feature available in WordPress is a “nonce”. Generally, a
“nonce” is a token that can only be used once and are often used to
prevent unauthorised people from submitting data on behalf of another
person.
Ref: http://myatus.com/p/wordpress-caching-and-nonce-lifespan/
In this mockup nonce_my_ajax is just an example, indeed it should be more unique like nonce_{my_plugin_name}, or even better nonce_{my_plugin_name}_{what_action} where what_action represents updating user, or inserting new book, etc...
More info about it: WP Codex: WordPress Nonces, WPtuts+: Capabilities and Nonces.
Yes, jQuery can do it using ajax.
First of all, when using ajax, you don't have to post a form to get the data. Ajax in jQuery will load the text data of an url.
You may start by giving your select an id (here id="changeCurrency") and:
$("#changeCurrency").change(function(){
currency = $('#changeCurrency option:selected').val() // get the selected option's value
$("#some_div").load("someurl.php?currency=" + currency);
});
Now read up on jQuery and ajax for what kind of ajax call you need to do that suites your needs the best.
Drop the onchange and add an ID
<select name="ChangeCurrency" id="ChangeCurrency">...
On the page give all your prices a price in your base currency as well as outputting them
<span class="price" data-base="0.12">£0.12</span>
In your JS have a conversion table
// base currency is GBP
// each currency has 0: currency symbol, 1: conversion rate
var currency={"GBP":["£", 1], "USD":["&dollar;", 0.67]};
var usercurrency=currency['GBP'];
and bind an event to the change
$('#ChangeCurrency').on('change', function(){
// post to the server to update it
$.post(...);
// set locally on the page
usercurrency=currency[$(this).val()];
// and change all the values
$('.price').each(function(){
$(this).html(usercurrency[0] + (usercurrency[1] * $(this).data('base')).toFixed(2) );
});
}).trigger('change'); // trigger this to run on page load if you want.
I haven't checked any of this code

Find users likes a page : Best method?

$likes = $facebook->api("/me/likes");
foreach($likes['data'] as $page_likes) {
if($page_likes['id'] == "someid") {
}
}
Is there any simple way to find it instead of looping those hundreds, thousands likes?
Best method (to answer questions like this): Read documentation …!
https://developers.facebook.com/docs/reference/api/user/#likes:
“You can check if a User likes a specific page by issuing an HTTP GET to /PROFILE_ID/likes/PAGE_ID. […] This will return, in the data array, an object with the following fields if the user is connected to the page: […] If the user is not connected to the page, the data array will be empty.”
array_map or array_walk might be more efficient, but it depends on what you want to do with the data...

Organizing a PHP page with a bunch of $_POST's

I am making quite a large online points/purchasing system in PHP and just have a fundamental question.
All the relevant stuff is on a single PHP page within the site, with "includes" from other parts of the site such as shopping cart, points review, products etc..., but throughout the page there are stages where the user clicks a form submit button to pass values via $_POST.
As there is a main page for all this stuff, I have a part at the top of the page where it takes all the POST values and makes decisions based upon them, like so:
if($_POST['add']) {
$product_id = $_POST['add'];
}
if($_POST['remove']) {
$rid = $_POST['id'];
$cart->del_item($rid);
}
if($_POST['empty']){
$cart->empty_cart();
}
if($_POST['purchase']) {
foreach($cart->get_contents() as $item) {
$sql="INSERT INTO wp_scloyalty_orders VALUES (".$user_id.", ".$item['id'].")";
$result=mysql_query($sql);
}
$cart->empty_cart();
unset($_SESSION['cart']);
}
if($_POST['add']) {
query_posts('post_type=prizes&showposts=-1&p='.$product_id.'');
while (have_posts()) : the_post();
$my_meta = get_post_meta($post->ID,'_my_meta',TRUE);
if($calctotalnew > $my_meta['pointsvalue']){
$cart->add_item(get_the_id(), 1, $my_meta['pointsvalue'], get_the_title());
} else {
echo 'You do not have sufficient points to redeem this product...';
}
endwhile;
wp_reset_query();
}
So my question is... is this really a good way to organize a system, having the form actions go to the same page that the form is on, and have a load of IF statements to decide what to do with the POST values?
Thanks! :)
it's generally best to capture separate POST calls (grouped by type) in separate actions. I usually go as follows:
page 1 has a form, which will submit to eg. product.php?action=add. In product.php you can route the 'add' action to the function add_product() (or whatever). Then when the product is added, just header the user back to the main page (or whatever page you'd like). This immediately tackles the problem with refresh-posts (user refreshing the page which will send the same data again).
following mvc imagine you have a controller Product which handles all the product actions. The skeleton could look like this (assuming function action_x will be executed when yoursite.com/product/x is requested):
class Product_Controller {
function action_show() {
}
function action_update() {
}
function action_delete() {
}
}
if your framework supports a default action of some sort you could route your actions:
function action_default() {
if(method_exists(array($this, 'action_'. $_POST['action']))) {
return call_user_method('action_'. $_POST['action'], $this);
}
}
ofcourse the same can be achieved without controller classes;
if(function_exists('action_'. $_POST['action'])) {
call_user_func('action_'. $_POST['action']);
}
function action_show() { }
...
and to illustrate the discussion in the comments;
function action_update() {
// do some update logic, query an UPDATE to mysql etc.
if($result) {
// optionally save a success message
Message::add('Your record has been updated');
header('Location: main_page.php'); // or another intelligent redirect function
} else {
Message::add('Sorry, something went wrong');
header('Location: error_page.php'); // or also main_page
}
}
This will also keep your code cleaner, as updating/adding/deleting stuff is radically different from showing stuff, this will prevent you from mixing up stuff. You could even call the show function from within the update function if you want to skip the redirect.
But in the end it's a matter of choice, led by pragmatism or your framework ;)
I hope this'll explain everything a bit, don't hesitate to ask for clarification
if you want to separate the logic from the interface then you can simple create new file and put the all logical and database related code in that file and include OR require that file in the view file
like
view.php interface file and
logic.php is your logic file then
first line in view.php is
require_once(logic.php');
and all the logic is in this files
simple MVC
Well, it seems I have to explain.
It absolutely does not matter how much IF statements you have in the POST handler. Your current design is okay, and there is no reason to ask nor change it.
The only thing that you may wish to add to your design is a front controller, which will take both entity (cart) and action("add") and call add() method of $cart class. these methods you may store one under another in the class source.
Though it is quite huge improvement, requiring great rethinking of the whole site architecture. So, you may stick with your current one.
As for your other question, how to display errors, here is an answer: php redirection not working

How can I write this Drupal snippet more efficiently?

I'm working on a patch to submit to the Registration Code module for Drupal. In short, is there a more efficient way to write the code below?
if (module_exists('regcode_voucher')) {
$cnfg = variable_get('regcode_voucher_display', array('regform' => 'regform'));
if (empty($cnfg['regform'])) {
return;
}
}
It seems like I should be able to reduce it to one if statement with && combining two conditions, but I haven't found the syntax or the necessary php array function that would allow me to do that.
In case some context helps, the regcode_voucher sub-module allows users to enter their registration code on the user edit page. On our sites, after a "beta" period, we want to simplify the registration form by removing the registration code field; but we'd like users to still be able to enter the code on their account edit page. The code above is part of a patch that allows the regcode's hook_user changes to be bypassed.
Code looks like good, what efficient do you want? Little changes may be:
if (module_exists('regcode_voucher')) {
$cnfg = variable_get('regcode_voucher_display', null);
if ($cnfg) {
// do your actions
}
}
And I don't recommend to merge if..., code should be clear and simpler to understand. If you merge these for optimizing, you win "tiny" milliseconds for real-live processors, but lost your clean code.
Why are you returning an array from variable_get if the variable is not found? variable_get will always return a string or a serialized array (that needs to be unserialized). If I'm missing something, you can use array_key_exists('regcode', variable_get(...)) to check for the array key.
This should work... note returning "false" from variable_get as a default if the variable is not found, which will cause the if conditions to not match. I personally find this more readable than nested if statements (for 3+ conditions I'd nest, though).
if( module_exists('regcode_voucher') && variable_get('regcode_voucher_display', false) ) {
// stuff
}

Curing the "Back Button Blues"

Ever stumbled on a tutorial that you feel is of great value but not quite explained properly? That's my dilemma. I know THIS TUTORIAL has some value but I just can't get it.
Where do you call each function?
Which function should be called
first and which next, and which
third?
Will all functions be called in all files in an application?
Does anyone know of a better way cure the "Back Button Blues"?
I'm wondering if this will stir some good conversation that includes the author of the article. The part I'm particularly interested in is controlling the back button in order to prevent form duplicate entries into a database when the back button is pressed. Basically, you want to control the back button by calling the following three functions during the execution of the scripts in your application. In what order exactly to call the functions (see questions above) is not clear from the tutorial.
All forwards movement is performed by
using my scriptNext function. This is
called within the current script in
order to activate the new script.
function scriptNext($script_id)
// proceed forwards to a new script
{
if (empty($script_id)) {
trigger_error("script id is not defined", E_USER_ERROR);
} // if
// get list of screens used in this session
$page_stack = $_SESSION['page_stack'];
if (in_array($script_id, $page_stack)) {
// remove this item and any following items from the stack array
do {
$last = array_pop($page_stack);
} while ($last != $script_id);
} // if
// add next script to end of array and update session data
$page_stack[] = $script_id;
$_SESSION['page_stack'] = $page_stack;
// now pass control to the designated script
$location = 'http://' .$_SERVER['HTTP_HOST'] .$script_id;
header('Location: ' .$location);
exit;
} // scriptNext
When any script has finished its
processing it terminates by calling my
scriptPrevious function. This will
drop the current script from the end
of the stack array and reactivate the
previous script in the array.
function scriptPrevious()
// go back to the previous script (as defined in PAGE_STACK)
{
// get id of current script
$script_id = $_SERVER['PHP_SELF'];
// get list of screens used in this session
$page_stack = $_SESSION['page_stack'];
if (in_array($script_id, $page_stack)) {
// remove this item and any following items from the stack array
do {
$last = array_pop($page_stack);
} while ($last != $script_id);
// update session data
$_SESSION['page_stack'] = $page_stack;
} // if
if (count($page_stack) > 0) {
$previous = array_pop($page_stack);
// reactivate previous script
$location = 'http://' .$_SERVER['HTTP_HOST'] .$previous;
} else {
// no previous scripts, so terminate session
session_unset();
session_destroy();
// revert to default start page
$location = 'http://' .$_SERVER['HTTP_HOST'] .'/index.php';
} // if
header('Location: ' .$location);
exit;
} // scriptPrevious
Whenever a script is activated, which
can be either through the scriptNext
or scriptPrevious functions, or
because of the BACK button in the
browser, it will call the following
function to verify that it is the
current script according to the
contents of the program stack and take
appropriate action if it is not.
function initSession()
// initialise session data
{
// get program stack
if (isset($_SESSION['page_stack'])) {
// use existing stack
$page_stack = $_SESSION['page_stack'];
} else {
// create new stack which starts with current script
$page_stack[] = $_SERVER['PHP_SELF'];
$_SESSION['page_stack'] = $page_stack;
} // if
// check that this script is at the end of the current stack
$actual = $_SERVER['PHP_SELF'];
$expected = $page_stack[count($page_stack)-1];
if ($expected != $actual) {
if (in_array($actual, $page_stack)) {// script is within current stack, so remove anything which follows
while ($page_stack[count($page_stack)-1] != $actual ) {
$null = array_pop($page_stack);
} // while
$_SESSION['page_stack'] = $page_stack;
} // if
// set script id to last entry in program stack
$actual = $page_stack[count($page_stack)-1];
$location = 'http://' .$_SERVER['HTTP_HOST'] .$actual;
header('Location: ' .$location);
exit;
} // if
... // continue processing
} // initSession
The action taken depends on whether
the current script exists within the
program stack or not. There are three
possibilities:
The current script is not in the $page_stack array, in which case it is
not allowed to continue. Instead it is
replaced by the script which is at the
end of the array.
The current script is in the
$page_stack array, but it is not the
last entry. In this case all
following entries in the array are
removed.
The current script is the last entry
in the $page_stack array. This is
the expected situation. Drinks all
round!
That is a good discussion but more to the point you should be looking into Post Redirect Get (PRG) also known as "Get after Post."
http://www.theserverside.com/patterns/thread.tss?thread_id=20936
If you do not understand my article then you should take a close look at figure 1 which depicts a typical scenario where a user passes through a series of screens – logon, menu, list, search, add and update. When I describe a movement of FORWARDS I mean that the current screen is suspended while a new screen is activated. This happens when the user presses a link in the current screen. When I describe a movement as BACKWARDS I mean that the user terminates the current screen (by pressing the QUIT or SUBMIT button) and returns to the previous screen, which resumes processing from where it left off. This may include incorporating any changes made in the screen which has just been terminated.
This is where maintaining a page stack which is independent of the browser history is crucial – the page stack is maintained by the application and is used to verify all requests. These may be valid as far as the browser is concerned, but may be identified by the application as invalid and dealt with accordingly.
The page stack is maintained by two functions:
scriptNext() is used to process a
FORWARDS movement, which adds a new
entry at the end of the stack and
activates the new entry.
scriptPrevious() is used to process
a BACKWARDS movement, which removes
the last entry from the stack and
re-activates the previous entry.
Now take the situation in the example where the user has navigated to page 4 of the LIST screen, gone into the ADD screen, then returned to page 5 of the LIST screen. The last action in the ADD screen was to press the SUBMIT button which used the POST method to send details to the server which were added to the database, after which it terminated automatically and returned to the LIST screen.
If you therefore press the BACK button while in page 5 of the LIST screen the browser history will generate a request for the last action on the ADD screen, which was a POST. This is a valid request as far as the browser is concerned, but is not as far as the application is concerned. How can the application decide that the request is invalid? By checking with its page stack. When the ADD screen was terminated its entry was deleted from the page stack, therefore any request for a screen which is not in the page stack can always be treated as invalid. In this case the invalid request can be redirected to the last entry in the stack.
The answers to your questions should therefore be obvious:
Q: Where do you call each function?
A: You call the scriptNext()
function when the user chooses to
navigate forwards to a new screen,
and call the scriptPrevious()
function when the user terminates
the current screen.
Q: Which function should be called
first and which next, and which
third?
A: Each function is called in
response to an action chosen by the
user, so only one function is used
at a time.
Q: Will all functions be called in
all files in an application?
A: All functions should be available
in all files in an application, but
only called when chosen by the user.
It you wish to see these ideas in action then you can download my sample application.
The part I'm particularly interested in is controlling the back button in order to prevent form duplicate entries into a database when the back button is pressed.
Your premise is wrong. There is no such thing as "Back Button Blues", if you design your application as a web application. If you design your application without any server side state, you will never run into this problem in the first case. This minimalistic approach to web applications works remarkably well, and is usually known as REST.
# troelskn
If you design your application without any server side state ....
It is not possible to design an effective application which does not have state, otherwise all you have is a collection of individual pages which do not communicate with each other. As maintaining state on the client is fraught with issues there is no effective alternative but to maintain state on the server.
#Marston.
I solved the problem with post/redirect/get but I believe the tutorial has some merit and perhaps Tony Marston can elaborate on it. And how it could be used to solve not necessarily my particular problem but perhaps something similar. Or how is it better than post/redirect/get if the functions can in fact be used in solving my particular problem. I think this will be a good addition to the community here.
if ($_POST) {
process_input($_POST);
header("Location: $_SERVER[HTTP_REFERER]");
exit;
}

Categories