Search code examples
validationyii2unique

yii2 on update action for unique attribute


In users model I have following rule

[['username', 'email'], 'unique'],

What I want to do is, to make this rule available during new user creation, on update (when user entered another username or email to check if it's unique or not). but if I don't change the username in currently user model (in update action) gives error "username is not unique" even if user doesn't edit current username and email and just save filled form (with current values) .

For example, your username is: user1. You opened update action and changed your password only. In this situation my configuration gives user1 is not unique message. another situation is , you change username to : user2 and it's work correctly

Here is actions code:

public function actionMe()
{
    $data["model"] = $model = $this->findModel(Yii::$app->user->id);

    if ($model->load(Yii::$app->request->post())) {
        if (trim($model->password) != "")
            $model->setPassword($model->password);
        if ($model->validate() && $model->save())
            return $this->redirect(['site/index']);
    }
    return $this->render('me', $data);
}

How can I prevent this behaviour?


Solution

  • You can do it in two ways first one is more taken from Yii2 Starter Kit. Here you add these to your rules. And add this function below to the end of your UserForm.

    [ 'username', 'unique', 'targetClass' => User::className(), 'filter' => function ($query) {
                    if (!$this->getModel()->isNewRecord) {
                        $query->andWhere(['not', ['id'=>$this->getModel()->id]]);
                    }
                }],
    
    
    
     /**
         * @return app/models/User
         */
        public function getModel()
        {
            if (!$this->model) {
                $this->model = new User();
            }
            return $this->model;
        }
    

    Second way is just create your own validator and use it on Update Scenario. For example:

    [['username', 'email'], 'myUniqueValidator','on'=>'update'],
    
     public function myUniqueValidator($attribute,$params){
            if(!$this->hasErrors()){
                if(User::find()->where([$attribute=>$this->$attribute])->andWhere(['<>','id',Yii::$app->user->id])){
    
                 }
    
            }
        }
    

    then you should add a scenario to your action.

    public function actionMe()
    {
        $data["model"] = $model = $this->findModel(Yii::$app->user->id);
    
        if ($model->load(Yii::$app->request->post())) {
             $model->setScenario('update');
            if (trim($model->password) != "")
                $model->setPassword($model->password);
            if ($model->validate() && $model->save())
                return $this->redirect(['site/index']);
        }
        return $this->render('me', $data);
    }