Cannot Able To Validate Multiple Models Into One Form

Hi everyone,

I’ve started in Yii some weeks ago and I’m very happy to have met this framework, that I find powerful and easy to learn, at the same time.

Well Well Well… I have a problem :)

I’ve created a form that collect multiple models data, main model and added models. Of course I’ve edited the main controller and the form as well, adding three models.

I cannot validate added models. What happens is that form works fine if data don’t have errors or if main field (from main model) don’t have errors. If one of added field (from the three added models) have some errors I don’t get to my HTML the classic errorSummary div.

What I get instead is:

Fatal error: Call to a member function isAttributeRequired() on a non-object in C:\wamp\www\yii\framework\web\helpers\CHtml.php on line 1414

Before posting I’ve read very interesting articles like

http://www.yiiframework.com/wiki/19/how-to-use-a-single-form-to-collect-data-for-two-or-more-models/ and http://www.yiiframework.com/wiki/362/how-to-use-multiple-instances-of-the-same-model-in-the-same-form/

…but looks like them cannot help me. Anyone of you can help me?

Very thanks

You should post some snipplets of code, that is how you define your views and a stack trace for that error message you get.

Ok, of course.

We have main model, Ancestors.php




<?php


/**

 * This is the model class for table "{{ancestors}}".

 *

 * The followings are the available columns in table '{{ancestors}}':

 * @property string $id

 * @property string $name

 * @property string $surname

 */

class Ancestors extends CActiveRecord

{

	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return '{{ancestors}}';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('name, surname', 'length', 'max'=>255),

			array('name', 'required'),

			// The following rule is used by search().

			// @todo Please remove those attributes that should not be searched.

			array('id, name, surname', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

		    'events'=>array(self::HAS_MANY, 'Events', 'ancestor_id'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'name' => 'Name',

			'surname' => 'Surname',

		);

	}


	/**

	 * Retrieves a list of models based on the current search/filter conditions.

	 *

	 * Typical usecase:

	 * - Initialize the model fields with values from filter form.

	 * - Execute this method to get CActiveDataProvider instance which will filter

	 * models according to data in model fields.

	 * - Pass data provider to CGridView, CListView or any similar widget.

	 *

	 * @return CActiveDataProvider the data provider that can return the models

	 * based on the search/filter conditions.

	 */

	public function search()

	{

		// @todo Please modify the following code to remove attributes that should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id,true);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('surname',$this->surname,true);


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}


	/**

	 * Returns the static model of the specified AR class.

	 * Please note that you should have this exact method in all your CActiveRecord descendants!

	 * @param string $className active record class name.

	 * @return Ancestors the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}

}




The other model is Events.php




<?php


/**

 * This is the model class for table "{{events}}".

 *

 * The followings are the available columns in table '{{events}}':

 * @property string $id

 * @property integer $type_id

 * @property string $type_string

 * @property integer $ancestor_id

 * @property integer $year

 * @property integer $month

 * @property integer $day

 * @property string $julianday_min

 * @property string $julianday_max

 * @property string $place

 * @property integer $parish_id

 */

class Events extends CActiveRecord

{

	/**

	 * @return string the associated database table name

	 */

	public function tableName()

	{

		return '{{events}}';

	}


	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			// tolgo required fields per aggirare ostacoli nei form !!!

			// array('type_string, ancestor_id, julianday_min, julianday_max', 'required'),

			array('type_id, ancestor_id, year, month, day, parish_id', 'numerical', 'integerOnly'=>true),

			array('day', 'numerical', 'min'=>1, 'max'=>31, 'allowEmpty'=>true),

			array('month', 'numerical', 'min'=>1, 'max'=>12, 'allowEmpty'=>true),

			array('type_string', 'length', 'max'=>4),

