9

i'm having some problem with posting data as an array of array. This is how i'd like my data to be POSTED:

array(
['someName'] =>
array([0] =>
      array(['description'] =>890
            ['valore'] =>444)
      [1] =>
      array(['description'] =>98090
            ['value'] =>77)
) 

I know i can achieve this if my html is like this:

<input type='text' name="someName[0][value]">
<input type='text' name="someName[0][description]">
<input type='text' name="someName[1][value]">
<input type='text' name="someName[1][description]">

My problem is that the input fields are on rows of a table and the user can add/remove as many rows as he want, so i can't have fixed index (or i have to modify the name of the input fields each time a row is added since every time i add a row i clone the upper row in the table)

So what i am asking is one of these two things:

1) is there a way to post data the way i want without specifing an index

2)if not, how can i modify dynamically the new input field so that they have an updated name with the new index?

EDIT - i had alredy tried using name="someName[value][]" and name="someName[description][]" but the output is not the desired one:

array(['terreniOneri'] =>
       array(['descrizione'] =>array([0] =>890
                                      [1] => 98090)
               ['valore'] =>array([0] =>444
                                  [1] =>677)
      ) 

i know i can iterate on this array in php i was just wondering if i could avoid it.

Nicola Peluchetti
  • 76,206
  • 31
  • 145
  • 192
  • 1
    Commenting since no time for a comprehensive answer: Rather than copying the top row you could generate a template row in a variable (eg `var template='...` and then replace all occurences of `%%ROWNUM%%` with the next index you want (you'll have to keep track of your count). Then use jquery to build that as HTML and add it in at the end of your table. – Chris May 20 '11 at 13:25
  • @chris i need to clone the row because "someName" may vary depending on the row and so i can't hardcode it. What i should do (but i don't know how to do it) is to change the index in the name (just the number) in the new row (so that if i have someName[1][description] in the orginal row, the copy has someName[2][description]) No problem when subtracting as you pointed out! – Nicola Peluchetti May 20 '11 at 13:41
  • Well you can always make "someName" a template variable too or store different templates for different purposes, etc. Once you're playing with javascript you should be able to do whatever you want really. – Chris May 20 '11 at 14:23

2 Answers2

7

Do it the way you put in the question. If the user removes some row, your form elements would be:

<form action="..." method="post" onsubmit="return reindexer(this);">
    <input type='text' name="someName[0][value]">
    <input type='text' name="someName[0][description]">
    <input type='text' name="someName[2][value]">
    <input type='text' name="someName[2][description]">
</form>

But there's no problem to traverse an array with non-contiguous numeric indexes in php: use a foreach loop.

<?php
if (count($_POST['somename']) > 0)
{
    foreach ($_POST['somename'] as $row)
    {
        echo "Value: ".$row['value']."<br />\n";
        echo "Description: ".$row['description']."<br />\n";
    }
}

If you need to know the number of each row as a continous index (in the example provided, row 0 would still be 0, but row 2 should be 1 (as the user deleted one row), you can use a variable acting as a counter:

<?php
if (count($_POST['somename']) > 0)
{
    $i = 0;
    foreach ($_POST['somename'] as $row)
    {
        echo "Index $i<br />\n";
        echo "Value: ".$row['value']."<br />\n";
        echo "Description: ".$row['description']."<br />\n";
        $i++;
    }
}

I think this approach has more sense that the other solutions, as this way you would have an array of items, being each item a value and a description, instead of having two separate arrays of values and descriptions and having to get the values for your item from those two arrays instead of one.

edit: I've modified the first piece of code to include the <form> element. This would be the accompanying js function:

<script type="text/javascript">
function reindexer(frm)
{
    var counter = 0;
    var inputsPerRow = 2;
    for (var idx = 0; idx < frm.elements.length; idx++)
    {
        elm.name = elm.name.replace('%%INDEX%%', counter);
        if (idx % inputsPerRow == 1)
        {
            // only increment the counter (or row number) after you've processed all the
            // inputs from each row
            counter++;
        }
    }
}
</script>
Carlos Campderrós
  • 22,354
  • 11
  • 51
  • 57
  • the problem is when the user add a row (that's because i clone the upper row): in this case the new row would have the same name as the old one and would overwrite it when posted – Nicola Peluchetti May 20 '11 at 13:36
  • When you add a row, it gets appended at the end of the table, or between two existing rows? – Carlos Campderrós May 20 '11 at 13:45
  • between existing rows. And i have to clone it because 'somename' changes between rows of the table – Nicola Peluchetti May 20 '11 at 13:48
  • Then I would go with something like Chris' comment in the question. At the beginning, before the user clones any row, all your rows have the string "%%INDEX%%" as index (`name="somename[%%INDEX%%][value]`). You capture the `onsubmit` event of the form, and then for each `input` you have in the form, replace %%INDEX%% with a counter. Downside from this approach is that if some user has js disabled (we exist) your code would only process the last row (as each row would overwrite the previous (all would have the same index). – Carlos Campderrós May 20 '11 at 13:59
  • @Carlos: surely the downside would just be an inability to add new rows? If the js doesn't work for renaming the indexes then it won't work for adding new rows. – Chris May 20 '11 at 14:17
  • @Chris right, but the inability to add new rows is a minor downside compared with the pain of filling all the inputs and see then that just the last row has been saved. – Carlos Campderrós May 20 '11 at 14:36
  • @Carlos i agree with chris, the inability to add rows is minimal comperad to just saving the last one!Thx for your answer – Nicola Peluchetti May 20 '11 at 14:46
  • @Carlos: Sorry, I had not properly read your comment that I was replying to. My thought was to use that template row and change the values as you add each row, not use a generic repeated row and change it on submit. So when you create the page server side all the rows have the right indexes and you also create some JS vars to store the next row index, etc. Then when you generate each row you do the replace and increment the next row number. Generating HTML as you describe is going to doom you to failure and is even less graceful fallback to non-js than doing nothign would be. :) – Chris May 20 '11 at 14:52
  • Also the reindexed can be written more nicely with jquery by processing each table row one at a time and skipping the inputsperrow code which would make it a little more reusable. :) We're probably into a different question if we're optimising that though since yours works (I assume). :) – Chris May 20 '11 at 14:57
4

Try like this:

<input type='text' name="someNameValue[]">
<input type='text' name="someNameDescription[]">

If the fields are paired, they can be attached by the indexes. So if you have the 10th row, someNameValue[9] and someNameDescription[9] will be a pair. You can merge them.

EDIT: You don't have to write the indexes manually, they will be automatically generated.

<input type='text' name="someName[]">
<input type='text' name="someName[]">
<input type='text' name="someName[]">

and

<input type='text' name="someName[0]">
<input type='text' name="someName[1]">
<input type='text' name="someName[2]">

will give the same result in your post array.

Damien
  • 674
  • 5
  • 12
  • I would do it the same way except using `someName['value'][]` and `someName['description'][]`. You can then turn that into the array you want via a few rows of php. – Martin Hennings May 20 '11 at 13:16
  • Does the HTML spec actually guarantee that the elements POSTed will be posted in a well defined order (such as position in HTML)? Although this would probably work it feels a bit wrong relying on that (though I might be wrong and it is well defined what order they are passed in). – Chris May 20 '11 at 13:18
  • Hmm... I think yes. I used this many times before and never had any problems with it. – Damien May 20 '11 at 13:20
  • if i do `someNameValue[]` and `someNameDexcription[]` obviously i have two different arrays and that is not what i want. If i do `someName['value'][]` and `someName['description'][]` it's something i tried but it doesn't give what i desire (i'll post above the output) – Nicola Peluchetti May 20 '11 at 13:31
  • @Damien: Well, yes, all current browsers do it like this (for obvious technical reasons); but the order is not *guaranteed* anywhere. – Piskvor left the building May 20 '11 at 13:42
  • 1
    Then the best option would to generate the index with JS based on the row number. – Damien May 20 '11 at 13:44