I have an decent sized HTML form, on top of this is Javascript which can create many more 'records' to certain parts of the form. In total we could be talking 50+ INPUT elements.
I'm wondering of the best way to process all of this data by PHP:
Pass it as a normal INPUT elements via POST and let the PHP decode it all.
Or lose the names of the form input elements so they are not presented in the POST and then use Javascript on the submit event to encode all the required input elements into an object and then encode that object into JSON and pass via a single hidden input element.
I'm thinking the later would allow me to write clearer code on the PHP side, and it would basically be passed as an object once run through json_decode(), and would be more abstract from html changes. Whilst the former requires no javascript, but would need to be kept in sync with the html.
Or another method I've not thought of.
Which do you think would be the best way?
You don't even need JS to write "clearer code". You can pass arrays of data to PHP from forms in this manner. This is even clear enough. Taken from my other answer, you can do this (the previous question was about checkboxes, but should work with any input type):
<input type="checkbox" name="answers[set1][]" value="apple" /> //imagine checked
<input type="checkbox" name="answers[set1][]" value="orange" /> //imagine checked
<input type="checkbox" name="answers[set1][]" value="grape" />
<input type="checkbox" name="answers[set2][]" value="airplane" /> //imagine checked
<input type="checkbox" name="answers[set2][]" value="train" /> //imagine checked
<input type="checkbox" name="answers[set2][]" value="boat" />
<input type="checkbox" name="answers[solo]" value="boar" /> //single type value. note that there is no [] in the end
end up like this in the request array (like say POST):
$_POST[] = array(
'answers' => array(
'set1' => array('apple','orange'), //unchecked items won't be included
'set2' => array('airplane','train'), //unchecked items won't be included
'solo' => 'boar'
)
);
You do not need an abstraction layer between html and php. Nobody does.
Use normal html behavior and retrieve if from php the normal way.
If for some reason you really need an object instead the normal $_POST array, call
$POSTobject = json_decode(json_encode($_POST), FALSE);
In my experience I found that for large to very large forms the easiest was to keep the input fields in their own form, with tags, but rather to give the input fields a pattern name, like somefunctionalname_01, somefunctionalname_02, etc, and have the server-side processor to look for this class of variables.
Your business code can then sort the parameters according to the category they belong to, and you can use them at leisure in a structured way later.
e.g.
$MyBroadCategories = array('somefunctionalname', 'someotherfunctionalname');
foreach($MyBroadCategories as $Cat) {
foreach($_POST as $key => $val) {
if (preg_match('/^' . $Cat . '_(\d+)$/', $key, $bits)) {
// Please business code here with this particular class of variables
// Or add them to an specific array of candidates for later use.
}
}
}
}
}
Attention, in all cases, if you exceed the maximum number of parameters handled by your server (defaults to 1000), think of adjusting the max_input_vars parameter of your PHP configuration.
Related
I understand that if I use a checkbox value of name[], then I will receive a data array on the server (when using PHP), named 'name[]'. This has worked fine for me, but I'm running into some URL sizes that could cause issues with less robust IE browsers and all the encoded square braces are killing me in this area, easily causing the URL length to be at least 4-6 times longer than what it could possibly be, if another way were available. Is there a reliable method using javascript (jquery syntax even better) to intercept a checkbox forms values and convert them into something like this:
"&checkboxarray=1-23-45-13-67"
I figure that on the other end I can easily explode $_GET['checkboxarray'] into an actual array and go from there as I usually do with matching the selection array against the options array, etc... I just don't know if it's possible or how to create alter the submit process.
Side note, isn't passing "name[]" to a URL non-standards compliant anyways? Every browser I've used auto encodes it, but not Mozilla, however it seems to work fine.
EDIT: I need to create paginated links, which is why I'm using GET, instead of POST. This is also and industrial search, very comprehensive, lots of power user options.
UPDATE & Answer: I managed to come up with my own answer. For anyone else who wants to take advantage of $_GET's easy pagination workflow but you need to pass large data arrays and are worried about URL length, here's a simplistic way to compact it all down into one variable with dash separated values:
NOTE: I HIGHLY suggest you first make sure any dynamic arrays generated from queries start with 1 rather than 0 if your going to recheck values after submit, here's how, since 0 can be a real pain in the neck to work with in PHP conditional statements:
$your_array= array();
array_unshift($your_array,'');
unset($your_array[0]);
In your HTML code, set all checkbox input names to "something[]" and underneath this set of inputs, create a hidden input with the name "something", I suggest you make them match, but I suppose you could use another name, just make sure the hidden one is missing the square braces, also set the hidden input value to "":
<input type="text" name="something[]" value="1">
.....
<input type="text" name="something[]" value="20">
<input type="hidden" name="something" value="">
Javascript: NOTE, requires jquery... this grabs all the "something[]" input values and forms a dashed array while killing off "something[]" values from being submitted and only submitting "something".
$('#submitbutton').click(function(){
var searchIDs = $('input[name="something[]"]:checked').map(function(){
return $(this).val();
}).get();
var IDstring = searchIDs.toString();
var newvar = IDstring.replace(/,/g, '-');
$('input[name="something"]').val(newvar);
$('input[name="something[]"]:checkbox').prop("checked", false);
});
On the server side, simply explode the 'something' value from $_GET.
$somethingArray = explode('-',$_GET['something']);
There it is! Hope it helps someone in the future make their GET sent arrays more compact. Bonus: Avoids sending unsafe characters in the URL, Mozilla doesn't appear to auto encode square braces, at least not my version of it on Linux Mint ;)
Update:
I just implemented this code on a big country checkbox form with 284 possible selections. With my old code, even using 'c[]' as the name, my character count was around 3100 characters, with the new approach, my character count now rings in at just 1109. Worth the effort.
You can use POST instead of GET method.
GET has URL length limitations.
POST is useful in passing long data, e.g. an array in your case.
There is a default limit of POST method which is 2MB which is way higher than GET. If needed, it can easily be increased in php.ini file post_max_size 10MB.
Replace $_GET with $_POST in your script.
How can I protect my ajax form of inspect element?
I'm creating a system and I forgot that the user can edit the value of a hidden input using inspect element.
Please look this code:
<input type="hidden" class="id" value="<?=$array["id"]?>" />
And the ajax:
var id = $(this).parent().find('.id').val();
If the id is 5 and I edit to 2 the ajax will get 2
Can anyone help me?
You simply can't. You can never assume the data send from the client is trustworthy. That's why you should ALWAYS check input on the server. Checking it on the client is just an extra measure to improve efficiency. It's NEVER an alternative and should never be the ONLY security measure.
But, as an additional security. You could add a second field, which contains a hashed value of the hidden field (salted of course, otherwise it is pointless). Then check on the server if the value still matches up with the hash (by doing the hashing with the same salt value again).
But you shouldn't trust that too much either.
<input type="hidden" name="id" class="id" value="<?=$array["id"];?>" />
<input type="hidden" name="hash" class="id" value="<?=md5($array["id"] . "somerandomstring");?>" />
Then on the server check if it still matches up and it hasn't been tampered with.
$id = $_GET["id"];
$hash = $_GET["hash"]
if(md5($id . "somerandomstring") == $hash) {
// it's okay
} else {
// he changed it
}
When you receive the id and the hash value from the client over AJAX, just check if hashing the id again with the same salt value gives the same hash. If not, it means he changed the id.
But you should still check if the user is allowed to work on the item with this id. And probably you should use sha instead of md5, because md5 is highly insecure.
The salt must be secret and the same on both places. A "salt" value is a random piece of code which is added to the value you want to hash, so hackers have a harder time trying to bruteforce your original value based on the hash (but not impossible).
Add a timeout of a few minutes and it might be safe enough.
This has driven me "doo-lally" this afternoon!
A vendor (Zaxaa) uses a multi-dimentional form thus:
<form method="post" name="zaxaa" action="xxxx">
<input type="text" name="products[0][prod_name]" value="ABC">
<input type="text" name="products[0][prod_type]" id="pt" value="FRONTEND">
</form>
** This is my understanfing of how a multdimentional array is set up, and it seems to pass the variables to the server OK.
However, dependant on what other inputs are set to on the test form, the [prod_type] (and others) may need to change to "OTO" This is obviously going to be a javascript function, (but not the variant that starts with "$" on code lines ... whatever that type is!)
I have tried
document.zaxaa.products[0].prod_type.value
document.getElementById('products[0][prod_type]').value
document.getElementsByName('products[0][prod_type]').value
but in everycase, I get "products is not defined". (I have simplified the form as there are ten product[0] fields)
I've solved it... mainly a glaring error on my part. The getElementById worked fine ... except in my test script I'd used getElementById[xxx] and not getElementById(xxx)!! ie "[" rather than "(" Does help if you get the syntax right!
But I will take notice of those other methods, such as enclosing both array arguments in ["xxx"].
getElementById didn't work because the only one of those elements that has an id is the second input, with id="pt".
On any modern browser, you can use querySelector to get a list of the inputs using a CSS selector:
var nameInput = document.querySelector('input[name="products[0][prod_name]"]');
var typeInput = document.querySelector('input[name="products[0][prod_type]"]');
Then use their value property. So for instance, to set the name to "OTO":
document.querySelector('input[name="products[0][prod_name]"]').value = "OTO";
Use querySelectorAll if you need a list of relevant inputs, e.g.:
var nameInputs = document.querySelectorAll('input[name="products[0][prod_name]"]');
Then loop through them as needed (the list as a length, and you access elements via [n] where n is 0 to length - 1).
Re
* This is my understanfing of how a multdimentional array is set up...
All that HTML does is define input elements with a name property. That name property is sent to the server as-is, repeated as necessary if you have more than one field with that name. Anything turning them into an array for you is server-side, unrelated to JavaScript on the client. (The [0] is unusual, I'm used to seeing simply [], e.g name="products[][prod_name]".)
You can access it in this syntax:
document.zaxaa['products[0][prod_name]'].value
document.zaxaa.products[0].prod_type.value
The name is a single string, not making a nested structure to access the input. It would need to be
document.zaxaa["products[0][prod_type]"].value
// or better:
document.forms.zaxaa.elements["products[0][prod_type]"].value
The complicated name does only serve to parse the data into a (multidimensional) array on the server side, but all data will be sent "flattened".
document.getElementById('products[0][prod_type]').value
The id of your input is pt, so this should work as well:
document.getElementById("pt").value
document.getElementsByName('products[0][prod_type]').value
getElementsByName does return a collection of multiple elements - which does not have a .value property itself. Instead, access the first element in the collection (or iterate it completely):
document.getElementsByName('products[0][prod_type]')[0].value
I have a form with a hidden field:
<input type="hidden" name="newdesc[]" id="newdesc" />
I have an autolookup and a plus button so the plus button adds the field value of the autolookup to an array:
newdesc_a.push(document.getElementById('autocomplete1').value);
var x=document.getElementById("businesslist");
x.innerHTML=newdesc_a;
document.getElementById('newdesc').value = newdesc_a;
(newdesc_a is previously declared as an array)
It updates the div OK (businesslist) but does not assign the value to the newdesc.
I am sure it is simple but it is driving me mad!
Given the name="newdesc[]" attribute, I assume PHP on the server side. When you submit a key two or more times, only the last value is available in the script via $_REQUEST. In the case of a key ending with [] , instead, PHP builds an array and make it available to your script via $_REQUEST['key']. Note that this behavior is application-specific, there's nothing like an array at the HTTP level.
Now, you want to pass an array from the client side (Javascript) to the backend (PHP). You have two options:
Use a proprietary format, eg separate values by commas or colons, and split the string on the server side (or you can use an existing format like JSON, XML, ...)
Take advantage of the PHP syntax for passing arrays
It seems you want to adopt the 2nd way, so you will want to submit a form like the following
<input name="email[]" value="joel#example.com" />
<input name="email[]" value="mark#people.com" />
<input name="email[]" value="bill#hello.com" />
PHP will be able to access a plain array in $_REQUEST if you build your form like this. Now, you have the problem of building the form programmatically on demand. This is the best I can think of (jQuery):
var email = $("#autocomplete").value;
// if you really need to keep emails in an array, just
// emails.push(email);
$('<input type="hidden">').attr({
name: 'email[]',
value: email
}).appendTo('#myForm');
You want to assign a particular value of array to that element?
You can use like this.
document.getElementById('newdesc').value = newdesc_a.index;
where 'index' is the index of array. replace it.
I'm using django and have a page of search results that I want to be able to filter by category with ajax (jquery) requests. There's a filter bar on the side of the page and when someone selects certain categories and clicks submit, those corresponding results should show up on the page. My code looks something like this:
<input type="checkbox" name="category1" value="1" />
<input type="checkbox" name="category2" value="2" />
<input type="button" name="submit" onclick="{
var cats = new Array();
$(input[type=checkbox]:checked).each(function() {
cats.push($(this).val());
});
$.getJSON('page.html', {'cats':cats}, function(data) {...});
}" />
But in the django view when I try to read the cats array it returns a 500 error. I can, however, pass scalars and strings to the django view with no problem.
Any thoughts? Is there a more elegant jQuery way to do this without using a javascript array?
Rather than getting the field values manually, you can use jQuery's serializeArray() method, which turns a set of fields into an array which can be sent as JSON. Note that you'll probably want both checkboxes to have the same name, so that when it's serialised it becomes something that Django will interpret as a list.
As Steve says, to help further we'll need to know what the view is doing and what the error is.
Got it working - I downloaded the jquery-json plugin, encoded my array as a json obect to send to django, and then used simplesjon.loads() in the django view to convert that json object to a python list. However, the fact that it took so many steps and jQuery on its own doesn't even come with json encode functionality still makes me think there must be a more elegant way - if anyone has any insight, I'd love to hear it.
Thanks Daniel for pointing me in the right direction.
I guess that a workaround would be transforming your array into a string, eg in javascript:
a = new Array(0,1,2,3,4);
[0, 1, 2, 3, 4]
a.join(" ")
"0 1 2 3 4"
Then you can pass this to the python back end, split it and reconstruct the data structure you need.....