			array('julianday_min, julianday_max', 'length', 'max'=><img src='http://www.yiiframework.com/forum/public/style_emoticons/default/cool.gif' class='bbc_emoticon' alt='8)' />,

			array('place', 'length', 'max'=>255),

			// The following rule is used by search().

			// @todo Please remove those attributes that should not be searched.

			array('id, type_id, type_string, ancestor_id, year, month, day, julianday_min, julianday_max, place, parish_id', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @return array relational rules.

	 */

	public function relations()

	{

		// NOTE: you may need to adjust the relation name and the related

		// class name for the relations automatically generated below.

		return array(

		    'parish'=>array(self::BELONGS_TO, 'Parishes', 'parish_id'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'type_id' => 'Type',

			'type_string' => 'Type String',

			'ancestor_id' => 'Ancestor',

			'year' => 'Year',

			'month' => 'Month',

			'day' => 'Day',

			'julianday_min' => 'Julianday Min',

			'julianday_max' => 'Julianday Max',

			'place' => 'Place',

			'parish_id' => 'Parish',

		);

	}


	/**

	 * Retrieves a list of models based on the current search/filter conditions.

	 *

	 * Typical usecase:

	 * - Initialize the model fields with values from filter form.

	 * - Execute this method to get CActiveDataProvider instance which will filter

	 * models according to data in model fields.

	 * - Pass data provider to CGridView, CListView or any similar widget.

	 *

	 * @return CActiveDataProvider the data provider that can return the models

	 * based on the search/filter conditions.

	 */

	public function search()

	{

		// @todo Please modify the following code to remove attributes that should not be searched.


		$criteria=new CDbCriteria;


		$criteria->compare('id',$this->id,true);

		$criteria->compare('type_id',$this->type_id);

		$criteria->compare('type_string',$this->type_string,true);

		$criteria->compare('ancestor_id',$this->ancestor_id);

		$criteria->compare('year',$this->year);

		$criteria->compare('month',$this->month);

		$criteria->compare('day',$this->day);

		$criteria->compare('julianday_min',$this->julianday_min,true);

		$criteria->compare('julianday_max',$this->julianday_max,true);

		$criteria->compare('place',$this->place,true);

		$criteria->compare('parish_id',$this->parish_id);


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}


	/**

	 * Returns the static model of the specified AR class.

	 * Please note that you should have this exact method in all your CActiveRecord descendants!

	 * @param string $className active record class name.

	 * @return Events the static model class

	 */

	public static function model($className=__CLASS__)

	{

		return parent::model($className);

	}

}




We have AncestorsController.php (I show you only until actionCreate, because I’ll work for actionUpdate only when create is ok)





class AncestorsController extends Controller

{

	/**

	 * @var string the default layout for the views. Defaults to '//layouts/column2', meaning

	 * using two-column layout. See 'protected/views/layouts/column2.php'.

	 */

	public $layout='//layouts/column2';


	/**

	 * @return array action filters

	 */

	public function filters()

	{

		return array(

			'accessControl', // perform access control for CRUD operations

			'postOnly + delete', // we only allow deletion via POST request

		);

	}


	/**

	 * Specifies the access control rules.

	 * This method is used by the 'accessControl' filter.

	 * @return array access control rules

	 */

	public function accessRules()

	{

		return array(

			array('allow',  // allow all users to perform 'index' and 'view' actions

				'actions'=>array('index','view'),

				'users'=>array('*'),

			),

			array('allow', // allow authenticated user to perform 'create' and 'update' actions

				'actions'=>array('create','update'),

				'users'=>array('@'),

			),

			array('allow', // allow admin user to perform 'admin' and 'delete' actions

				'actions'=>array('admin','delete'),

				'users'=>array('admin'),

			),

			array('deny',  // deny all users

				'users'=>array('*'),

			),

		);

	}


	/**

	 * Displays a particular model.

	 * @param integer $id the ID of the model to be displayed

	 */

	public function actionView($id)

	{

		$this->render('view',array(

			'model'=>$this->loadModel($id),

		));

	}


	/**

	 * Creates a new model.

	 * If creation is successful, the browser will be redirected to the 'view' page.

	 */

	public function actionCreate()

	{

		$model = new Ancestors;

		$event_model = array();

		$event_model[] = new Events;

		$event_model[] = new Events;

		$event_model[] = new Events;

		

		// Uncomment the following line if AJAX validation is needed

		//$this->performAjaxValidation($model);

		

		if(!empty($_POST))

		{

		    // Set attribute for ancestor

		    $model->attributes=$_POST['Ancestors'];

		    // Set attribute for events

		    foreach ($event_model as $k => $event) {

			$event_model[$k]->attributes = $_POST['Events'][$k];

		    }

		    

		    // Validate all models

		    $valid=$model->validate();

		    

		    foreach ($event_model as $k => $event) {

			/*

			$valid=$event_model[$k]->validate() && $valid;

			*/

			$event_model[$k]->validate();

			$event_errors = $event_model[$k]->getErrors();

			var_dump($event_errors);

			

			if (empty($event_errors)) {

				$valid = $valid && true;

			} else {

				$valid = $valid && false;

			}

		    }

		    

		    if($valid)

		    {

			// save main model

			$model->save();

			

			// ATTENTION: To get ancestor_id we need to save it before...

			// set foreign keys for event models

			$ancestor_id = $model->id;

			

			$event_model[0]->type_string = 'BIRT';

			$event_model[1]->type_string = 'BAPT';

			$event_model[2]->type_string = 'DEAT';

			foreach ($event_model as $k => $event) {

				$event_model[$k]->type_id = $k+1;

				$event_model[$k]->ancestor_id = $ancestor_id;

			}

			

			// set julian days

			// WIP to avoid errors...

			/*

			$event_model_1->julianday_min = gregoriantojd ($event_model_1->month , $event_model_1->day , $event_model_1->year);

			$event_model_1->julianday_max = gregoriantojd ($event_model_1->month , $event_model_1->day , $event_model_1->year);

			$event_model_2->julianday_min = gregoriantojd ($event_model_2->month , $event_model_2->day , $event_model_2->year);

			$event_model_2->julianday_max = gregoriantojd ($event_model_2->month , $event_model_2->day , $event_model_2->year);

			*/

			

			// save event models

			foreach ($event_model as $k => $event) {

				// check before save 

				if ( ($event_model[$k]->place!=null) || ($event_model[$k]->parish_id > 0) || ($event_model[$k]->day!=null) || ($event_model[$k]->month!=null) || ($event_model[$k]->year!=null) )

				{

					$event_model[$k]->save();

				}

			}

			

			// save contribute

			$contribution = new Contributions;

			$contribution->contribution_type = 'CREATE';

			$contribution->user_id = Yii::app()->user->getId();

			$contribution->username = Yii::app()->user->name;

			$contribution->object_type = 'Ancestors';

			$contribution->object_id = $model->id;

			$contribution->submit_time = time();

			// save without validation

			$contribution->save(false);

			

			$this->redirect(array('view','id'=>$model->id));

			

		    }

		    

		}

		

		$this->render('create',array(

		    'model'=>$model,

		    'event_model[0]'=>$event_model[0],

		    'event_model[1]'=>$event_model[1],

		    'event_model[2]'=>$event_model[2],

		));

	}




[b]

This is the form where I collect data (Ancestors form)[/b]





<?php

/* @var $this AncestorsController */

/* @var $model Ancestors */

/* @var $form CActiveForm */

?>


<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'ancestors-form',

	// Please note: When you enable ajax validation, make sure the corresponding

	// controller action is handling ajax validation correctly.

	// There is a call to performAjaxValidation() commented in generated controller code.

	// See class documentation of CActiveForm for details on this.

	'enableAjaxValidation'=>false,

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


	<?php

	

		$event_model=array();

		$event_model[]=new Events;

		$event_model[]=new Events;

		$event_model[]=new Events;

		

		//echo $form->errorSummary($model);

		echo $form->errorSummary($model);

		echo $form->errorSummary($event_model[0]);

		echo $form->errorSummary($event_model[1]);

		echo $form->errorSummary($event_model[2]);

	?>

	


	<div class="row">

		<?php echo $form->labelEx($model,'name'); ?>

		<?php echo $form->textField($model,'name',array('size'=>60,'maxlength'=>255)); ?>

		<?php echo $form->error($model,'name'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'surname'); ?>

		<?php echo $form->textField($model,'surname',array('size'=>60,'maxlength'=>255)); ?>

		<?php echo $form->error($model,'surname'); ?>

	</div>

	

<?php

	

	if ((isset($_POST['Events'])) && (sizeof($_POST['Events'])>0))

	{

		foreach ($_POST['Events'] as $k => $event) {

			$event_model[$k]->attributes = $_POST['Events'][$k];

		}

	}

	

	// load events if Update...

	if (isset($model->name) && (strlen($model->name))>0) {

		$event_model[0]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BIRT'));

		$event_model[1]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BAPT'));

		$event_model[2]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'DEAT'));

	}

	

	foreach ($event_model as $k => $event) {

		

		// which event?

		switch ($k) {

		    case 0:

			echo '<h2>Birth </h2> ';

			break;

		    case 1:

			echo '<h2>Baptism </h2> ';

			break;

		    case 2:

			echo '<h2>Death </h2> ';

			break;

		}

		

		echo '

			<div class="row">

			    '.$form->labelEx($event_model[$k],'['.$k.']place').'

			    '.$form->textField($event_model[$k],'['.$k.']place').'

			    '.$form->error($event_model[$k],'['.$k.']place').'

			</div>

		';

		

		if ($k>0) {

			/* no parish for BIRTH */

			echo '

				<div class="row">

					'.$form->labelEx($event_model[$k],'['.$k.']parish_id').'

			';

			

					$parishes_list = CHtml::listData(Parishes::model()->findAll(array('order'=>'name ASC')), 'id', 'name');

					$options = array(

						'tabindex' => '0',

						'empty' => 'select Parish',

					);

					

			echo '

					'.$form->dropDownList($event_model[$k],'['.$k.']parish_id', $parishes_list, $options).'

					'.$form->error($event_model[$k],'['.$k.']parish_id').'

				</div>

			';

		}

		

		

		echo '

			<div class="row">

			    '.$form->labelEx($event_model[$k],'['.$k.']day').'

			    '.$form->textField($event_model[$k],'['.$k.']day').'

			    '.$form->error($event_model[$k],'['.$k.']day').'

			</div>

			<div class="row">

			    '.$form->labelEx($event_model[$k],'['.$k.']month').'

			    '.$form->textField($event_model[$k],'['.$k.']month').'

			    '.$form->error($event_model[$k],'['.$k.']month').'

			</div>  

			<div class="row">

			    '.$form->labelEx($event_model[$k],'['.$k.']year').'

			    '.$form->textField($event_model[$k],'['.$k.']year').'

			    '.$form->error($event_model[$k],'['.$k.']year').'

			</div>

		';

		

	}

	

?>


	

	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>


<?php $this->endWidget(); ?>


</div><!-- form -->




Thanks again!

You create three event models in the controller. You should pass them to the view this way:




                $this->render('create',array(

                    'model'=>$model,

                    'event_model'=>$event_model,

                ));



Then in the view you create them again, don’t do that.

Then again you load the event. First, this should be done in the controller. Second, they probably are not found and variables are null, hence the exception you get.

I’ve passed Events Models in the following way:




		$this->render('create',array(

		    'model'=>$model,

		    'event_model[0]'=>$event_model[0],

		    'event_model[1]'=>$event_model[1],

		    'event_model[2]'=>$event_model[2],

		));



Is it correct?

Maybe I don’t understand(?), but if I remove the following lines from the beginning of actionCreate() in the main controllor




		$event_model = array();

		$event_model[] = new Events;

		$event_model[] = new Events;

		$event_model[] = new Events;



what happens is that i get error:

PHP notice

Undefined variable: event_model

Can you explain me better?

I just told you it’s not correct. The array you pass to render() method gets extracted and each key is used as a variable name. ‘event_model[0]’ is not a valid variable name in PHP. Just pass the whole array as ‘event_model’ and the keys will be there.

I said you should remove it from the view, not the controller. It should be in the controller only.

All right. Thanks again.

I’ve just do it what you told me.

Se here we are the controller (code until actionCreate() ) :




<?php


class AncestorsController extends Controller

{

        /**

         * @var string the default layout for the views. Defaults to '//layouts/column2', meaning

         * using two-column layout. See 'protected/views/layouts/column2.php'.

         */

        public $layout='//layouts/column2';


        /**

         * @return array action filters

         */

        public function filters()

        {

                return array(

                        'accessControl', // perform access control for CRUD operations

                        'postOnly + delete', // we only allow deletion via POST request

                );

        }


        /**

         * Specifies the access control rules.

         * This method is used by the 'accessControl' filter.

         * @return array access control rules

         */

        public function accessRules()

        {

                return array(

                        array('allow',  // allow all users to perform 'index' and 'view' actions

                                'actions'=>array('index','view'),

                                'users'=>array('*'),

                        ),

                        array('allow', // allow authenticated user to perform 'create' and 'update' actions

                                'actions'=>array('create','update'),

                                'users'=>array('@'),

                        ),

                        array('allow', // allow admin user to perform 'admin' and 'delete' actions

                                'actions'=>array('admin','delete'),

                                'users'=>array('admin'),

                        ),

                        array('deny',  // deny all users

                                'users'=>array('*'),

                        ),

                );

        }


        /**

         * Displays a particular model.

         * @param integer $id the ID of the model to be displayed

         */

        public function actionView($id)

        {

                $this->render('view',array(

                        'model'=>$this->loadModel($id),

                ));

        }


        /**

         * Creates a new model.

         * If creation is successful, the browser will be redirected to the 'view' page.

         */

        public function actionCreate()

        {

                $model = new Ancestors;

                $event_model = array();

                $event_model[] = new Events;

                $event_model[] = new Events;

                $event_model[] = new Events;

                

                // Uncomment the following line if AJAX validation is needed

                //$this->performAjaxValidation($model);

                

                if(!empty($_POST))

                {

                    // Set attribute for ancestor

                    $model->attributes=$_POST['Ancestors'];

                    // Set attribute for events

                    foreach ($event_model as $k => $event) {

                        $event_model[$k]->attributes = $_POST['Events'][$k];

                    }

                    

                    // Validate all models

                    $valid=$model->validate();

                    

                    foreach ($event_model as $k => $event) {

                        /*

                        $valid=$event_model[$k]->validate() && $valid;

                        */

                        $event_model[$k]->validate();

                        $event_errors = $event_model[$k]->getErrors();

                        //var_dump($event_errors);

                        

                        if (empty($event_errors)) {

                                $valid = $valid && true;

                        } else {

                                $valid = $valid && false;

                        }

                    }

                    

                    if($valid)

                    {

                        // save main model

                        $model->save();

                        

                        // ATTENTION: To get ancestor_id we need to save it before...

                        // set foreign keys for event models

                        $ancestor_id = $model->id;

                        

                        $event_model[0]->type_string = 'BIRT';

                        $event_model[1]->type_string = 'BAPT';

                        $event_model[2]->type_string = 'DEAT';

                        foreach ($event_model as $k => $event) {

                                $event_model[$k]->type_id = $k+1;

                                $event_model[$k]->ancestor_id = $ancestor_id;

                        }

                        

                        // set julian days

                        // WIP to avoid errors...

                        /*

                        $event_model_1->julianday_min = gregoriantojd ($event_model_1->month , $event_model_1->day , $event_model_1->year);

                        $event_model_1->julianday_max = gregoriantojd ($event_model_1->month , $event_model_1->day , $event_model_1->year);

                        $event_model_2->julianday_min = gregoriantojd ($event_model_2->month , $event_model_2->day , $event_model_2->year);

                        $event_model_2->julianday_max = gregoriantojd ($event_model_2->month , $event_model_2->day , $event_model_2->year);

                        */

                        

                        // save event models

                        foreach ($event_model as $k => $event) {

                                // check before save 

                                if ( ($event_model[$k]->place!=null) || ($event_model[$k]->parish_id > 0) || ($event_model[$k]->day!=null) || ($event_model[$k]->month!=null) || ($event_model[$k]->year!=null) )

                                {

                                        $event_model[$k]->save();

                                }

                        }

                        

                        // save contribute

                        $contribution = new Contributions;

                        $contribution->contribution_type = 'CREATE';

                        $contribution->user_id = Yii::app()->user->getId();

                        $contribution->username = Yii::app()->user->name;

                        $contribution->object_type = 'Ancestors';

                        $contribution->object_id = $model->id;

                        $contribution->submit_time = time();

                        // save without validation

                        $contribution->save(false);

                        

                        $this->redirect(array('view','id'=>$model->id));

                        

                    }

                    

                }

                

                $this->render('create',array(

                    'model'=>$model,

                    'event_model'=>$event_model,

                ));

        }



and the form (view)




<?php

/* @var $this AncestorsController */

/* @var $model Ancestors */

/* @var $form CActiveForm */

?>


<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

        'id'=>'ancestors-form',

        // Please note: When you enable ajax validation, make sure the corresponding

        // controller action is handling ajax validation correctly.

        // There is a call to performAjaxValidation() commented in generated controller code.

        // See class documentation of CActiveForm for details on this.

        'enableAjaxValidation'=>false,

)); ?>


