Как сделать ajax валидацию для нескольких форм в одном контролере?

Добрый вечер.
На главной странице сайта расположены несколько форм. Все они используют одну модель, но набор данных в каждой форме свой.
Каждая отдельная форма использует свой сценарий валидации, поэтому для каждой формы создана отдельная модель, которая наследуется от основной модели. Обрабатываются формы в одном контроллере, для каждой формы отдельное действие.
Сейчас это реализовано так(покажу на примере одной из четырёх форм):
Модель
class CallbackForm extends Proposal
{
    const SCENARIO_CALLBACK_FORM = 'CallbackForm';

    public function rules()
    {
        return ArrayHelper::merge(parent::rules(), [
            [['name', 'phone'], 'required', 'on' => self::SCENARIO_CALLBACK_FORM],
            [
                'name', 'match', 'pattern' => '#^[а-яё ]+$#iu',
                'message' => '«{attribute}» может содержать А-ЯЁ', 'on' => self::SCENARIO_CALLBACK_FORM
            ],
            [
                'phone', 'match', 'pattern' => '#\+7 \(\d{3}\) \d{3}-\d{2}-\d{2}#',
                'message' => '«{attribute}» должен быть в формате +7(222) 333-44-55', 'on' => self::SCENARIO_CALLBACK_FORM
            ]
        ]);
    }

    public function scenarios()
    {
        $scenarios = parent::scenarios();
        $scenarios[self::SCENARIO_CALLBACK_FORM] = ['name', 'phone'];
        return $scenarios;
    }
}
Действие в контролере
public function actionCallback()
    {

            $model = new CallbackForm();

            $model->scenario = CallbackForm::SCENARIO_CALLBACK_FORM;

            if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())){
                if($model->save()){

                    $model->sendNoteEmails($model);

                    $answer = ['success' => true, 'title' => 'Ваша заявка принята.', 'message' => 'SUCCESS'];
                }
                else{
                    $message = '';
                    foreach($model->getErrors() as $value){
                        $message .= $value[0].'<br>';
                    }
                    $answer = ['validation' => true, 'title' => 'Произошла ошибка.', 'message' => $message];
                }
                return $this->asJson($answer);
            }
    }
Действие для ajax валидации
public function actionAjaxValidate()
    {
        if(Yii::$app->request->isAjax){

            $model = new CallbackForm();
            $model->scenario = CallbackForm::SCENARIO_CALLBACK_FORM;
            if($model->load(Yii::$app->request->post())){
                Yii::$app->response->format = Response::FORMAT_JSON;
                return ActiveForm::validate($model);
            }
        }
    }
Форма
<?php
        $form = ActiveForm::begin([
                    'id' => 'Callback',
                    'action' => ['/proposal/default/callback'],
                    'validationUrl' => Url::toRoute(['/proposal/default/ajax-validate']),
                    'enableAjaxValidation' => true,
                ]);
      ?>
            <?= $form->field($callback, 'name')->textInput(['class' => 'b-calcInput', 'placeholder' => 'Иван Иванов'])->label(false) ?>
      
            <?= $form->field($callback, 'phone')->textInput(['class' => 'b-calcInput j-maskTel', 'placeholder' => '+7 (000) 000 00 00'])->label(false) ?>
      
        <?= Html::submitButton('Перезвоните мне', ['class' => 'b-btn b-calcBtn',]) ?>

      <?php
        ActiveForm::end();
      ?>
JS для ajax запроса
spoiler
$('form').on('beforeSubmit', function(e){
console.log('beforeSubmit')

var form = $(this)

console.log(form.serialize())

$.ajax({

type: form.attr('method'),
url: form.attr('action'),
data: form.serialize()

}).done(function(data){
if(data.success){
console.log(data)
ajaxAnswer(data)
}
else if(data.validation){
console.log('validate')
ajaxAnswer(data)
}
else{

}
}).fail(function(){
console.log('fail')
})

return false;
})

Для меня представляет сложность решить проблему, как обработать ajax валидацию для других форм? Сейчас в actionAjaxValidate можно обработать только одну форму. Как можно изменить действие, чтобы в нём обрабатывались другие формы, то есть, использовать другие модели и другие сценарии?

Пробовал ajax валидацию писать в действии для определённой формы, но тогда у меня получалась двойная отправка формы и запись шла в базу два раза.
Выглядело это так
public function actionCallback()
    {
            $model = new CallbackForm();

            $model->scenario = CallbackForm::SCENARIO_CALLBACK_FORM;

                if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())){
                    if($model->save()){

                        $model->sendNoteEmails($model);

                        $answer = ['type' => 'success', 'title' => 'Ваша заявка принята.', 'message' => 'SUCCESS'];
                        return json_encode($answer);
                    }
                    else{
                        Yii::$app->response->format = Response::FORMAT_JSON;
                        return ActiveForm::validate($model);
                    }
                }
    }

Была мысль для каждой формы сделать отдельный контролер, но это, как я думаю, полнейший бред.

В общем, как обрабатывать все формы в одном действии actionAjaxValidate?
  • Вопрос задан
  • 317 просмотров
Пригласить эксперта
Ответы на вопрос 1
andrew72ru
@andrew72ru
системный администратор, веб-разработчик
как обрабатывать все формы в одном действии actionAjaxValidate?

Если формы реализуют разные модели, то как их обработать в одном действии контроллера? Никак, очевидно же.

В целом – для формы можно указать validationUrl и по этому адресу реализовать действие, которое будет возвращать результат ActiveForm::validate(), убрать кастомный js и поставить validateOnSubmit | OnChange | OnBlur.
Притом в этом действии не надо ничего сохранять, надо запустить валидацию модели и вернуть результат.
Yii перед отправкой формы будет обращаться (Ajax-ом) к тому адресу и действовать дальше в зависимости от того, что пришло.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы