FormFields[ $Field->GetName( ) ] = $Field; } public function AddFields( $Fields ) { foreach( $Fields as $FieldName => $Field ) { $this->FormFields[ $FieldName ] = $Fields[ $FieldName ]; } } public function SetValues( $FormValues ) { foreach( $this->FormFields as $FieldName => $FieldValue ) { if( isset( $FormValues[ $FieldName ] ) ) { $this->FormFields[ $FieldName ]->SetValue( $FormValues[ $FieldName ] ); } } } public function GetValues( ) { $Values = array( ); foreach( $this->FormFields as $FieldKey => $FieldValue ) { $Values[ $FieldKey ] = $this->FormFields[ $FieldKey ]->GetValue( ); } return $Values; } public function GetErrors( &$ErrorStrings ) { $Errors = array( ); foreach( $this->FormFields as $FieldKey => $FieldValue ) { $Errors[ $FieldKey ] = $this->FormFields[ $FieldKey ]->GetErrors( $ErrorStrings ); } return $Errors; } private $FormFields; } // Plain 'ole form field // Can be used for basically any kind of form field // text, radio buttons, checkboxes, dropdowns, whatever class FormField { function __construct( $Name, $Validators = array( ) ) { $this->Name = $Name; if( is_array( $Validators ) ) { $this->Validators = $Validators; } else { $this->Validators = array( $Validators ); } } public function GetName( ) { return $this->Name; } public function SetValue( $Value ) { $this->Value = $Value; } public function GetValue( ) { return $this->Value; } public function GetErrors( &$ErrorStrings ) { $error = false; foreach( $this->Validators as $Validator ) { if( !$Validator->ValueIsValid( $this->Value ) ) { $ErrorStrings[ $this->Name ] = $Validator->GetError( $this->Value ); $error = true; } } return $error; } private $Name; private $Value; } // Form field map is a special kind of form field; // it contains a number of other form fields, each // associated by a name. In my example, CD is a form // field that maps to a band name, and an album name. // it becomes useful only when you have nested form // fields and in conjunction with FormFieldArray class FormFieldMap extends FormField { function __construct( $Name, $InnerFields ) { parent::__construct( $Name ); foreach( $InnerFields as $InnerField ) { $this->InnerFields[ $InnerField->GetName( ) ] = $InnerField; } } function __clone( ) { foreach( $this->InnerFields as $InnerFieldName => $InnerField ) { $this->InnerFields[ $InnerFieldName ] = clone $this->InnerFields[ $InnerFieldName ]; } } public function SetValue( $Values ) { foreach( $this->InnerFields as $InnerFieldName => $InnerField ) { if( isset( $Values[ $InnerFieldName ] ) ) { $this->InnerFields[ $InnerFieldName ]->SetValue( $Values[ $InnerFieldName ] ); } } } public function GetValue( ) { $Values = array( ); foreach( $this->InnerFields as $InnerFieldName => $InnerField ) { $Values[ $InnerFieldName ] = $this->InnerFields[ $InnerFieldName ]->GetValue( ); } return $Values; } public function GetErrors( &$ErrorStrings ) { $errors = array( ); foreach( $this->InnerFields as $InnerFieldName => $InnerField ) { $errors[ $InnerFieldName ] = $this->InnerFields[ $InnerFieldName ]->GetErrors( $ErrorStrings ); } return $errors; } private $InnerFields; } // FormFieldArray is a form field that has one form field inside of it, // but can have zero to many of that form field. In my example, 'CDs' has // zero to many 'CD's inside it. Useful if a form can have a variable number // of fields class FormFieldArray extends FormField { function __construct( $Name, $InnerField ) { parent::__construct( $Name ); $this->InnerField = $InnerField; $this->InnerFields = array( ); } function __clone( ) { $this->InnerField = clone $this->InnerField; } public function ValueIsValid( $Values ) { if( is_array( $Values ) ) { $isvalid = true; foreach( $Values as $Value ) { $isvalid = $isvalid && $this->InnerField->ValueIsValid( $Value ); } return $isvalid; } else { return false; } } public function SetValue( $Values ) { $this->InnerFields = array( ); if( is_array( $Values ) ) { foreach( $Values as $Value ) { $newval = clone $this->InnerField; $newval->SetValue( $Value ); $this->InnerFields[] = $newval; } } } public function GetValue( ) { $Values = array( ); foreach( $this->InnerFields as $InnerField ) { $Values[] = $InnerField->GetValue( ); } return $Values; } public function GetErrors( &$ErrorString ) { $errors = array( ); foreach( $this->InnerFields as $InnerField ) { $errors[] = $InnerField->GetErrors( $ErrorString ); } return $errors; } private $InnerField; // template field private $InnerFields; // holds actual values } // Value Checkers check form fields for their validity interface ValueChecker { public function ValueIsValid( $Value ); public function GetError( ); } // I was going to make more than just one ValueChecker, // but it turns out that you can pretty much check anything // with regular expressions class RegexValueChecker implements ValueChecker { // MatchCount can be an array of values where at least one must be matched, // or it can be a scalar that must be matched function __construct( $Regex, $MatchCount, $ErrorString ) { $this->Regex = $Regex; $this->MatchCount = $MatchCount; $this->ErrorString = $ErrorString; } public function ValueIsValid( $Value ) { $Matches = preg_match_all( $this->Regex, $Value, $Out ); if( is_array( $this->MatchCount ) ) { $isvalid = false; foreach( $this->MatchCount as $MatchVal ) { $isvalid = $isvalid || ( $Matches == $MatchVal ); } return $isvalid; } else { return $Matches == $this->MatchCount; } } public function GetError( ) { return $this->ErrorString; } private $Regex; private $MatchCount; private $ErrorString; } // create validators $phonenumChecker = new RegexValueChecker( "/\d/", array( 7, 10, 11 ), "Phone numbers must have 7, 10, or 11 digits" ); $alphanumChecker = new RegexValueChecker( "/[^a-zA-Z0-9_\s]/", 0, "Only alphanumeric characters allowed" ); // create fields $namefield = new FormField( "Name", $alphanumChecker ); $phonefield = new FormField( "Phone", $phonenumChecker ); $bandfield = new FormField( "Band", $alphanumChecker ); $albumfield = new FormField( "Album", $alphanumChecker ); $cdfield = new FormFieldMap( "CD", array( $bandfield, $albumfield ) ); $cdsfield = new FormFieldArray( "CDs", $cdfield ); // create form $myform = new Form; $myform->AddField( $namefield ); $myform->AddField( $phonefield ); $myform->AddField( $cdsfield ); $myform->SetValues( $_GET ); $ErrorStrings = array( ); $Values = $myform->GetValues( ); $Errors = $myform->GetErrors( $ErrorStrings ); $Template = new Smarty; $Template->template_dir = './smarty/templates'; $Template->compile_dir = './smarty/templates_c'; $Template->cache_dir = './smarty/cache'; $Template->config_dir = './smarty/configs'; if( $_REQUEST[ "submit" ] ) { $Template->assign( $Values ); $Template->assign( "Errors", $Errors ); $Template->assign( "ErrorStrings", $ErrorStrings ); } $Template->display( "index.tpl" ); ?>