        <p class="note">Fields with <span class="required">*</span> are required.</p>


        <?php

        

	/*

                $event_model=array();

                $event_model[]=new Events;

                $event_model[]=new Events;

                $event_model[]=new Events;

	*/

                //echo $form->errorSummary($model);

                echo $form->errorSummary(array($model, $event_model));

        ?>

        

        <div class="row">

                <?php echo $form->labelEx($model,'name'); ?>

                <?php echo $form->textField($model,'name',array('size'=>60,'maxlength'=>255)); ?>

                <?php echo $form->error($model,'name'); ?>

        </div>


        <div class="row">

                <?php echo $form->labelEx($model,'surname'); ?>

                <?php echo $form->textField($model,'surname',array('size'=>60,'maxlength'=>255)); ?>

                <?php echo $form->error($model,'surname'); ?>

        </div>

        

<?php

        

        if ((isset($_POST['Events'])) && (sizeof($_POST['Events'])>0))

        {

                foreach ($_POST['Events'] as $k => $event) {

                        $event_model[$k]->attributes = $_POST['Events'][$k];

                }

        }

        

        // load events if Update...

        if (isset($model->name) && (strlen($model->name))>0) {

                $event_model[0]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BIRT'));

                $event_model[1]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BAPT'));

                $event_model[2]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'DEAT'));

        }

        

        foreach ($event_model as $k => $event) {

                

                // which event?

                switch ($k) {

                    case 0:

                        echo '<h2>Birth </h2> ';

                        break;

                    case 1:

                        echo '<h2>Baptism </h2> ';

                        break;

                    case 2:

                        echo '<h2>Death </h2> ';

                        break;

                }

                

                echo '

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']place').'

                            '.$form->textField($event_model[$k],'['.$k.']place').'

                            '.$form->error($event_model[$k],'['.$k.']place').'

                        </div>

                ';

                

                if ($k>0) {

                        /* no parish for BIRTH */

                        echo '

                                <div class="row">

                                        '.$form->labelEx($event_model[$k],'['.$k.']parish_id').'

                        ';

                        

                                        $parishes_list = CHtml::listData(Parishes::model()->findAll(array('order'=>'name ASC')), 'id', 'name');

                                        $options = array(

                                                'tabindex' => '0',

                                                'empty' => 'select Parish',

                                        );

                                        

                        echo '

                                        '.$form->dropDownList($event_model[$k],'['.$k.']parish_id', $parishes_list, $options).'

                                        '.$form->error($event_model[$k],'['.$k.']parish_id').'

                                </div>

                        ';

                }

                

                

                echo '

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']day').'

                            '.$form->textField($event_model[$k],'['.$k.']day').'

                            '.$form->error($event_model[$k],'['.$k.']day').'

                        </div>

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']month').'

                            '.$form->textField($event_model[$k],'['.$k.']month').'

                            '.$form->error($event_model[$k],'['.$k.']month').'

                        </div>  

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']year').'

                            '.$form->textField($event_model[$k],'['.$k.']year').'

                            '.$form->error($event_model[$k],'['.$k.']year').'

                        </div>

                ';

                

        }

        

