I recently had to work on a signup form where the user was present different set of fields based on where the user came from. Lets take a sample scenario, I have a signup form where users get to from the “Plans” page.
If the user chooses to go with the Free Plan, he gets to a page which asks for his username and password only where as if the user chooses the “Paid” plan, he is presented with the full form including his username, password, credit card info and expiry date.
I shall cover writing the Credit Card Validator for Symony in one of the next few posts potentially for now, just documenting what I did for implementing the conditional validator.
So the standard RegisterForm.class logged like this:
public function configure() {
// Remove all widgets we don't want to show
unset(
$this['is_active'],
$this['is_super_admin'],
$this['updated_at'],
$this['groups_list'],
$this['permissions_list'],
$this['last_login'],
$this['created_at'],
$this['salt'],
$this['algorithm']
);
// Setup proper password validation with confirmation
$this->widgetSchema['password'] = new sfWidgetFormInputPassword();
$this->widgetSchema['password_confirmation'] = new sfWidgetFormInputPassword();
$this->widgetSchema['cc'] = new sfWidgetFormInput();
$this->widgetSchema['toc'] = new sfWidgetFormInputCheckbox();
// assuming someone would have a card which
// expires as far as 10years from today
$years = range(date('Y'), date('Y') + 10);
$this->widgetSchema['cc_expiry_date'] = new sfWidgetFormI18nDate(
array(
'can_be_empty' => 'false',
'years'=> array_combine($years, $years),
'culture'=>'en',
'month_format'=>'name',
'format'=> '%month% %year%')
);
$this->widgetSchema->setLabel('username', 'Email');
$this->widgetSchema->setLabel('password_confirmation', 'Confirm Password');
$this->widgetSchema->setLabel('cc', 'Credit Card Number');
$this->widgetSchema->setLabel('cc_expiry_date', 'Credit Card Expiry Date');
$this->widgetSchema->setLabel('toc', 'I agree to the Terms of Service, Privacy, & Refund policies');
$this->validatorSchema['username'] = new sfValidatorEmail();
$this->validatorSchema['cc'] = new sfValidatorCreditCard();
$this->validatorSchema['toc'] = new sfValidatorBoolean(
array('required' => true),
array('required'=> 'You need to accept the terms and conditions to proceed')
);
$this->validatorSchema['password_confirmation'] = clone $this->validatorSchema['password'];
$this->validatorSchema['password']->setOption('required', true);
$this->validatorSchema['cc_expiry_date'] = new sfValidateCCExpiryDate(array('min' => time()),
array('invalid' => 'Please enter a valid expiry date'));
$this->widgetSchema->moveField('password_confirmation', 'after', 'password');
$this->mergePostValidator(new sfValidatorSchemaCompare('password',
sfValidatorSchemaCompare::EQUAL, 'password_confirmation',
array(),
array('invalid' => 'The two passwords must be the same.')));
as you can see credit card and expiry date are a required field, however i don’t want to use that validation when it comes to doing a signup for a basic plan which will not have credit card and expiry date fields. So, this is implemented by overriding the bind method to change the validation rules based on the input provided. In the code snippet below the field validations change based on the hidden value price_id passed.
public function bind(array $taintedValues = null, array $taintedFiles = null)
{
$price = PricePeer::retrieveByPK(@$taintedValues['price_id']);
if (!$price) {
return false;
}
if(0 == $price->getPrice()) {
// turn off all the other validations
$this->validatorSchema['cc_expiry_date']->setOption('required',false);
$this->validatorSchema['cc']->setOption('required',false);
}
return parent::bind($taintedValues,$taintedFiles);
}
I would be interested in knowing how you worked around this problem. Do leave a comment below.
