Multiple Model Form Validation Problem

Hi,

after reading this:

http://www.yiiframework.com/wiki/19/

and searching in the forum I not found any solution for my problem.

I created a _form like this:


<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm',array(

	'id'=>'restaurant-form',

	'enableAjaxValidation'=>false,

)); ?>


	<p class="help-block">Fields with <span class="required">*</span> are required.</p>


	<?php echo CHtml::errorSummary($model,$tags); ?>


        <div class="btn-toolbar">

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

            <?php echo $form->dropDownList($model,'country', CHtml::listData(Country::model()->findAll(), 'country_id', 'short_name'),array(

                    'empty'=>'Select Country',

                    'ajax' => array(

                    'type'=>'POST', //request type

                    'url'=>CController::createUrl('restaurant/dynamicstates'), //url to call.

                    //Style: CController::createUrl('currentController/methodToCall')

                    'success' => 'js:function(data) { 

                        var parsed = $.parseJSON(data);

                        var o = $("#state");

                        o.empty();

                        $.each(parsed.states, function(){

                        o.append($("<option value=\'"+this.id+"\'>"+this.name+"</option>"));

                        });

                        $("#Restaurant_tel").val("(+"+parsed.prefix+")"); }',

                    'update'=>'#state', //selector to update

                    'data'=>array('country'=>'js:this.value'),

                    //leave out the data key to pass all form values through

                    ))); ?>

        </div>

        

	

[b]        <?php echo $form->checkBoxListInlineRow($tags, 'idcuisintype',CHtml::listData(CuisinesTypes::model()->findAll(),'id','word')); ?>

[/b]

	

        <div class="btn-toolbar">

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

            <?php echo CHtml::dropDownList('state','', array('empty'=>'Only if Country is US')); ?>

        </div>

	

	<div class="form-actions">

		<?php $this->widget('bootstrap.widgets.TbButton', array(

			'buttonType'=>'submit',

			'type'=>'primary',

			'label'=>$model->isNewRecord ? 'Create' : 'Save',

		)); ?>

	</div>


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

the line in bold is the one that belong to $tags model.

In the actionCreate in the controller i wrote the following code:




$model=new Restaurant;

                $tags=new RestCuisineTypeRel;


		if(isset($_POST['Restaurant'],$_POST['RestCuisineTypeRel']))

		{

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

                    $tags->attributes=$_POST['RestCuisineTypeRel'];

                    $country = Country::model()->findByPk($model->country);

                    if($model->validate() & $tags->validate()) {

                        $model->save(false);

                        $tags->save(false);

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


                        }

                    }

		

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

			'model'=>$model,

                        'tags' => $tags,

		));

But after I submit the form it got this error:

[b]Object of class RestCuisineTypeRel could not be converted to string

[/b]

RestCuisineTypeRel is the model of $tags variable

can you please help me, i’m going crazy!

I don’t see anything wrong except that ‘&’ typo in your if


$model=new Restaurant;

                $tags=new RestCuisineTypeRel;


                if(isset($_POST['Restaurant'],$_POST['RestCuisineTypeRel']))

                {

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

                    $tags->attributes=$_POST['RestCuisineTypeRel'];

                    $country = Country::model()->findByPk($model->country);

                    if($model->validate() & $tags->validate()) { // I believe you need && not &

                        $model->save(false);

                        $tags->save(false);

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


                        }

                    }

                

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

                        'model'=>$model,

                        'tags' => $tags,

                ));

thank you for reply but it doesn’t solve my problem,

I fix the double & error but the

Object of class RestCuisineTypeRel could not be converted to string

continue to show when i click on submit

can you paste your stack trace