?>


        

        <div class="row buttons">

                <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

        </div>


<?php $this->endWidget(); ?>


</div><!-- form -->



What happens is that now I have the following error on _form.php

[b]PHP notice

Undefined variable: event_model [/b]

exactly on this line:


 echo $form->errorSummary(array($model, $event_model)); 

Even if I remove the above line, I have same error later on _form.php:




      foreach ($event_model as $k => $event) {

// do something...

}



because $event_model is not defined. What I miss?

Thanks again

You have a ‘create’ view and a ‘_form’ partial view? Do you also pass event_model variable to ‘_form’?

Yes. I have create.php as a view and I’ve just seen that was




<?php

/* @var $this AncestorsController */

/* @var $model Ancestors */


$this->breadcrumbs=array(

	'Ancestors'=>array('index'),

	'Create',

);


$this->menu=array(

	array('label'=>'List Ancestors', 'url'=>array('index')),

	array('label'=>'Manage Ancestors', 'url'=>array('admin')),

);

?>


<h1>Create Ancestors</h1>


<?php


$event_model=array();

$event_model[]=new Events;

$event_model[]=new Events;

$event_model[]=new Events;


$this->renderPartial('_form', array('model'=>$model,

		    'event_model[0]'=>$event_model[0],

		    'event_model[1]'=>$event_model[1],

		    'event_model[2]'=>$event_model[2]

)); ?>



