Wrote a small registration form.
Model:
public const SCENARIO_STEP_1 = 1;
public const SCENARIO_STEP_2 = 2;
public const SCENARIO_STEP_3 = 3;
public function scenarios(): array
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_STEP_1] = ['email'];
$scenarios[self::SCENARIO_STEP_2] = ['username', 'password'];
$scenarios[self::SCENARIO_STEP_3] = ['firstname', 'lastname', 'organization'];
return $scenarios;
}
public function rules(): array
{
return [
['step', 'number'],
['username', 'trim'],
['username', 'required'],
[
'username',
'unique',
'targetClass' => Users::class,
'message' => 'This username has already been taken.',
],
['username', 'string', 'min' => 2, 'max' => 255],
['email', 'trim'],
['email', 'required'],
['email', 'email'],
['email', 'string', 'max' => 255],
[
'email',
'unique',
'targetClass' => '\common\entity\Users',
'message' => 'This email address has already been taken.',
],
['password', 'required'],
['password', 'validatePassword'],
[['firstname', 'lastname'], 'required'],
];
}
Controller:
$model = new SignupForm();
$result = [
'status' => 'success',
'cur_step' => 0,
'next_step' => 1,
'final_step' => false,
];
$session = Yii::$app->session;
$session_signup_form = $session->get(self::SIGNUP_FORM);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
switch ($model->step) {
case SignupForm::SCENARIO_STEP_1:
$model->setScenario(SignupForm::SCENARIO_STEP_1);
break;
case SignupForm::SCENARIO_STEP_2:
$model->setScenario(SignupForm::SCENARIO_STEP_2);
break;
case SignupForm::SCENARIO_STEP_3:
$model->setScenario(SignupForm::SCENARIO_STEP_3);
break;
default:
$model->setScenario(SignupForm::SCENARIO_STEP_1);
}
if (Yii::$app->request->get('validate')) {
return ActiveForm::validate($model);
}
if ($model->validate()) {
$session_model = new SignupForm();
if ((int)$model->step === SignupForm::SCENARIO_STEP_1) {
$session->set(self::SIGNUP_FORM, $model->getAttributes());
$result['cur_step'] = SignupForm::SCENARIO_STEP_1;
$result['next_step'] = SignupForm::SCENARIO_STEP_2;
}
if ((int)$model->step === SignupForm::SCENARIO_STEP_2) {
$session_model->setAttributes($session_signup_form, false);
$session_model->step = (int)$model->step;
$session_model->username = $model->username;
$session_model->password = $model->password;
$model->setAttributes($session_signup_form, false);
$session->set(self::SIGNUP_FORM, $session_model->getAttributes());
$result['cur_step'] = SignupForm::SCENARIO_STEP_2;
$result['next_step'] = SignupForm::SCENARIO_STEP_3;
}
if ((int)$model->step === SignupForm::SCENARIO_STEP_3) {
$session_model->setAttributes($session_signup_form);
$session_model->step = (int)$model->step;
$session_model->firstname = $model->firstname;
$session_model->lastname = $model->lastname;
$session_model->organization = $model->organization;
$model->setAttributes($session_model->getAttributes(), false);
$session->set(self::SIGNUP_FORM, $session_model->getAttributes());
$result['cur_step'] = SignupForm::SCENARIO_STEP_3;
$result['final_step'] = true;
if ($model->signup()) {
$session->remove(self::SIGNUP_FORM);
Yii::$app->session->setFlash(
'success',
'Thank you for registration. Please check your inbox for verification email.'
);
}
}
return $result;
}
}
View:
<?php
$form = ActiveForm::begin([
'id' => 'form-signup',
'enableAjaxValidation' => true,
'enableClientValidation' => false,
'validateOnSubmit' => true,
'validateOnChange' => false,
'validateOnBlur' => false,
'validationUrl' => Url::to(['/signup-steps', 'validate' => '1']),
'action' => ['/signup-steps']
]); ?>
<ul id="form-signup-steps" class=" uk-switcher uk-margin">
<li>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'email', [
'inputOptions' => [
'placeholder' => 'Enter email address',
'type' => 'email',
'autocomplete' => 'email',
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-text-center">
<a id="btn-nextstep1" class="uk-button uk-button-text uk-button-large">Next</a>
</div>
</li>
<li>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'username', [
'inputOptions' => [
'placeholder' => 'Enter username',
'type' => 'text',
'autocomplete' => 'username',
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'password', [
'inputOptions' => [
'placeholder' => 'Enter password',
'type' => 'password',
'autocomplete' => 'new-password',
'uk-tooltip' => Yii::t('app', 'At least 8 characters of letters, numbers and special characters'),
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-text-center">
<a id="btn-nextstep2" class="uk-button uk-button-text uk-button-large">Next</a>
</div>
</li>
<li>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'firstname', [
'inputOptions' => [
'placeholder' => 'Enter firstname',
'type' => 'text'
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'lastname', [
'inputOptions' => [
'placeholder' => 'Enter lastname',
'type' => 'text'
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-margin">
<?= $form->field($model, 'organization', [
'inputOptions' => [
'placeholder' => 'Enter organization (optional)',
'type' => 'text'
]
])->label(false) ?>
</div>
<div class="uk-width-1-1 uk-text-center">
<a id="btn-nextstep3" class="uk-button uk-button-primary uk-button-large"><?= Yii::t('app', 'Signup') ?></a>
</div>
<?= $form->field($model, 'step', ['inputOptions' => [
'value' => SignupForm::SCENARIO_STEP_1,
]])->label(false)->hiddenInput() ?>
<div hidden class="uk-width-1-1 uk-text-center">
<button class="uk-button uk-button-primary uk-button-large">Sign up</button>
</div>
<div class="uk-width-1-1 uk-margin uk-text-center">
<p class="uk-text-small uk-margin-remove">By signing up you agree to our <a class="uk-link-border" href="#">terms</a> of service.</p>
</div>
</li>
</ul>
<?php ActiveForm::end(); ?>
JS:
$form.on('afterValidate', function (event, messages, deferreds) {
console.log(event);
console.log(messages);
console.log(deferreds);
switchIconSpin();
}).on('beforeSubmit', function (event, messages, deferreds) {
console.log(event);
console.log(messages);
console.log(deferreds);
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
/** @param {{status: string, cur_step: number, next_step: number, final_step: boolean}} data */
success: function (data) {
console.log(data);
if (data) {
if (data.status && !data.final_step) {
activeCurrentStep(data.next_step)
}
if (data.final_step) {
activeCurrentStep(data.cur_step + 1);
window.location.href = '/login';
}
}
},
error: function (jqXHR, errMsg) {
alert(errMsg);
}
});
return false; // prevent default submit
});
The first step of registration:
The second step of registration:
What happens is the following:
We go through the first step of registration, where the script validates only the mail. Validation is correct, the error message is displayed.
Then we go to the second step of registration, where the script checks login and password. Here validation in the model is correct, but for some reason in JS in the AfterValidate event in deferreds does not return the text of error messages, which should be displayed in the form. Although in the messages parameter, these error messages are present.
How to fix it? I understand that you can handle all this manually, but I would like to use validation out of the box.