Search code examples
phpyiiyii-components

Why CFormModel::validate needs in tableName?


I have ChangeEmailForm extending CFormModel:

<?php

/**
 * LoginForm class.
 * LoginForm is the data structure for keeping
 * user login form data. It is used by the 'login' action of 'SiteController'.
 */
class ChangeEmailForm extends CFormModel
{
    public $newemail;
    public $password;

    /**
     * Declares the validation rules.
     * The rules state that username and password are required,
     * and password needs to be authenticated.
     */
    public function rules()
    {
        return array(
            array('newemail, password', 'required'),
            array('newemail', 'email'),
            array('newemail', 'unique', 'attributeName' => 'User.email'),
            array('password', 'authenticate')
        );
    }

    /**
     * Declares attribute labels.
     */
    public function attributeLabels()
    {
        return array(
            'newemail' => 'Новый Email-адрес',
            'password' => 'Пароль учетной записи'
        );
    }

    /**
     * Authenticates the password.
     * This is the 'authenticate' validator as declared in rules().
     */
    public function authenticate($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = User::model()->findByAttributes(array(
                'password' => $this->password,
                'id' => Yii::app()->user->id
            ));
            if ($user === null)
                $this->addError('password', 'Неверный пароль учетной записи.');
        }
    }
}

Also I have the following action in ProfileController:

public function actionSettings()
{
    $profile = $this->loadModel(Yii::app()->user->id);
    $model = new ChangeEmailForm;

    // if it is ajax validation request
    if(isset($_POST['ajax']) && $_POST['ajax']==='change-email-form')
    {
      echo CActiveForm::validate($model);
      Yii::app()->end();
    }

    // collect user input data
    if(isset($_POST['ChangeEmailForm']))
    {
      $model->attributes=$_POST['ChangeEmailForm'];
                        // validate user input and redirect to the previous page if valid
                        if($model->validate()) {
        $this->redirect('/');
      }

    }

    $this->render('settings',array(
      'model'=>$model,
      'profile'=>$profile,
    ));
}

And view:

<?php
/* @var $this ProfileController */
/* @var $model ChangeEmailForm */
/* @var $profile User */
/* @var $form CActiveForm */

$this->breadcrumbs=array(
    'Учетная запись',
);
?>

<h1>Настройки учетной записи</h1>

<h2>Смена email-адреса</h2>
<div class="form-register">

    <?php $form=$this->beginWidget('CActiveForm', array(
        'id'=>'change-email-form',
        'enableAjaxValidation'=>true,
    )); ?>

    <div class="row">
        <div class="col col-label"><?php echo $form->labelEx($model,'newemail', array('class'=>'label1')); ?></div>
        <div class="col col-input"><?php echo $form->textField($model,'newemail',array('size'=>32,'maxlength'=>64, 'class'=>'input1')); ?></div>
        <div class="col col-error"><?php echo $form->error($model,'newemail'); ?></div>
    </div>

    <div class="row">
        <div class="col col-label"><?php echo $form->labelEx($model,'password', array('class'=>'label1')); ?></div>
        <div class="col col-input"><?php echo $form->passwordField($model,'password',array('size'=>32,'maxlength'=>128, 'class'=>'input1')); ?></div>
        <div class="col col-error"><?php echo $form->error($model,'password'); ?></div>
    </div>

    <div class="row buttons">
        <div class="col col-label"></div>
        <div class="col col-input"><?php echo CHtml::submitButton('Сменить пароль', array('class'=>'submit1')); ?></div>
    </div>

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

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

Problem: after submitting the form my application throws an exception: ChangeEmailForm and its behaviors do not have a method or closure named "tableName".

Question: why does CFormModel throw this exception? Why does everything work in example of LoginForm in SiteController?

P.S. Sorry, but I'am a beginner in Yii.


Solution

  • You have the following rules() method in your ChangeEmailForm:

    public function rules()
    {
        return array(
            array('newemail, password', 'required'),
            array('newemail', 'email'),
            array('newemail', 'unique', 'attributeName'=>'User.email'),
            array('password', 'authenticate'),
        );
    }
    

    As one can see unique validator used for newemail attribute can be applied only to CActiveRecord linked with some database table through tableName() but not to CFormModel:

    Validates that the attribute value is unique in the corresponding database table.

    Instead you can write a custom validation method for your form model to test whether entered email already exists in database table represented by User model.