Now I’ve changed this into




<?php

/* @var $this AncestorsController */

/* @var $model Ancestors */


$this->breadcrumbs=array(

	'Ancestors'=>array('index'),

	'Create',

);


$this->menu=array(

	array('label'=>'List Ancestors', 'url'=>array('index')),

	array('label'=>'Manage Ancestors', 'url'=>array('admin')),

);

?>


<h1>Create Ancestors</h1>


<?php

$this->renderPartial('_form', array('model'=>$model,

                                    'event_model'=>$event_model,

)); ?>



following your advice.

However I have this error

Create Ancestors

Fields with * are required.

[b]

( ! ) Fatal error: Call to a member function getErrors() on a non-object in C:\wamp\www\yii\framework\web\helpers\CHtml.php on line 2002[/b]

About the _form.php (view) I’ve just posted before. I post it again here

And everytime I told about form view I was talking about this _form.php




<?php

/* @var $this AncestorsController */

/* @var $model Ancestors */

/* @var $form CActiveForm */

?>


<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

        'id'=>'ancestors-form',

        // Please note: When you enable ajax validation, make sure the corresponding

        // controller action is handling ajax validation correctly.

        // There is a call to performAjaxValidation() commented in generated controller code.

        // See class documentation of CActiveForm for details on this.

        'enableAjaxValidation'=>false,

)); ?>


        <p class="note">Fields with <span class="required">*</span> are required.</p>


        <?php

        

	/*

                $event_model=array();

                $event_model[]=new Events;

                $event_model[]=new Events;

                $event_model[]=new Events;

	*/

                //echo $form->errorSummary($model);

                echo $form->errorSummary(array($model, $event_model));

        ?>

        

        <div class="row">

                <?php echo $form->labelEx($model,'name'); ?>

                <?php echo $form->textField($model,'name',array('size'=>60,'maxlength'=>255)); ?>

                <?php echo $form->error($model,'name'); ?>

        </div>


        <div class="row">

                <?php echo $form->labelEx($model,'surname'); ?>

                <?php echo $form->textField($model,'surname',array('size'=>60,'maxlength'=>255)); ?>

                <?php echo $form->error($model,'surname'); ?>

        </div>

        

