Just thought I’d share this additional method for using the validator.
I have a structure with three models – one parent form with two children forms. Which child form is filled out depends on the toggle item selected from the parent.
Ie:
Form A:
Do you want to do B or C?
If B: Render and validate B
If C: Render and validate C
So, form A model has property B and property C.
I don’t want A to validate unless B OR C validates and I want A to be smart enough to know what it needs to do.
So, in my rules for A, I add:
array(
'BValid',
'application.components.validators.ChildrenRequiredValidator',
'parentAttribute'=>'BOrC',
'requiredValue'=>true,
'match'=>self::OPTION_B
),
array(
'CValid',
'application.components.validators.ChildrenRequiredValidator',
'parentAttribute'=>'BOrC',
'requiredValue'=>true,
'match'=>self::OPTION_C
),
And then I add the following methods to A:
public function getBValid(){ return $this->B->validate(); }
public function getCValid(){ return $this->C->validate(); }
So now, my controller will only have to run $a->validate() rather than a complex series to determine if A validates, then if B or C validates depending on which sub-section is required.
Like so in my controller action:
$model = new A();
if ( isset( $_POST['A'] ) )
{
$model->attributes = $_POST['A'];
if ( isset( $_POST['B'] ))
$model->B->attributes = $_POST['B'];
if ( isset( $_POST['C'] ))
$model->C->attributes = $_POST['C'];
// the children required way..
if ( $model->validate() )
{
// do whatever I need to do next here
}
}
Rather than the old way:
$model = new A();
if ( isset( $_POST['A'] ) )
{
$model->attributes = $_POST['A'];
if ( isset( $_POST['B'] ))
$model->B->attributes = $_POST['B'];
if ( isset( $_POST['C'] ))
$model->C->attributes = $_POST['C'];
// check which selected children have been chosen
// and ensure everything validates
if ( $model->validate() && (
$model->selectedB && $model->B->validate() ||
$model->selectedC && $model->C->validate()
))
{
// do whatever I need to do next here
}
}
The old way requries that my controller be very cognizant of the way B and C might be used and is much more confusing for another developer to maintain in the long run.
(Should be noted that I’m instancing B and C as part of the afterConstruct for A)