Yii2. Валидация формы. Часть 2

11.03.2019

Теги: PHPPOSTWeb-разработкаYii2ФормаФреймворк

Теперь, когда мы задали правила валидации полей формы, настало время их проверять при отправке данных пользователем. В этом нам поможет метод модели validate(). Посмотрим, как можно получить массив сообщений об ошибках, сохранить данные в сессии и выполнить редирект.

Простой вариант

Класс модели:

<?php
namespace app\models;
use yii\base\Model;

/**
 * Модель для формы обратной связи
 */
class FeedbackForm extends Model
{
    public $name, $email, $body;

    public function attributeLabels() {
        return [
            'name' => 'Ваше имя',
            'email' => 'Ваш email',
            'body' => 'Сообщение',
        ];
    }

    public function rules() {
        return [
            // удалить пробелы для полей name и email
            [['name', 'email'], 'trim'],
            // поле name обязательно для заполнения
            ['name', 'required', 'message' => 'Поле «Ваше имя» обязательно для заполнения'],
            // поле email обязательно для заполнения
            ['email', 'required', 'message' => 'Поле «Ваш email» обязательно для заполнения'],
            // поле email должно быть корректным адресом почты
            ['email', 'email', 'message' => 'Поле «Ваш email» должно быть адресом почты'],
            /*
             * Поле формы обязательно должно проходить валидацию, даже если оно не обязательное.
             * В противном случае, поле не пройдет валидацию, независимо от того, пустое оно или
             * нет. Здесь мы просто говорим, что поле безопасное (safe). Можно было вместо safe
             * использовать trim или default — главное, чтобы хоть какой-нибудь валидатор был
             * использован.
             */
            ['body', 'safe']
        ];
    }
}

Класс контроллера:

<?php
namespace app\controllers;

use Yii;
use yii\web\Controller;
use app\models\FeedbackForm;

class PageController extends Controller {
    public function actionIndex() {
        return $this->render('index');
    }

    public function actionFeedback() {
        $model = new FeedbackForm();
        // если пришли post-данные, загружаем их в модель...
        if ($model->load(Yii::$app->request->post())) {
            // ...и проверяем эти данные
            if ($model->validate()) {
                // данные прошли валидацию, отмечаем этот факт
                Yii::$app->session->setFlash(
                    'validate',
                    true
                );
                // перезагружаем страницу, чтобы избежать повторной отправки формы
                return $this->refresh();
            } else {
                // данные не прошли валидацию, отмечаем этот факт
                Yii::$app->session->setFlash(
                    'validate',
                    false
                );
                // не перезагружаем страницу, чтобы сохранить пользовательские данные
            }
        }
        return $this->render('feedback', ['model' => $model]);
    }
}

Шаблон формы:

<?php
/* @var $this yii\web\View */

use yii\widgets\ActiveForm;
use yii\helpers\Html;

$this->title = 'Обратная связь';
?>

<?php if (Yii::$app->session->hasFlash('validate')): ?>
    <?php if (Yii::$app->session->getFlash('validate')): ?>
        <p>Данные формы прошли валидацию</p>
    <?php else: ?>
        <p>Данные формы не прошли валидацию</p>
    <?php endif; ?>
<?php endif; ?>

<div class="site-feedback">
    <h1><?= Html::encode($this->title) ?></h1>

    <?php $form = ActiveForm::begin(['id' => 'feedback-form', 'options' => ['novalidate' => '']]); ?>
        <?= $form->field($model, 'name')->textInput(); ?>
        <?= $form->field($model, 'email')->textInput(); ?>
        <?= $form->field($model, 'body')->textarea(['rows' => 5]); ?>
        <?= Html::submitButton('Отправить', ['class' => 'btn btn-primary']); ?>
    <?php ActiveForm::end(); ?>
</div>
Чтобы ошибки валидации на стороне клиента не мешали тестированию, есть смысл отключить в браузере поддержку JavaScript.

Вывести в шаблоне все сообщения об ошибках можно с помощью метода errorSummary() класса Html или класса ActiveForm:

<div class="alert alert-warning alert-dismissible" role="alert">
    <?= Html::errorSummary($model); ?>
</div>
<?php $form = ActiveForm::begin(); ?>
    <?= $form->errorSummary($model); ?>
    ..........
    <?= Html::submitButton('Отправить'); ?>
<?php ActiveForm::end(); ?>

Вариант посложнее

Класс модели:

<?php
namespace app\models;
use yii\base\Model;

/**
 * Модель для формы обратной связи
 */
class FeedbackForm extends Model
{
    public $name, $email, $body;

    public function attributeLabels() {
        return [
            'name' => 'Ваше имя',
            'email' => 'Ваш email',
            'body' => 'Сообщение',
        ];
    }

    public function rules() {
        return [
            // удалить пробелы для полей name и email
            [['name', 'email'], 'trim'],
            // поле name обязательно для заполнения
            ['name', 'required', 'message' => 'Поле «Ваше имя» обязательно для заполнения'],
            // поле email обязательно для заполнения
            ['email', 'required', 'message' => 'Поле «Ваш email» обязательно для заполнения'],
            // поле email должно быть корректным адресом почты
            ['email', 'email', 'message' => 'Поле «Ваш email» должно быть адресом почты'],
            // поле body не проверяем
            ['body', 'safe'],
        ];
    }
}