sorry what do you mean for stack trace :( ?

Ok I found the problem, i didn’t put array inside


$form->errorSummary

so before was


$form->errorSummary($model,$tags);

and now is


$form->errorSummary(array($model,$tags));

Now my problem is:

How can i show all the errors together?

because with this method it shows first all the errors of the first model, then when they are "fixed" it shows up the second model errors

the best solution is create the custom model extends to CFormModel

please see link…

http://www.yiiframework.com/doc/guide/1.1/en/form.model#defining-model-class

@Ankit Can you show some example code for this??

Looking forward to hear from you.

Yes sure… :rolleyes:

my model code…





<?php

class ChangePasswordForm extends CFormModel

{

	public $oldpassword;

	public $new_password;

	public $password_repeat;

	public $salt;

	public $id;


	public function rules(){


		//$message ="<span class='ui-icon ui-icon-alert'></span><span class='app'>". Yii::t('app','{attribute} cannot be blank.')."</span>";

		return array(

			array('new_password,password_repeat,oldpassword', 'required'),

			array('password_repeat', 'compare', 'compareAttribute'=>'new_password'),

		);

	}


	public function attributeLabels() {

		return array(

			'oldpassword'=>Yii::t('app', 'Old  Password'),

			'new_password' => Yii::t('app', 'New Password'),

			'password_repeat' => Yii::t('app', 'Repeat New Password'),

		);

	}


}




my controller code


public function actionChange($id)

	{


		$model=Users::model()->findByPk((int)$id);

		$modelForm=new ChangePasswordForm();


		if(isset($_POST['ChangePasswordForm']['password']) && !empty($_POST['ChangePasswordForm']['password']))

		{

			if((count(CJSON::decode(CActiveForm::validate($modelForm)))>0)) {

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

						'model'=>$modelForm,

				));

				Yii::app()->end();

			}

			$modelForm->password_repeat = $_POST['ChangePasswordForm']['password_repeat'];

			$model->password=md5($modelForm->password_repeat);

			$model->save(false);

			if (!$model->hasErrors()) {

				Yii::app()->user->setFlash('success', "Password change successfully!");

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

			}else {

				Yii::app()->user->setFlash('error', "Password change failure!");

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

			}

		}

		$this->render('change',array('model'=>$modelForm,));


	}

and my view code…


<div class="form">


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

	'id'=>'pwd-form',

	'enableAjaxValidation'=>false,

)); ?>


	<p class="note">

		<?php echo Yii::t('app', 'Fields with'); ?> <span class="required">*</span> <?php echo Yii::t('app', 'are required'); ?>.

	</p>


	<?php echo $form->errorSummary($model, Yii::t('app','Please fix the following input errors:')); ?>

	<div class="row">

	<?php ?>

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

			<?php echo $form->passwordField($model,'password',array('size'=>40,'maxlength'=>40,'value'=>'')); ?>

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

			

	</div>

	

	<div class="row">

	<?php ?>

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

			<?php echo $form->passwordField($model,'password_repeat',array('size'=>40,'maxlength'=>40,'value'=>'')); ?>

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

			

	</div>

	

	<div class="row buttons">

		<?php echo CHtml::submitButton(Yii::t('app','Change Password')); ?>

	</div>

	

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


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

Well Ankit that’s ok whatever code you have done here.

But The problem here is validating a multiple model instance which have a relation like Belongs to or Has Many.

As i can see in your code you are not doing anything like that and even though in your code you are validating both these models differently.Which should not happen.

So, my concern here is validating related model validation here.Which is not happening in your code. :)

Do, you have this kind of sample code ??

yes sure,

In this model email,username,fisrtname,lastname,password filed is stored the user table and any other filed stored in customer table…so i create the common model which is combine user and customer.


<?php


/**

 * CustomerForm class.

 * CustomerForm is the data structure for keeping

 *

 */

class CustomerForm extends CFormModel

{

	public $email;

	public $username;

	public $password;

	public $password_repeat;

	public $firstname;

	public $lastname;

	public $birth_day;

	public $phone_number;

	public $address;

	public $address2;

	public $city;

	public $zipcode;

	public $state;

	public $country;

	public $preferred_contact;

	public $over_21;

	public $newsletter_subscribe;

	public $is_active;

	public $user_id;







	public static function model($className = __CLASS__) {

		return parent::model($className);

	}





	/**

	 * Declares the validation rules.

	 */





	public function rules()

	{

		return array(

		// name, email, subject and body are required

		array('email,username,firstname,lastname', 'required'),

		array('email', 'email'),

		array('zipcode,phone_number', 'numerical'),

		array('password', 'required', 'on'=>'update'),

		array('password_repeat', 'compare', 'compareAttribute'=>'password', 'on'=>'update'),


		);

	}







	public static function label($n = 1) {

		return Yii::t('app', 'Customers|Customers', $n);

	}


	public function attributeLabels() {

		return array(

			

			'address' => Yii::t('app', 'Address'),

			'address2' => Yii::t('app', 'Address2'),

			

		);

	}


}


