I’m curious what is your way of handling forms with many optional group of fields. Lets take a contact form with some standard fields like name, surname, mail, phone. Additionally, there are 3 checkboxes. Checking any of them unreveals different, additional group of 4, 5 fields to fill.
My first attempt to solve that was to declare a scenario for every combination of checkboxes turned on. In this example it would look like that:
- mandatory fields are without scenario so they are always validated
- there must be a different scenario for each optional field group: scenarioA, scenarioB, scenarioC
- and we have to prepare a scenario for every combination of checkboxes: scenarioAB, scenarioAC, scenarioABC, scenarioBC
Next all these scenarios have to be assigned to fields which may be optionally filled. Now here is my question. What if I had 5 or 6 optional fields group? That would give me a huge amount of scenarios to prepare. Am I missing something, or there is no better way to solve that using tools build in Yii ?
What if there was an option to create a meta scenarios, or set many active scenarios before validation?
EDIT
I have created a sample behavior which can create metaScenario on the fly. Basically it allows to assign few scenarios to single validation run.
Attached is MetaScenario behaviour. Put it into extensions directory.
Usage is simple. In model add following to configure behaviour
public function behaviors(){
return array(
'MetaScenario' => array(
'class' => 'application.extensions.MetaScenario', //path to class file
'mark' => 'META' // trigger which will run meta scenario generator
),
);
}
and modify rules and safe attributes functions
$rules = array(
//your rules set. Example:
// default rules
array('name, surname, mail', 'required', 'on'=>'main'),
array('mail', 'email', 'on'=>'main'),
array('verifyCode', 'captcha', 'on'=>'main'),
// scenario 1 rules
array('from, to, nights, persons', 'required', 'on'=>'scenario1'),
array('nights, persons', 'numerical','integerOnly'=>true, 'on'=>'scenario1'),
// scenario 2 rules
array('carFrom, carTo', 'required', 'on'=>'scenario2'),
);
$this->createMetaRule(&$rules,$this->getScenario());
return $rules;
public function safeAttributes()
{
$scenario1 = array('from', 'to', 'nights', 'persons');
$scenario2 = array('carFrom', 'carTo');
$main = array_diff($this->attributeNames(), $scenario1, $scenario2); //This is formModel example and I want all attributes not used in other scenarios to be considered safe
$attributes = array(
'main'=>implode(', ',$main),
'scenario1'=>implode(', ',$scenario1),
'scenario2'=>implode(', ',$scenario2),
);
$this->createMetaSafeAttributes(&$attributes,$this->getScenario());
return $attributes;
}
finally in controller set the scenario name in format yourMarkKeyword_defaultScenario_Scenario1_Scenario2_(…)_ScenarioN
for example:
$contact = new ContactForm;
$scenario = 'META_main';
$scenario .= ($_POST['ContactForm']['hotel']=='rent')?'_scenario1':'';
$scenario .= ($_POST['ContactForm']['car']=='rent')?'_scenario2':'';
$contact->setScenario($scenario);
Thats all. behavior will create another rule and safe Attributes set containing rules and safe attributes from all scenarios separated with " _ ".
Note that scenarios names I used are just examples. You may use any names as long as they don’t contain " _ " symbol as it is used as separator. Alse keep in mind that methods createMetaRules and createMetaSafeAttributes take reference to an array not an array.
Comments are welcome.