<?php

        

        if ((isset($_POST['Events'])) && (sizeof($_POST['Events'])>0))

        {

                foreach ($_POST['Events'] as $k => $event) {

                        $event_model[$k]->attributes = $_POST['Events'][$k];

                }

        }

        

        // load events if Update...

        if (isset($model->name) && (strlen($model->name))>0) {

                $event_model[0]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BIRT'));

                $event_model[1]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BAPT'));

                $event_model[2]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'DEAT'));

        }

        

        foreach ($event_model as $k => $event) {

                

                // which event?

                switch ($k) {

                    case 0:

                        echo '<h2>Birth </h2> ';

                        break;

                    case 1:

                        echo '<h2>Baptism </h2> ';

                        break;

                    case 2:

                        echo '<h2>Death </h2> ';

                        break;

                }

                

                echo '

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']place').'

                            '.$form->textField($event_model[$k],'['.$k.']place').'

                            '.$form->error($event_model[$k],'['.$k.']place').'

                        </div>

                ';

                

                if ($k>0) {

                        /* no parish for BIRTH */

                        echo '

                                <div class="row">

                                        '.$form->labelEx($event_model[$k],'['.$k.']parish_id').'

                        ';

                        

                                        $parishes_list = CHtml::listData(Parishes::model()->findAll(array('order'=>'name ASC')), 'id', 'name');

                                        $options = array(

                                                'tabindex' => '0',

                                                'empty' => 'select Parish',

                                        );

                                        

                        echo '

                                        '.$form->dropDownList($event_model[$k],'['.$k.']parish_id', $parishes_list, $options).'

                                        '.$form->error($event_model[$k],'['.$k.']parish_id').'

                                </div>

                        ';

                }

                

                

                echo '

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']day').'

                            '.$form->textField($event_model[$k],'['.$k.']day').'

                            '.$form->error($event_model[$k],'['.$k.']day').'

                        </div>

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']month').'

                            '.$form->textField($event_model[$k],'['.$k.']month').'

                            '.$form->error($event_model[$k],'['.$k.']month').'

                        </div>  

                        <div class="row">

                            '.$form->labelEx($event_model[$k],'['.$k.']year').'

                            '.$form->textField($event_model[$k],'['.$k.']year').'

                            '.$form->error($event_model[$k],'['.$k.']year').'

                        </div>

                ';

                

        }

        