public function actionCreate() {

		$model = new CustomerForm('update');





		//$model->setScenario('update'); //

		if(isset($_POST['CustomerForm']) && !empty($_POST['CustomerForm'])){

			$user= new Users();


			$user->attributes=$_POST['CustomerForm'];

			$user->password=md5($_POST['CustomerForm']['password']);


			if($_POST['CustomerForm']['email']!='')

			{

				$Obj = $user->findByAttributes(array('email'=>$_POST['CustomerForm']['email']));

				if(isset($Obj->attributes))

				{

					$user->attributes=$_POST['CustomerForm'];

					Yii::app()->user->setFlash('error', Yii::t("messages","There is already an account with this email address, please try again.!"));

					$this->render('create',array('model' => $model));

					Yii::app()->end();

				}

			}




			$criteria = new CDbCriteria;

			$criteria->select = 'max(unique_id) as unique_id';

			$row = $user->find($criteria);


			// increment in unique id ::

			$user->unique_id = substr($row->unique_id, 3);

			//

			$incre = $user->unique_id + 1;

			$user->unique_id = "RKC" . $incre;


			$user->save(false);




			//p($_POST);

			$customer= new Customers();

			$customer->user_id=$user->id;

			$customer->attributes=$_POST['CustomerForm'];

			$customer->save(false);




			if (!$model->hasErrors()) {

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

			}


		}


		$this->render('create', array('model' => $model));


		//$this->render('create', array( 'model' => $model));

	}




Hmm…Still your model class is validating through static fields (or i can say fix number of fields)

Suppose, i have a form which is taking User information (UserModel)and we can add n number of Address(UserAddress Model) on the same page.(I am creating dynamic row by clicking on add new button. I hope you got my point here ;))

So, basically User->UserAddress have a has many relation.i hope, i have clarify everything here and i am assuming it would help you to provide answer in a very straight way. :)

yes sure…i got the answer…

if i want to add a multiple email address on _form.php then and add a validation so please follow the code…


<input type="hidden" id="emailCountID" value="<?php echo count($objCustomerEmail); ?>">

<input type="hidden" id="emaillblCountID" value="<?php echo count($objCustomerEmail); ?>">


<div class="row">

                            <div class="col410 float_l">

                                <?php echo $form->labelEx($objUsers, 'email', array('class' => 'width135')); ?>


                                <span class="input"><?php echo $form->textField($objUsers, 'email', array('class' => 'width219 email required')); ?></span>


                                <span class="addmore"><a href="javascript:void(0);" onclick="addEmailDiv();"><img src="../../../images/addIcon.png"></a></span>

                            </div>

                        </div>


function addEmailDiv()

    {

        emailNew = $('#emailNew').val();

        countId = $('#emailCountID').val();

        countlblId = parseInt($('#emaillblCountID').val()) + 1;

        addEmail = parseInt(countId) + 1;

        $('#emailWrap').append('<div class="" id="div' + addEmail + '"><div class="col410 row float_l"><label class="width135" for="Users_email">Email' + addEmail + '</label><span class="input"><input id="email_' + addEmail + '" type="text" value="" name="email[new' + emailNew + ']" class="width219 email required"></span><span class="addmore"><a href="javascript:void(0);" onclick="deleteEmailDiv(' + addEmail + ');"><img src="../../../images/minus.png"></a></span></div></div>');


        countId = parseInt(countId) + 1;

        emailNew = parseInt(emailNew) + 1;

        $('#emailNew').val(emailNew);

        $('#emailCountID').val(countId);

        $('#emaillblCountID').val(countlblId);

    }




remove a email filed one by one…


function deleteEmailDiv(divId)

    {

        $('#div' + divId).remove();

        countlblId = $('#emaillblCountID').val();

        countId = $('#emailCountID').val();

        $('#emaillblCountID').val(parseInt(countlblId) - 1);

        var j = 1;

        for (var i = 1; i <= countId; i++) {

            if ($("#div" + i).length > 0) {

                $("#div" + i).find("label").text("Email" + j)

                j = j + 1;

            }

        }

    }

3)And finally you want to insert the record you can do the foreach loop…

i hope you can got the point… :rolleyes:

Still you are not upto to the mark :) :)

I hope you know very well that we have to take care of server side validation also (Which is must !!) Which is missing here.So, i hope you got my point here what i am expecting from you.i am taking about the Yii model validations here (Server side validations not about Client side validation) ;)

And Regarding this

The way you have code is not consider as a good practice. You should have to use some methods or behaviors to deal with this kind of situation. ;) :)

Have you available to any solutions ?

Yupp ;)

i am working on it…after finishing everything i’ll keep you posted about it.;)

Ok cool… :rolleyes: i wait…