AR - skipOnError not working

Hello,

I’m experiencing this error for several times, again and again.

Got some classic CForm model


class OrderForm extends CFormModel {

  public $userId;

  public $userLogin;


   // bla bla bla


  public function rules() {

    return array(

      array('userLogin', 'required', 'on'=>'insert'),

      array('userId',    'required', 'on'=>'insert', 'skipOnError'=>true),

    );

  }


  // bla bla bla

}

Some customized template


<?php $form=$this->beginWidget('CActiveForm'); ?>

  <table id="usr">

    <tr>

      <th><?php echo Yii::t('models', 'Login') ?></th>

      <td>

        <?php $this->widget('zii.widgets.jui.CJuiAutoComplete', array('name'=>'OrderForm[userLogin]',

          'sourceUrl' => $this->createUrl('/orders/userSuggest'),

          'options'=>array(

            'minLength' => '2',

            'select'    => 'js:afterSelect',

           ),

        )); ?>


        <?php if ($form->error($orderModel, 'userLogin')) { ?>

          <div class="error">

            <?php echo $form->error($orderModel, 'userLogin'); ?>

          </div>

        <?php } ?>

        <?php if ($form->error($orderModel, 'userId')) { ?>

          <div class="error">

            <?php echo $form->error($orderModel, 'userId'); ?>

          </div>

        <?php } ?>

      </td>

    </tr>

    bla bla bla

Worth of note - in js:afterSelect I manualy create hidden field named userId. So by this I created auto suggesting form, which send back to server found userLogin and userId.

Controller:


public function actionCreate() {

  $orderModel = new OrderForm();


  if (Yii::app()->request->getIsPostRequest()) {

    $orderModel->userId = (isset($_POST['OrderForm']['userId'])) ? $_POST['OrderForm']['userId'] : '';

    $orderModel->userLogin = $_POST['OrderForm']['userLogin'];


    if ($orderModel->validate()) {

      // bla bla bla

    }

  }


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

    'orderModel' => $orderModel,

  ));

}

Pretty classic stuff too. Problem is, when user sends form without filling user login. Both errors get triggered and user obtain two error messages. Because user submitted form without autosuggest "loop", userId field is not even sended to server (it is created in afterSend() callback of CUI). But Controller handle this just fine a fill empty string into OrderForm. But as the topic says - both validators get fired. Any ideas?

As documentation says: "…rule should be skipped if when there is already a validation error for the current attribute". You have two validation rules for two different attributes.

It can be solved by writing a validator method for userId. If userLogin attribute already has an error method should simply return. If it hasn’t and userId is invalid the method can add an error to userId.

I know this is an old thread, but I found it because I had the same problem.

I ended up writing my own unique validator class, which checks the AR doesn’t already have errors, which I wanted especially for checking unique usernames so the user would have to fill out everything else, including the Captcha Code before it would confirm if the username was taken or not as an added security to prevent someone from automatically obtaining a list of usernames.

Here is what I wrote, perhaps you could make something better.

I preferred to use pure SQL since it’s quicker than using AR’s and the query is very simple.




class uniqueNoErrors extends CValidator

{

	/*

	 * Validates the attribute of the object.

	 * If there is any error, the error message is added to the object.

	 * @param CModel $object the object being validated

	 * @param string $attribute the attribute being validated

	*/

	protected function validateAttribute($object,$attribute)

	{

		if(!$object->hasErrors())

		{

			// Ensure the attribute hasn't already been taken

			$result = Yii::app()->db->createCommand()

				->select($attribute)

				->from($object::tableName())

				->where($attribute . '=:test_attr', array(':test_attr' => $object->$attribute))

				->queryScalar();

		

			if($result !== false)

				$this->addError($object, $attribute, '"' . $object->$attribute . '" has already been taken.');

		}

	}

}



NOTE: Ensure you put the validator at the end of your rules or at least after the rules you want to ensure have no errors, otherwise it won’t work.

Hope this helps someone else.

Also a really good wiki was written (not by me) on creating your own validator classes: Create your own Validation Rule