?>

   

        <div class="row buttons">

                <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

        </div>


<?php $this->endWidget(); ?>


</div><!-- form -->



You can’t do:




echo $form->errorSummary(array($model, $event_model));



because $event_model is an array of models. You have to do:




echo $form->errorSummary(array_merge(array($model), $event_model));



so you pass an array of 4 models to errorSummary().

Very thanks. Now I can insert data correctly.

But I still have a problem.

When I insert data from form and the fields of main model have atleast one error then validation (with errorSummary) works perfectly, showing all messages (even checking other models’ fields) before the form.

When I insert data from form and the fields of main model are valid then if one or more fields from the added model have error, validation does not work! In this last case what I get on screen is

[b]

Please fix the following input errors:

Day must be a number.


Day must be a number.

Name *

Surname

Birth

( ! ) Fatal error: Call to a member function isAttributeRequired() on a non-object in C:\wamp\www\yii\framework\web\helpers\CHtml.php on line 1414[/b]

In my previous post you can see how I’ve changed create.php. Is it correct?

Thanks

When you post error messages please always remember to include the stack trace. No one likes to guess where that error comes from.

Ooops, sorry.

An example:




Create Ancestors


Fields with * are required.


Please fix the following input errors:


    Day must be a number.

    Day must be a number.


Name *

