Coding with Jesse

Use Arrays in HTML Form Variables

November 3rd, 2008

When you're dealing with processing forms, a lot of the time you have a one-to-one mapping of form fields and database columns, with perhaps an extra submit button or some fields that need special processing (eg. passwords).

Although many frameworks like CodeIgniter make this easier, you can still easily come up with code like this:

$this->db->insert('accounts', array(
    'first_name' => $this->input->post('first_name'),
    'last_name' => $this->input->post('last_name'),
    'email' => $this->input->post('email'),
    'address1' => $this->input->post('address1'),
    'address2' => $this->input->post('address2'),
    'city' => $this->input->post('city'),
    'state' => $this->input->post('state'),
    'zip' => $this->input->post('zip'),
    'phone' => $this->input->post('phone'),
    'fax' => $this->input->post('fax')
));

See all that repetition? Whenever you see a group of lines that look almost the same, you know there is probably an opportunity to clean things up. Well luckily, there's a really neat one.

You can create arrays in form fields using square brackets. That means you can name fields like account[first_name] and account[last_name] and in most server-side languages, you will end up with an 'account' array.

In HTML, PHP and CodeIgniter, that might look something like this:

<input type="text" name="account[first_name]"/>
<input type="text" name="account[last_name]"/>
<!-- etc... -->
<input type="text" name="account[fax]"/>
<input type="submit" name="submit"/> <!-- note the lack of 'account' -->

// meanwhile, on the server...
$account = $this->input->post('account');

// VERY IMPORTANT: unset any fields that shouldn't be edited
unset($account['id']);

$this->db->insert('accounts', $account);

See? Much cleaner. Yes, you do open a slight security hole potential, so be very careful when doing this on tables that have security implications, ie. user data. If you have an 'admin' column on your 'users' table, someone could maliciously add <input type="hidden" name="users[admin]" value="1"/> to your form using Firebug and grant themselves administration access. You can solve this by adding something like unset($user['admin']); to the incoming data. If you wanted to be really safe, you could have an array of the keys you want to allow and filter against that using something like PHP's array_intersect_key with array_flip:

$user = $this->input->post('user');

// only allow keys in $user to match the values in $columns
$columns = array('first_name', 'last_name', /* etc.. */ );
$user = array_intersect_key($user, array_flip($columns)));

You can even do this with checkboxes and multiple select boxes, and alternatively leave out the key in the array to create a regular array (ie. not associative) of values:

<label><input type="checkbox" name="choice[]" value="1"/> 1</label>
<label><input type="checkbox" name="choice[]" value="2"/> 2</label>
<!-- etc... -->

// meanwhile, on the server...
$choice = $this->input->post('choice');

print_r($choice); // Array ( [0] => 1 [1] => 2 )

This "trick" can really clean your code up. I used it recently to drastically simplify a form that had a shipping address and billing address. If the user checked a box saying "My shipping and billing are the same", I was able to simply do something like this:

$shipping = $_POST['shipping'];
$shipping_billing_same = $_POST['shipping_billing_same'];

if ($shipping_billing_same) {
    $billing = $shipping;
} else {
    $billing = $_POST['billing'];
}

Much nicer than that 24 form fields that were hard-coded previously.

I've given examples here in PHP and CodeIgniter. Does anyone else want to give examples in other server-side languages and frameworks?