Recall that form handlers are connected to your product item forms and they are responsible for transferring product options and features entered by your customers to and from your item objects.

One thing to be aware of is that the cart object makes temporary copies of your item objects when users modify items in their cart. This allows the cart to abandon any item changes if the user decides to cancel their request to modify the cart item.

Default Property Synchronization

If your cart item form handlers inherit cart_form_handler_base they will automatically have logic to perform basic item object updates and retrievals by matching like named form fields and cart item properties. When users post a page used to add a product to their cart, the default behavior is shown in the following pseudo code:

if ($this->button_pressed === 'transaction_previous') { // Back button was pressed
	if (we are on the first page of the item) 
		cancel any updates and return to the cart page
	else
		return to the previous page without performing validation of the current page
	}
}
// Iterate through all fields on the current form
foreach ($this->field_list as $field_name => $field_obj) {
	if (set method exists for this field) {
		invoke the set method of the field in the item object
		if (set method returns false) {
			call the transfer_your_messages_to_me method of the item object and 
			copy the error information to the form field
		}
	}
	else {
		add unmatched field to $unassigned keyed array
	}
}
if (isset($unassigned)) {
	$this->copy_unmatched_values_to_item($item_object,$unassigned);
}

Here are the key aspects of this pseudo code:

  1. Copying to the object and validation only occurs when the user clicks the transaction_next or transaction_last buttons, not the transaction_previous button.
  2. If the user clicks on the transaction previous button while on page one of the product page (often there is only one) this cancels the update and returns the user to  the cart.
  3. The code gathers all mismatched fields (fields that are on the form but have no set method in the item object) in the $unassigned array.
  4. If there are mismatched fields, these are passed  to the copy_unmatched_values_to_item method of the form handler.

copy_unmatched_values_to_item Method

You can avoid having to use the copy_unmatched_values_to_item method by always ensuring that your item object's have set methods for every field on your item form. Nevertheless, this may not be an appropriate strategy. As we have discussed, your item objects are intended to be generic and, as such, they should not really be governed by how your forms are implemented. You should strive to keep your item object interfaces generic, even if this means that your form fields won't necessarily map to your item fields on a 1-1 basis.

Consider the following form fields used to enter the size of a tarp to be ordered:

Sample form field that are unassigned

In order to populate these fields one at a time, the tarp item object would need 8 set methods, one for each form property. Since these fields represent a tightly related group, it may make more sense for the tarp object to have a single method, such as set_tarp_dimensions which sets all of these values in one function. Let's look at how this could be done in a couple of different ways.

Option 1 - Using Individual Parameters

Suppose the interface to the set_tarp_dimensions method is a series of individual fields as we show here:

function set_dimensions($width_major,$width_units_major,$width_minor,$width_units_minor,
		$length_major,$length_units_major,$length_minor,$length_units_minor)

Assuming the tarp object does not have set methods for each if these parameter fields to be set individually the page handler would not be able to match these fields to the object and, in such a case, these values would end up in the $unassigned array as shown here:

// contents of $unassigned array after attempting to match form fields with the item object
array ('width_major' => '8', 'width_units_major' => 'feet', 'width_minor' => '3', 'width_units_minor' => 'inches',
       'length_major' => '7', 'length_units_major' => 'feet', 'length_minor' => '11.5', 'length_units_minor' => 'inches')

The job of the form_handler's copy_unmatched_values_to_item method is to find a home for these fields within the item object.

Here is an example of how these unassigned values would be transferred to the tarp object assuming the set_dimensions method shown above.

function copy_unmatched_values_to_item($item,$unassigned) {
	// If we are on form 1, invoke the tarp object's set_dimension method with the size values  
	if (isset($unassigned['length_major'])) {
		$success = $item->set_dimensions($unassigned['width_major'],$unassigned['width_units_major'],
                                         $unassigned['width_minor'],$unassigned['width_units_minor'],
                                         $unassigned['length_major'],$unassigned['length_units_major'],
                                         $unassigned['length_minor'],$unassigned['length_units_minor']);
		if (!$success) {
			$item->transfer_your_messages_to_me($this->form);
		}
	}
	return $success;
}

Option 2 - Using Array Parameters

Another way that the tarp object could implement the set_dimensions method would be to accept a keyed array containing the 8 required parameters, rather than a list of 8 individual arguments. There are pros and cons to each approach however this is a reasonably generic interface and does have some advantages over requiring individual fields to be passed.

function set_dimensions($dimensions_array)

The nice thing about this approach is that if you name your form fields to match the names of the keys expected by the tarp objects set_dimensions method you can simply pass the $unassigned array to this method as we show here:

function copy_unmatched_values_to_item($item,$unassigned) {
	// If we are on form 1, invoke the tarp object's set_dimension method with the size values  
	if (isset($unassigned['length_major'])) {
		$success = $item->set_dimensions($unassigned);
		if (!$success) {
			$item->transfer_your_messages_to_me($this->form);
		}
	}
	return $success;
}

Synchronizing Values in the Opposite Direction

We have seen how the form_handler's copy_unmatched_values_to_item method is used to copy extraneous form fields to a cart item. In most cases, if you need to code this method, you will also need the complementary method named copy_unmatched_values_to_form. As you might expect, this method is used to copy values from the item object to fields on the form. The base class cart_form_handler_base will automatically populate form fields for which the object has a like named get method. When finished any remaining fields will end up as keys within the $unassigned array. In this case, there will not be any values in the array except for the value true as we see here.

array ( 'width_major' => true, 'width_units_major' => true, 'width_minor' => true, 'width_units_minor' => true, 
		'length_major' => true, 'length_units_major' => true, 'length_minor' => true, 'length_units_minor' => true)

Let's see how the form handler can obtain the values from the item object and copy them to the correct form fields. We once again assume that the field names used on the form match up with the keys returned by the get_dimensions method of the item object.

function copy_unmatched_values_to_form($item,$unassigned) {
	if (isset($unassigned['length_major'])) {
		$tarp_size = $item->get_dimensions();
		foreach ($tarp_size as $field_name => $value) {
			$this->field_list[$field_name]->assign($value);
		}
	}
}

Obviously the code for your copy methods may vary considerably from what is shown here since this depends heavily on the methods available to populate and retrieve values stored in your item objects as well as how these values are represented on your order form(s).