Surname

Birth


( ! ) Fatal error: Call to a member function isAttributeRequired() on a non-object in C:\wamp\www\yii\framework\web\helpers\CHtml.php on line 1414

Call Stack

#	Time	Memory	Function	Location

1	0.0003	148272	{main}( )	..\index.php:0

2	0.0208	1133984	CApplication->run( )	..\index.php:13

3	0.0208	1134592	CWebApplication->processRequest( )	..\CApplication.php:180

4	0.0227	1254536	CWebApplication->runController( )	..\CWebApplication.php:141

5	0.0277	1538040	CController->run( )	..\CWebApplication.php:282

6	0.0286	1566096	CController->runActionWithFilters( )	..\CController.php:265

7	0.0300	1617064	CFilterChain->run( )	..\CController.php:291

8	0.0301	1618224	CInlineFilter->filter( )	..\CFilterChain.php:130

9	0.0301	1618312	CController->filterAccessControl( )	..\CInlineFilter.php:58

10	0.0310	1672880	CFilter->filter( )	..\CController.php:1145

11	0.0344	1853992	CFilterChain->run( )	..\CFilter.php:40

12	0.0344	1853992	CController->runAction( )	..\CFilterChain.php:133

13	0.0345	1854064	CInlineAction->runWithParams( )	..\CController.php:308

14	0.0345	1854400	AncestorsController->actionCreate( )	..\CInlineAction.php:49

15	0.0830	3271264	CController->render( )	..\AncestorsController.php:156

16	0.0830	3271392	CController->renderPartial( )	..\CController.php:782

17	0.0834	3272032	CBaseController->renderFile( )	..\CController.php:869

18	0.0834	3272208	CBaseController->renderInternal( )	..\CBaseController.php:95

19	0.0839	3291880	require( 'C:\wamp\www\project\protected\views\ancestors\create.php' )	..\CBaseController.php:126

20	0.0839	3293736	CController->renderPartial( )	..\create.php:28

21	0.0841	3293936	CBaseController->renderFile( )	..\CController.php:869

22	0.0841	3294000	CBaseController->renderInternal( )	..\CBaseController.php:95

23	0.0849	3336024	require( 'C:\wamp\www\project\protected\views\ancestors\_form.php' )	..\CBaseController.php:126

24	0.1019	4097200	CActiveForm->labelEx( )	..\_form.php:67

25	0.1019	4097280	CHtml::activeLabelEx( )	..\CActiveForm.php:597



You’re trying to load the event models in the _form view and that probably sets them to null:




        if (isset($model->name) && (strlen($model->name))>0) {

                $event_model[0]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BIRT'));

                $event_model[1]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'BAPT'));

                $event_model[2]=Events::model()->findByAttributes(array('ancestor_id' => $model->id, 'type_string' => 'DEAT'));

        }



OOOOOOOOOOOO Yeah! Very thanks! You solved my problem.