Twitter Feed

Xataface Maillist

Sign up to receive the Xataface news letter with weekly updates and development tips.

 

How to define custom serialization for fields

Xataface allows you to define your own serializers and parsers for fields so that you can store arbitrarily formatted content in your database and manage how it is edited and displayed in your application.

(Applies to Xataface 0.5.x and newer)

At some point you may feel the need to massage the data as it is passed between forms, the database, and the Xataface API.  Some example scenarios include:

  • Stripping HTML out of a field to be stored in the database
  • Storing complex data types as XML in the database - but making the values accessible as data structures in the API
  • Compressing a blob field using GZIP compression to be stored in the database, but automatically extracting it when it is retrieved from the database.

Xataface allows you to define methods in the delegate class to customize the serialization/unserialization/parsing behavior for specific fields.  These methods are as follows:

  • <fieldname>__serialize()  : Serializes the value for the field <fieldname> so that it can be stored in the database.
  • <fieldname>__parse() : Parses data to be stored in the <fieldname> field of the Dataface_Record object.  This method is called every time any value is  inserted into the field, so this method should be able to recognize if the data is already parsed, so that it doesn't "double-parse" the data. i.e., the following condition should always be true of your <fieldname>__parse() method: <fieldname>__parse(<fieldname>__parse($val)) == <fieldname>__parse($val)
  • <fieldname>__toString() : Converts the value in the <fieldname> field of the Dataface_Record object so that it can be displayed as a string.  This method should be the inverse of the <fieldname>__parse() method. i.e., <fieldname>__toString( <fieldname>__parse( $val ) ) == $val .  (unless $val was already parsed).
  • <fieldname>__pushValue() : Takes a value as submitted by an HTML form and outputs a value that is prepared to be saved in the Dataface_Record object.  Note that the output of this method will still be passed to <fieldname>__parse() for further parsing.  This is handy if the widget to edit this field is complex and needs some custom logic to format the data to be inserted into the Dataface_Record object.
  • <fieldname>__pullValue() : Takes a value as retrieved from a Dataface_Record object and outputs a value that can be placed in an HTML form widget.  This is the inverse of <fieldname>__pushValue().
Figure 1 shows the flow of events involved in storing/saving data in the database with respect to these methods:
Serialization-flowchart.png
Figure 1: Flow of events for storing/retrieving data for a field named "Phone"

The diagram above assumes that we have a field named "Phone" in our table.  For a normal phone field, it wouldn't be necessary to implement any custom serialization or parsing behavior because it would just be edited with a plain text field and saved to the database as is.  However, suppose we want to store more than one phone number in the field and let the user enter a separate phone number on each line of the text area.  Then we would probably want the Phone field to be stored internally (in the Dataface_Record object) as an array of phone numbers, and we may want to store them in the database as XML.  In this case we would implement some functions as follows in the delegate class:

<?
tables_Profiles {
/**
* Parses phone numbers when they are stored in the Dataface_Record object.
* Accepts XML as input or an array of phone numbers as input.
* @param $value The phone number(s) to be parsed.
* @returns Array of phone numbers to be stored in Dataface_Record object.
*/
function Phone__parse($value){
if ( is_array($value) ){
// The value is already an array - the proper format.. just leave it alone
return $value;
} else {
// The value is not an array, so it should be XML.
// (! For brevity, I am making up some fake functions: isXML() and xmlToArray()
if ( isXML($value) ){
// The value appears to be valid XML, we can parse it and return
// the parsed value to be stored in the object.
return xmlToArray($value);
} else {
// The value is not valid XML.. we have decided to only allow
// XML format so we will return an error.
return PEAR::raiseError('Expecting XML input for field Phone but received '.$value);
}
}
}

/**
* Serializes phone numbers as XML to be stored in the database. The input for this is
* always an array of phone numbers (the format defined in the Phone__parse() method).
* Note: It is important that the output of this function will be accepted as input
* by the Phone__parse() method and vice versa.
*
* @param $value Array of phone numbers to be serialized.
* @returns XML encoded phone numbers to be stored in the database.
*/
function Phone__serialize($value){
// (! Note: I am making up a fake function called arrayToXML() to spare the details.
return arrayToXML($value);
}

/**
* Converts phone numbers entered in a text area field (one per line) so that they can
* be saved in the Dataface_Record object. It is important that the output of this method
* will be accepted as input by the Phone__parse() method.
*
* @param $record Dataface_Record object where phone numbers will be saved.
* @param $element HTML_QuickForm_element object representing the widget
* that was used in the HTML form to edit the Phone number.
* @returns Array of phone numbers to be stored in the Dataface_Record object.
*/
function Phone__pushValue(&$record, &$element){
$phoneNumbers = $element->getValue();
// getValue from the widget .. phone numbers in string separated by line breaks.
return explode("\n", $phoneNumbers);
// converts to array.
}

/**
* Converts array of phone numbers obtained from a Dataface_Record object into a form
* that can be edited in an HTML text area.
*
* @param $record Dataface_Record object storing the phone numbers.
* @param $element HTML_QuickForm_element object representing the HTML widget
* that is used to edit the Phone field in the HTML form.
* @returns String of phone numbers separated by line breaks to be edited in HTML text area.
*/
function Phone__pullValue(&$record, &$element){
return implode("\n", $record->getValue('Phone'));
}

/**
* Converts the array of phone numbers to a string that will be used to display
* the phone numbers to the screen. (not necessarily for editing).
* @param $value The array of phone numbers as obtained from the Dataface_Record object.
* @returns String form of the phone numbers.
*/
function Phone__toString($value){
return implode("\n", $value);
}

}

?>

The above example is a little bit contrived as an attempt to show the full cycle of methods that can be defined.  However, you will seldom need to define all of these methods.  Even in the above example, the Phone__pullValue() method was unnecessary because Xataface will automatically use the Phone__toString() method to obtain a string version to place in the HTML form for editing.  In fact, most of the time you will find that it is enough to define only Phone__parse() and Phone__serialize(), and in some cases you can do most of your dirty work in only the serialize() method.

Example 2: Stripping HTML from Blurb field

Suppose we only want to strip out the HTML from a Blurb field before it is stored in the database.  Then we need only define the Blurb__serialize() method to strip the tags as follows:
<?
class tables__Profiles {
function Blurb__serialize($value){
return strip_tags($value);
}
}
?>

See Also


Powered by Xataface
(c) 2005-2017 All rights reserved