Storing an indeterminate number of decorated values

I haven’t thought of a clever name for this design pattern yet, but it solved a problem today, and I thought I’d share.

I was working on a new site’s admin section, and I needed to add phone numbers to an existing person model. This existing model didn’t have any support for phone numbers already, so I was faced with a choice.

On the one hand, I could add four or five phone fields (and a matching number of phone label fields) to the model, and have a big ugly bunch of usually empty fields whenever you wanted to edit a record.

On the other hand, I could make a new ‘phone’ model, use a foreign key to associate each one with its parent person, and be able to have a cool “add a number” button in the interface, and never any extra fields to tab through when editing.

This second approach was a lot more elegant, but it’s a lot of extra work just to throw around a few key-value pairs. So what I did was to use PHP’s serialize() and unserialize() functions to store and restore an array in a plain text field.

The form is laid out just like you would a set of checkboxes: each like field in the set is named the same, but has a trailing pair of square brackets on its name so PHP will treat it as an array.

<select class="phoneopt" name="phonetype[]" size="1">
	<option label="Home" value="Home" selected="selected">Home</option>
	<option label="Work" value="Work">Work</option>
	<option label="Mobile" value="Mobile">Mobile</option>
</select>
<input type="text" name="phone[]" class="phone" value="(215) 782-8710" /> 
<img src="Resources/icns/delete.png" width="16" height="16" alt="" class="icon remove_phone" />
<select class="phoneopt" name="phonetype[]" size="1">
	<option label="Home" value="Home">Home</option>
	<option label="Work" value="Work" selected="selected">Work</option>
	<option label="Mobile" value="Mobile">Mobile</option>
</select>
<input type="text" name="phone[]" class="phone" value="(215) 782-3051" /> 
<img src="Resources/icns/delete.png" width="16" height="16" alt="" class="icon remove_phone" />
<select class="phoneopt" name="phonetype[]" size="1">
	<option label="Home" value="Home">Home</option>
	<option label="Work" value="Work">Work</option>
	<option label="Mobile" value="Mobile" selected="selected">Mobile</option>
</select>
<input type="text" name="phone[]" class="phone" value="(267) 879-8840" /> 
<img src="Resources/icns/delete.png" width="16" height="16" alt="" class="icon remove_phone" />
<span id="add_phone" class="caption"><img src="Resources/icns/add.png" class="icon" width="16" height="16" alt="+" />Add Phone</span>

In the controller, I intercept the POST as usual, but then iterate over the $_POST[‘phone’] and ‘phonetype’ arrays to generate my array for storage. It arrives looking like this:

$_POST['phone'] = array(
    0 => '(215) 782-8710',
    1 => '(215) 782-3051',
    ...
)
$_POST['phonetype'] = array(
    0 => 'Home',
    1 => 'Work',
    ...
)

And I store it as an array of arrays:

$phones = array(
    0 => array('Home','(215) 782-8710'),
    1 => array('Work','(215) 782-3051'),
    ...
)

It’s important to note that this works because the $_POST[‘phone’] and $_POST[‘phonetype’] arrays arrive at the server in source order, and the page is laid out with these fields all in the same div in alternating order, so I can count on the index from one array matching the index of the other. If the page was laid out in a mish-mosh of DIVs (as it might be if you sketched your form fields directly on the page in Freeway) then this couldn’t be guaranteed, and you’d have to do something far more elaborate to key them together.

Anyway, storing this in the database is as simple as this:

$person->phone_number = serialize($phones);

‘phone_number’ is just a big honking text field, so you can store quite a lot of phone numbers in it if you like. And then getting them back out looks like this:

$phones = unserialize($person->phone_number);
foreach($phones_array as $phone){
    $person->selected_phoneopt = $phone[0];
    $person->phone = $phone[1];
    $person->phones .= render_partial('phone',$person);
}

‘render_partial’ above is a function that populates a template with an object’s worth of variables, so each time this loop goes around, another set of form fields is generated.

The add and delete buttons are handled with JavaScript, which makes the phone options grow and shrink without a page reload. But this post is already three times too long, so I’ll save that part for another day.

Walter


dynamo mailing list
email@hidden
Update your subscriptions at:
http://freewaytalk.net/person/options

Grrr. Typo:

$phones = unserialize($person->phone_number);

should be

$phones_array = unserialize($person->phone_number);

dynamo mailing list
email@hidden
Update your subscriptions at:
http://freewaytalk.net/person/options