3

I am trying to figure out how I can handle possible duplicate array keys.

I have this form with a select dropdown which can select multiple options (more than 1). And I am using jQuery.serialize() to serialize the form on submit.

The serialized string for a multi-select element would look like so:

select=1&select=2&select=3 // assuming I selected first 3 options.

Now in the PHP side, I have the following code to handle the "saving" part into a database.

$form_data = $_POST['form_items'];

$form_data = str_replace('&','####',$form_data);
$form_data = urldecode($form_data);
$arr = array();
foreach (explode('####', $form_data) as $part) {
    list($key, $value) = explode('=', $part, 2);
    $arr[$key] = $value;
}

This all works for the rest of the form elements, but when it comes to the select element, it only picks the last selected key/value pair. So my array now looks like this:

Array ( [select_element] => 3)

What I need, is for it to look like:

Array ( [select_element] => '1,2,3')

So I guess what I am asking is based on my code, how can I check if a key already exists and if it does, append to the $value.

1

6 Answers 6

5

If you can modify the client-side code, I would rather change the name of the select to select[], that way it will be parsed as an array in your server script.

Sign up to request clarification or add additional context in comments.

3 Comments

I thought of doing it this way but it doesn't work well when you're serializing the form elements with jQuery.
I think that for jquery > 1.4 serialize will preserve the brackets. If you want to put it dynamically on an input or some other variable for you to send it, you can use decodeURIComponent on the result, and then on the server you would use parse_str on that variable.
Using the current jQuery 2+, using name="select[]" works perfectly with parse_str. Thanks for the tip ;)
2

PHP has its own way of encoding arrays into application/x-www-form-urlencoded strings.

PHP's deserialization of x-www-form-urlencoded is implemented by parse_str(). You can think of PHP as running these lines at the beginning of every request to populate $_GET and $_POST:

parse_str($_SERVER['QUERY_STRING'], $_GET);
parse_str(file_get_contents('php://input'), $_POST);

parse_str() creates arrays from the query string using a square-bracket syntax similar to its array syntax:

select[]=1&select[]=2&select[]=3&select[key]=value

This will be deserialized to:

array('1', '2', '3', 'key'=>'value');

Absent those square brackets, PHP will use the last value for a given key. See this question in the PHP and HTML FAQ.

If you can't control the POSTed interface (e.g., you're not able to add square-brackets to the form), you can parse the POST input yourself from php://input and either rewrite or ignore the $_POST variable.

(Note, however, that there is no workaround for multipart/form-data input, because php://input is not available in those cases. The select=1 and select=2 will be completely and irretrievably lost.)

2 Comments

Thank you for the explanation however could you possibly show me how that could incorporate into my code? I am not sure if you're stating if I should not use list function and use the parse_str?
Honestly your code doesn't make any sense. If you have control over your form you should change it to follow PHP convention and all your code should be unnecessary. If you don't have control of your form you should show us the raw POST body so we can suggest something more specific.
2

Ok, I was able to resolve this issue by using the following code:

    if (array_key_exists($key, $arr)) {
        $arr[$key] = $arr[$key].','.$value;
    } else { 
        $arr[$key] = $value;
    }

So now the loop looks like this:

foreach (explode('####', $form_data) as $part) {
    list($key, $value) = explode('=', $part, 2);

    if (array_key_exists($key, $arr)) {
        $arr[$key] = $arr[$key].','.$value;
    } else { 
        $arr[$key] = $value;
    }
}

This will essentially string up the value together separated by a comma which can then be exploded out into an array later.

Comments

1

You can't have multiple keys with the same name... They're unique keys. What it's doing is setting select = 1, then select = 2, then select = 3 and you end up with your final value of 3.

If you want to allow multiple choices in your select menu, you should change the name of the element so that it submits that data as an array, rather than multiple strings:

<select name="select[]" multiple="multiple">

which should result in something like this instead:

select[]=1&select[]=2&select[]=3

and when PHP requests that data, it will already be an array:

$data = $_POST['select'];
echo print_r($data); // Array(1, 2, 3)

Comments

0

You should change your client-side code to send the augmented value: array.join(","); and use that in your PHP side. Because when your data reaches your script, the other values have already been lost.

6 Comments

"when your data reaches your script, the other values have already been lost". Why would that be the case?=
@Niklas: I assume Milad means they will not be in the $_GET array, they can however be retrieved from $_SERVER['QUERY_STRING'].
Because by decoding the received data into PHP arrays, you are handing the conversion over to the PHP engine, which will replace items with the same key within arrays. So, only the last occurrence will survive the conversion.
@Wrikken: They will however actually be in $_POST['form_items'] array, because OP uses a seperate field for the whole string.
@Milad: This is simply wrong. $_POST['form_items'] will not be automatically parsed by PHP into a nested array. Also, you can access $_SERVER['QUERY_STRING'] directly.
|
-2

This should give you the solution you require

$form_data = str_replace('&','####',$form_data);
$form_data = urldecode($form_data);
$arr = array();

foreach (explode('####', $form_data) as $part) {
    list($key, $value) = explode('=', $part, 2);

    if (isset($arr[ $key ])) {
        $arr[$key] = ','.$value;
    } else {
        $arr[$key] = $value;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.