How can i skip unique field if i do not update it in Yii2?

Hello,
I have filed course_order and i need to check it for unique. If not - show message, else - do update. But if i do not update this field in action update i need to do update others field.
I have these rules:

And it works but i do not see any messages…

[[‘course_order’], ‘integer’, ‘min’ => 1],
[
[‘course_order’],
‘unique’,
‘when’ => function ($model, $attribute) {
return $model->{$attribute} !== $model->getOldAttribute($attribute);
},
‘on’ => ‘update’
]

For validate i use:

<?php $form = ActiveForm::begin([
    'enableAjaxValidation' => true,
    'validationUrl' => 'validate',
    'options' => [
        'data-pjax' => true,
    ]
]); ?>

And validation:

public function actionValidate() {
    $model = new InfCourses();

    if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
        Yii::$app->response->format = Response::FORMAT_JSON;
        return ActiveForm::validate($model);
    }
}

Why?

You are creating a new instance of InfCourses in your actionValidate(), but the model should be loaded from the db if you want to update the existing data.

How can i do it in my validation?

I’m not very sure because I don’t understand your business logic regarding the uniqueness of ‘course_order’, but I’m afraid that you are making things unnecessarily complicated.

Usually we don’t need to worry about the ‘update’ scenario when we use ‘unique’ validator. It is clever enough to prevent firing error when the value is not changed in ‘update’ scenario. I mean that your “when” trick is not necessary.

If i add to model just: [['course_order'],'unique'] then start edit course and i have message that course_order is already exists… But i do not change it.
You sad about edit my validator, how?

I think a simple rule is just OK.

[[‘course_order’], ‘integer’, ‘min’ => 1],
[[‘course_order’], 'unique’],

No “when” trick nor “on” limitation needed.

The form also could be simple:

<?php $form = ActiveForm::begin([
        'enableAjaxValidation' => true,
        // 'validationUrl' => 'validate',
        ...
       ]); ?>

You don’t need a dedicated “actionValidate”.
You could simply use the standard “actionUpdate”.

public function actionUpdate($id)
{
    $model = $this->findModel($id); // loads the existing InfCourses model

    if ($model->load(Yii::$app->request->post())) {
        if (Yii::$app->request->isAjax) {
            Yii::$app->response->format = Response::FORMAT_JSON;
            return ActiveForm::validate($model);
        }
        if ($model->save()) {
            return $this->redirect(['index']);
        }
    }
    return $this->render('update', [
        'model' => $model,
    ]);
}

The key point is that you have to load the existing model to validate in ‘update’ scenario. In your ‘actionValidate’ the model is newly created and the validator will work in ‘insert’ scenario, not in ‘update’.

2 Likes

Or in other words, Active-Record is not necessarily an improvement, it can also get in your way, depending on your way of thinking and software career.
In Yii, if you speak SQL, you might directly work one level deeper:
https://www.yiiframework.com/doc/guide/2.0/en/db-dao