Класс контроллера:

<?php
namespace app\controllers;

use Yii;
use yii\web\Controller;
use app\models\FeedbackForm;

class PageController extends Controller {
    public function actionIndex() {
        return $this->render('index');
    }

    public function actionFeedback() {
        $model = new FeedbackForm();
        // если пришли post-данные, загружаем их в модель...
        if ($model->load(Yii::$app->request->post())) {
            // ...и проверяем эти данные
            if ( ! $model->validate()) {
                // данные не прошли валидацию, отмечаем этот факт
                Yii::$app->session->setFlash(
                    'validate',
                    false
                );
                // сохраняем в сессии введенные пользователем данные
                Yii::$app->session->setFlash(
                    'form-data',
                    [
                        'name' => $model->name,
                        'email' => $model->email,
                        'body' => $model->body
                    ]
                );
                /* 
                 * Сохраняем в сессии массив сообщений об ошибках. Массив имеет вид
                 * [
                 *     'name' => [
                 *         'Поле «Ваше имя» обязательно для заполнения',
                 *     ],
                 *     'email' => [
                 *         'Поле «Ваш email» обязательно для заполнения',
                 *         'Поле «Ваш email» должно быть адресом почты'
                 *     ]
                 * ]
                 */
                Yii::$app->session->setFlash(
                    'form-errors',
                    $model->getErrors()
                );
            } else {
                // данные прошли валидацию, отмечаем этот факт
                Yii::$app->session->setFlash(
                    'validate',
                    true
                );
            }
            // выполняем редирект, чтобы избежать повторной отправки формы
            return $this->refresh();
        }
        return $this->render('feedback', ['model' => $model]);
    }
}

Шаблон формы:

<?php
/* @var $this yii\web\View */

use yii\widgets\ActiveForm;
use yii\helpers\Html;

$this->title = 'Обратная связь';

// Если форма не прошла проверку на сервере, мы должны снова показать форму,
// заполненную введенными ранее данными, не заставляя вводить их повторно
$data = [
    'name' => '',
    'email' => '',
    'body' => '',
];
?>

<?php if (Yii::$app->session->hasFlash('validate')): /* данные формы были отправлены? */ ?>
    <?php if ( ! Yii::$app->session->getFlash('validate')): /* форма не прошла валидацию */ ?>
        <?php
        // Если данные не прошли валидацию, получаем из сессии сохраненные данные, чтобы
        // заполнить ими поля формы, не заставляя пользователя заполнять форму повторно
        if (Yii::$app->session->hasFlash('form-data')) {
            $data['name'] = Html::encode(Yii::$app->session->getFlash('form-data')['name']);
            $data['email'] = Html::encode(Yii::$app->session->getFlash('form-data')['email']);
            $data['body'] = Html::encode(Yii::$app->session->getFlash('form-data')['body']);
        }
        ?>
        <div class="alert alert-warning alert-dismissible" role="alert">
            <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть">
                <span aria-hidden="true">&times;</span>
            </button>
            <p>При заполнении формы допущены ошибки</p>
            <?php if (Yii::$app->session->hasFlash('form-errors')): /* ошибки */ ?>
                <?php $allErrors = Yii::$app->session->getFlash('form-errors'); ?>
                <ul>
                <?php foreach ($allErrors as $errors): ?>
                    <?php foreach ($errors as $error): ?>
                        <li><?= $error; ?></li>
                    <?php endforeach; ?>
                <?php endforeach; ?>
                </ul>
            <?php endif; ?>
        </div>
    <?php else: /* форма прошла валидацию */ ?>
        <div class="alert alert-success alert-dismissible" role="alert">
            <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть">
                <span aria-hidden="true">&times;</span>
            </button>
            <p>Данные формы прошли валидацию.</p>
        </div>
    <?php endif; ?>
<?php endif; ?>

<div class="site-feedback">
    <h1><?= Html::encode($this->title) ?></h1>

    <?php $form = ActiveForm::begin(['id' => 'feedback-form', 'options' => ['novalidate' => '']]); ?>
        <?= $form->field($model, 'name')->textInput(['value' => $data['name']]); ?>
        <?= $form->field($model, 'email')->textInput(['value' => $data['email']]); ?>
        <?= $form->field($model, 'body')->textarea(['rows' => 5, 'value' => $data['body']]); ?>
        <?= Html::submitButton('Отправить', ['class' => 'btn btn-primary']); ?>
    <?php ActiveForm::end(); ?>
</div>

Что мы делаем в контроллере:

  1. Если данные формы были отправлены, вызываем метод validate() модели
  2. Записываем в сессию результат проверки данных формы с ключом validate
  3. Если форма не прошла проверку, записываем в сессию массив сообщений об ошибках
  4. Если форма не прошла проверку, записываем в сессию массив значений полей формы
  5. В любом случае, после POST-запроса делаем редирект

Что мы делаем в шаблоне:

  1. Если все поля формы прошли валидацию, просто показываем пользователю сообщение об этом
  2. Если поля формы не прошли валидацию, показываем список ошибок, которые были допущены
  3. Если поля формы не прошли валидацию, заполняем поля формы введенными ранее данными

Поиск: PHP • POST • Web-разработка • Yii2 • Форма • Form • Framework • Фреймворк • Validate • Валидация

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.