input-validation.md 24.3 KB
Newer Older
Qiang Xue committed
1 2
Validating Input
================
Alexander Makarov committed
3

4 5
As a rule of thumb, you should never trust the data received from end users and should always validate it
before putting it to good use.
Qiang Xue committed
6

Qiang Xue committed
7 8
Given a [model](structure-models.md) populated with user inputs, you can validate the inputs by calling the
[[yii\base\Model::validate()]] method. The method will return a boolean value indicating whether the validation
9
succeeded or not. If not, you may get the error messages from the [[yii\base\Model::errors]] property. For example,
Qiang Xue committed
10

Qiang Xue committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
```php
$model = new \app\models\ContactForm;

// populate model attributes with user inputs
$model->attributes = \Yii::$app->request->post('ContactForm');

if ($model->validate()) {
    // all inputs are valid
} else {
    // validation failed: $errors is an array containing error messages
    $errors = $model->errors;
}
```


## Declaring Rules <a name="declaring-rules"></a>

To make `validate()` really work, you should declare validation rules for the attributes you plan to validate.
This should be done by overriding the [[yii\base\Model::rules()]] method. The following example shows how
the validation rules for the `ContactForm` model are declared:
31

Qiang Xue committed
32 33 34 35 36 37
```php
public function rules()
{
    return [
        // the name, email, subject and body attributes are required
        [['name', 'email', 'subject', 'body'], 'required'],
38

Qiang Xue committed
39 40 41 42 43
        // the email attribute should be a valid email address
        ['email', 'email'],
    ];
}
```
44

Qiang Xue committed
45 46
The [[yii\base\Model::rules()|rules()]] method should return an array of rules, each of which is an array
of the following format:
47

Qiang Xue committed
48 49 50
```php
[
    // required, specifies which attributes should be validated by this rule.
Qiang Xue committed
51
    // For a single attribute, you can use the attribute name directly
52
    // without having it in an array
Qiang Xue committed
53
    ['attribute1', 'attribute2', ...],
54

Qiang Xue committed
55 56 57 58 59 60
    // required, specifies the type of this rule.
    // It can be a class name, validator alias, or a validation method name
    'validator',

    // optional, specifies in which scenario(s) this rule should be applied
    // if not given, it means the rule applies to all scenarios
Qiang Xue committed
61 62
    // You may also configure the "except" option if you want to apply the rule
    // to all scenarios except the listed ones
Qiang Xue committed
63 64 65 66 67 68 69
    'on' => ['scenario1', 'scenario2', ...],

    // optional, specifies additional configurations for the validator object
    'property1' => 'value1', 'property2' => 'value2', ...
]
```

Qiang Xue committed
70 71 72 73 74 75 76
For each rule you must specify at least which attributes the rule applies to and what is the type of the rule.
You can specify the rule type in one of the following forms:

* the alias of a core validator, such as `required`, `in`, `date`, etc. Please refer to
  the [Core Validators](tutorial-core-validators.md) for the complete list of core validators.
* the name of a validation method in the model class, or an anonymous function. Please refer to the
  [Inline Validators](#inline-validators) subsection for more details.
Qiang Xue committed
77
* a fully qualified validator class name. Please refer to the [Standalone Validators](#standalone-validators)
Qiang Xue committed
78
  subsection for more details.
Qiang Xue committed
79

Qiang Xue committed
80 81 82
A rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules.
A rule may be applied in certain [scenarios](structure-models.md#scenarios) only by specifying the `on` option.
If you do not specify an `on` option, it means the rule will be applied to all scenarios.
Qiang Xue committed
83 84 85

When the `validate()` method is called, it does the following steps to perform validation:

Qiang Xue committed
86 87 88 89
1. Determine which attributes should be validated by getting the attribute list from [[yii\base\Model::scenarios()]]
   using the current [[yii\base\Model::scenario|scenario]]. These attributes are called *active attributes*.
2. Determine which validation rules should be used by getting the rule list from [[yii\base\Model::rules()]]
   using the current [[yii\base\Model::scenario|scenario]]. These rules are called *active rules*.
Qiang Xue committed
90
3. Use each active rule to validate each active attribute which is associated with the rule.
91
   The validation rules are evaluated in the order they are listed.
Qiang Xue committed
92
   
Qiang Xue committed
93
According to the above validation steps, an attribute will be validated if and only if it is
Qiang Xue committed
94
an active attribute declared in `scenarios()` and is associated with one or multiple active rules
Qiang Xue committed
95 96
declared in `rules()`.

Qiang Xue committed
97

Qiang Xue committed
98
### Customizing Error Messages <a name="customizing-error-messages"></a>
Qiang Xue committed
99

Qiang Xue committed
100 101
Most validators have default error messages that will be added to the model being validated when its attributes
fail the validation. For example, the [[yii\validators\RequiredValidator|required]] validator will add
Qiang Xue committed
102
a message "Username cannot be blank." to a model when the `username` attribute fails the rule using this validator.
Qiang Xue committed
103

Qiang Xue committed
104 105
You can customize the error message of a rule by specifying the `message` property when declaring the rule,
like the following,
Qiang Xue committed
106 107 108 109 110

```php
public function rules()
{
    return [
Qiang Xue committed
111
        ['username', 'required', 'message' => 'Please choose a username.'],
Qiang Xue committed
112 113 114 115
    ];
}
```

Qiang Xue committed
116 117 118 119 120
Some validators may support additional error messages to more precisely describe different causes of
validation failures. For example, the [[yii\validators\NumberValidator|number]] validator supports
[[yii\validators\NumberValidator::tooBig|tooBig]] and [[yii\validators\NumberValidator::tooSmall|tooSmall]]
to describe the validation failure when the value being validated is too big and too small, respectively.
You may configure these error messages like configuring other properties of validators in a validation rule.
Qiang Xue committed
121 122


123 124 125 126 127 128 129 130 131 132 133 134 135 136
### Validation Events <a name="validation-events"></a>

When [[yii\base\Model::validate()]] is called, it will call two methods that you may override to customize
the validation process:

* [[yii\base\Model::beforeValidate()]]: the default implementation will trigger a [[yii\base\Model::EVENT_BEFORE_VALIDATE]]
  event. You may either override this method or respond to this event to do some preprocessing work
  (e.g. normalizing data inputs) before the validation occurs. The method should return a boolean value indicating
  whether the validation should proceed or not.
* [[yii\base\Model::afterValidate()]]: the default implementation will trigger a [[yii\base\Model::EVENT_AFTER_VALIDATE]]
  event. You may either override this method or respond to this event to do some postprocessing work after
  the validation is completed.


Qiang Xue committed
137
### Conditional Validation <a name="conditional-validation"></a>
Qiang Xue committed
138

Qiang Xue committed
139 140 141
To validate attributes only when certain conditions apply, e.g. the validation of one attribute depends
on the value of another attribute you can use the [[yii\validators\Validator::when|when]] property
to define such conditions. For example,
Qiang Xue committed
142

Qiang Xue committed
143 144 145 146 147 148 149
```php
[
    ['state', 'required', 'when' => function($model) {
        return $model->country == 'USA';
    }],
]
```
Qiang Xue committed
150

Qiang Xue committed
151
The [[yii\validators\Validator::when|when]] property takes a PHP callable with the following signature:
Qiang Xue committed
152 153

```php
Qiang Xue committed
154 155 156 157 158 159
/**
 * @param Model $model the model being validated
 * @param string $attribute the attribute being validated
 * @return boolean whether the rule should be applied
 */
function ($model, $attribute)
Qiang Xue committed
160 161
```

Qiang Xue committed
162 163 164
If you also need to support client-side conditional validation, you should configure
the [[yii\validators\Validator::whenClient|whenClient]] property which takes a string representing a JavaScript
function whose return value determines whether to apply the rule or not. For example,
Qiang Xue committed
165 166

```php
Qiang Xue committed
167 168 169 170
[
    ['state', 'required', 'when' => function ($model) {
        return $model->country == 'USA';
    }, 'whenClient' => "function (attribute, value) {
Kshitiz Singh committed
171
        return $('#country').val() == 'USA';
Qiang Xue committed
172 173
    }"],
]
Qiang Xue committed
174 175 176
```


Qiang Xue committed
177
### Data Filtering <a name="data-filtering"></a>
Qiang Xue committed
178

Qiang Xue committed
179
User inputs often need to be filtered or preprocessed. For example, you may want to trim the spaces around the
Qiang Xue committed
180 181 182 183
`username` input. You may use validation rules to achieve this goal.

The following examples shows how to trim the spaces in the inputs and turn empty inputs into nulls by using
the [trim](tutorial-core-validators.md#trim) and [default](tutorial-core-validators.md#default) core validators:
Qiang Xue committed
184

Qiang Xue committed
185 186
```php
[
Qiang Xue committed
187 188
    [['username', 'email'], 'trim'],
    [['username', 'email'], 'default'],
Qiang Xue committed
189 190
]
```
Qiang Xue committed
191

Qiang Xue committed
192 193
You may also use the more general [filter](tutorial-core-validators.md#filter) validator to perform more complex
data filtering.
Qiang Xue committed
194

Qiang Xue committed
195 196
As you can see, these validation rules do not really validate the inputs. Instead, they will process the values
and save them back to the attributes being validated.
Qiang Xue committed
197

Qiang Xue committed
198

Qiang Xue committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
### Handling Empty Inputs <a name="handling-empty-inputs"></a>

When input data are submitted from HTML forms, you often need to assign some default values to the inputs
if they are empty. You can do so by using the [default](tutorial-core-validators.md#default) validator. For example,

```php
[
    // set "username" and "email" as null if they are empty
    [['username', 'email'], 'default'],

    // set "level" to be 1 if it is empty
    ['level', 'default', 'value' => 1],
]
```

By default, an input is considered empty if its value is an empty string, an empty array or a null.
You may customize the default empty detection logic by configuring the the [[yii\validators\Validator::isEmpty]] property
with a PHP callable. For example,

```php
[
    ['agree', 'required', 'isEmpty' => function ($value) {
        return empty($value);
    }],
]
```

> Note: Most validators do not handle empty inputs if their [[yii\base\Validator::skipOnEmpty]] property takes
  the default value true. They will simply be skipped during validation if their associated attributes receive empty
  inputs. Among the [core validators](tutorial-core-validators.md), only the `captcha`, `default`, `filter`,
  `required`, and `trim` validators will handle empty inputs.


Qiang Xue committed
232
## Ad Hoc Validation <a name="ad-hoc-validation"></a>
Qiang Xue committed
233

Qiang Xue committed
234
Sometimes you need to do *ad hoc validation* for values that are not bound to any model.
235

Qiang Xue committed
236 237
If you only need to perform one type of validation (e.g. validating email addresses), you may call
the [[yii\validators\Validator::validate()|validate()]] method of the desired validator, like the following:
238 239 240 241

```php
$email = 'test@example.com';
$validator = new yii\validators\EmailValidator();
Qiang Xue committed
242

Qiang Xue committed
243
if ($validator->validate($email, $error)) {
244
    echo 'Email is valid.';
245
} else {
246
    echo $error;
247 248
}
```
Alexander Makarov committed
249

250
> Note: Not all validators support this type of validation. An example is the [unique](tutorial-core-validators.md#unique)
Qiang Xue committed
251
  core validator which is designed to work with a model only.
Qiang Xue committed
252

Qiang Xue committed
253 254
If you need to perform multiple validations against several values, you can use [[yii\base\DynamicModel]]
which supports declaring both attributes and rules on the fly. Its usage is like the following:
Qiang Xue committed
255 256 257 258 259

```php
public function actionSearch($name, $email)
{
    $model = DynamicModel::validateData(compact('name', 'email'), [
Ryadnov Andrey committed
260
        [['name', 'email'], 'string', 'max' => 128],
Qiang Xue committed
261 262
        ['email', 'email'],
    ]);
Qiang Xue committed
263

Qiang Xue committed
264 265 266 267 268 269 270 271
    if ($model->hasErrors()) {
        // validation fails
    } else {
        // validation succeeds
    }
}
```

Qiang Xue committed
272 273 274 275 276
The [[yii\base\DynamicModel::validateData()]] method creates an instance of `DynamicModel`, defines the attributes
using the given data (`name` and `email` in this example), and then calls [[yii\base\Model::validate()]]
with the given rules.

Alternatively, you may use the following more "classic" syntax to perform ad hoc data validation:
Qiang Xue committed
277

Qiang Xue committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
```php
public function actionSearch($name, $email)
{
    $model = new DynamicModel(compact('name', 'email'));
    $model->addRule(['name', 'email'], 'string', ['max' => 128])
        ->addRule('email', 'email')
        ->validate();

    if ($model->hasErrors()) {
        // validation fails
    } else {
        // validation succeeds
    }
}
```

294
After validation, you can check if the validation succeeded or not by calling the
Qiang Xue committed
295 296
[[yii\base\DynamicModel::hasErrors()|hasErrors()]] method, and then get the validation errors from the
[[yii\base\DynamicModel::errors|errors]] property, like you do with a normal model.
Qiang Xue committed
297 298 299
You may also access the dynamic attributes defined through the model instance, e.g.,
`$model->name` and `$model->email`.

Qiang Xue committed
300 301 302 303 304 305 306 307 308 309 310

## Creating Validators <a name="creating-validators"></a>

Besides using the [core validators](tutorial-core-validators.md) included in the Yii releases, you may also
create your own validators. You may create inline validators or standalone validators.


### Inline Validators <a name="inline-validators"></a>

An inline validator is one defined in terms of a model method or an anonymous function. The signature of
the method/function is:
Qiang Xue committed
311 312

```php
Qiang Xue committed
313 314 315 316
/**
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
Kevin LEVRON committed
317
function ($attribute, $params)
Qiang Xue committed
318 319
```

Qiang Xue committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
If an attribute fails the validation, the method/function should call [[yii\base\Model::addError()]] to save
the error message in the model so that it can be retrieved back later to present to end users.

Below are some examples:

```php
use yii\base\Model;

class MyForm extends Model
{
    public $country;
    public $token;

    public function rules()
    {
        return [
            // an inline validator defined as the model method validateCountry()
            ['country', 'validateCountry'],

            // an inline validator defined as an anonymous function
            ['token', function ($attribute, $params) {
                if (!ctype_alnum($this->$attribute)) {
                    $this->addError($attribute, 'The token must contain letters or digits.');
                }
            }],
        ];
    }

Johnny Theill committed
348
    public function validateCountry($attribute, $params)
Qiang Xue committed
349 350 351 352 353 354 355 356
    {
        if (!in_array($this->$attribute, ['USA', 'Web'])) {
            $this->addError($attribute, 'The country must be either "USA" or "Web".');
        }
    }
}
```

Qiang Xue committed
357 358
> Note: By default, inline validators will not be applied if their associated attributes receive empty inputs
  or if they have already failed some validation rules. If you want to make sure a rule is always applied,
Kevin LEVRON committed
359 360
  you may configure the [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] and/or [[yii\validators\Validator::skipOnError|skipOnError]]
  properties to be false in the rule declarations. For example:
Qiang Xue committed
361
>
Kevin LEVRON committed
362
> ```php
Qiang Xue committed
363 364 365 366
> [
>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],
> ]
> ```
Qiang Xue committed
367

Qiang Xue committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385

### Standalone Validators <a name="standalone-validators"></a>

A standalone validator is a class extending [[yii\validators\Validator]] or its child class. You may implement
its validation logic by overriding the [[yii\validators\Validator::validateAttribute()]] method. If an attribute
fails the validation, call [[yii\base\Model::addError()]] to save the error message in the model, like you do
with [inline validators](#inline-validators). For example,

```php
namespace app\components;

use yii\validators\Validator;

class CountryValidator extends Validator
{
    public function validateAttribute($model, $attribute)
    {
        if (!in_array($model->$attribute, ['USA', 'Web'])) {
Vadim committed
386
            $this->addError($model, $attribute, 'The country must be either "USA" or "Web".');
Qiang Xue committed
387 388 389 390 391 392 393 394 395 396 397
        }
    }
}
```

If you want your validator to support validating a value without a model, you should also override
[[yii\validators\Validator::validate()]]. You may also override [[yii\validators\Validator::validateValue()]]
instead of `validateAttribute()` and `validate()` because by default the latter two methods are implemented
by calling `validateValue()`.


398 399 400
## Client-Side Validation <a name="client-side-validation"></a>

Client-side validation based on JavaScript is desirable when end users provide inputs via HTML forms, because
401
it allows users to find out input errors faster and thus provides a better user experience. You may use or implement
402 403
a validator that supports client-side validation *in addition to* server-side validation.

404 405 406
> Info: While client-side validation is desirable, it is not a must. Its main purpose is to provide users with a better
  experience. Similar to input data coming from end users, you should never trust client-side validation. For this reason,
  you should always perform server-side validation by calling [[yii\base\Model::validate()]], as
407 408 409 410 411
  described in the previous subsections.


### Using Client-Side Validation <a name="using-client-side-validation"></a>

412 413
Many [core validators](tutorial-core-validators.md) support client-side validation out-of-the-box. All you need to do
is just use [[yii\widgets\ActiveForm]] to build your HTML forms. For example, `LoginForm` below declares two
Qiang Xue committed
414
rules: one uses the [required](tutorial-core-validators.md#required) core validator which is supported on both
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
client and server sides; the other uses the `validatePassword` inline validator which is only supported on the server
side.

```php
namespace app\models;

use yii\base\Model;
use app\models\User;

class LoginForm extends Model
{
    public $username;
    public $password;

    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],

            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }

    public function validatePassword()
    {
        $user = User::findByUsername($this->username);

        if (!$user || !$user->validatePassword($this->password)) {
            $this->addError('password', 'Incorrect username or password.');
        }
    }
}
```

The HTML form built by the following code contains two input fields `username` and `password`.
If you submit the form without entering anything, you will find the error messages requiring you
to enter something appear right away without any communication with the server.

```php
<?php $form = yii\widgets\ActiveForm::begin(); ?>
    <?= $form->field($model, 'username') ?>
    <?= $form->field($model, 'password')->passwordInput() ?>
    <?= Html::submitButton('Login') ?>
<?php yii\widgets\ActiveForm::end(); ?>
```

Behind the scene, [[yii\widgets\ActiveForm]] will read the validation rules declared in the model
and generate appropriate JavaScript code for validators that support client-side validation. When a user
changes the value of an input field or submit the form, the client-side validation JavaScript will be triggered.

Qiang Xue committed
467 468 469
If you want to turn off client-side validation completely, you may configure the
[[yii\widgets\ActiveForm::enableClientValidation]] property to be false. You may also turn off client-side
validation of individual input fields by configuring their [[yii\widgets\ActiveField::enableClientValidation]]
470 471 472 473 474 475 476 477 478 479 480 481 482
property to be false.


### Implementing Client-Side Validation <a name="implementing-client-side-validation"></a>

To create a validator that supports client-side validation, you should implement the
[[yii\validators\Validator::clientValidateAttribute()]] method which returns a piece of JavaScript code
that performs the validation on the client side. Within the JavaScript code, you may use the following
predefined variables:

- `attribute`: the name of the attribute being validated.
- `value`: the value being validated.
- `messages`: an array used to hold the validation error messages for the attribute.
Qiang Xue committed
483
- `deferred`: an array which deferred objects can be pushed into (explained in the next subsection).
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512

In the following example, we create a `StatusValidator` which validates if an input is a valid status input
against the existing status data. The validator supports both server side and client side validation.

```php
namespace app\components;

use yii\validators\Validator;
use app\models\Status;

class StatusValidator extends Validator
{
    public function init()
    {
        parent::init();
        $this->message = 'Invalid status input.';
    }

    public function validateAttribute($model, $attribute)
    {
        $value = $model->$attribute;
        if (!Status::find()->where(['id' => $value])->exists()) {
            $model->addError($attribute, $this->message);
        }
    }

    public function clientValidateAttribute($model, $attribute, $view)
    {
        $statuses = json_encode(Status::find()->select('id')->asArray()->column());
513
        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
514 515 516 517 518 519 520 521 522 523
        return <<<JS
if (!$.inArray(value, $statuses)) {
    messages.push($message);
}
JS;
    }
}
```

> Tip: The above code is given mainly to demonstrate how to support client-side validation. In practice,
Qiang Xue committed
524 525 526
> you may use the [in](tutorial-core-validators.md#in) core validator to achieve the same goal. You may
> write the validation rule like the following:
>
Kevin LEVRON committed
527
> ```php
Qiang Xue committed
528 529 530 531
> [
>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],
> ]
> ```
532

Qiang Xue committed
533
### Deferred Validation <a name="deferred-validation"></a>
Alex-Code committed
534

Qiang Xue committed
535 536
If you need to perform asynchronous client-side validation, you can create [Deferred objects](http://api.jquery.com/category/deferred-object/).
For example, to perform a custom AJAX validation, you can use the following code:
Alex-Code committed
537

Qiang Xue committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        deferred.push($.get("/check", {value: value}).done(function(data) {
            if ('' !== data) {
                messages.push(data);
            }
        }));
JS;
}
```

In the above, the `deferred` variable is provided by Yii, which is an array of Deferred objects. The `$.get()`
jQuery method creates a Deferred object which is pushed to the `deferred` array.

You can also explicitly create a Deferred object and call its `resolve()` method when the asynchronous callback
is hit. The following example shows how to validate the dimensions of an uploaded image file on the client side.
Alex-Code committed
556 557 558 559 560

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
Qiang Xue committed
561 562 563 564 565 566 567
        var def = $.Deferred();
        var img = new Image();
        img.onload = function() {
            if (this.width > 150) {
                messages.push('Image too wide!!');
            }
            def.resolve();
Alex-Code committed
568
        }
Qiang Xue committed
569 570 571 572 573
        var reader = new FileReader();
        reader.onloadend = function() {
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
Alex-Code committed
574

Qiang Xue committed
575
        deferred.push(def);
Alex-Code committed
576 577 578 579
JS;
}
```

Qiang Xue committed
580 581
> Note: The `resolve()` method must be called after the attribute has been validated. Otherwise the main form
  validation will not complete.
Alex-Code committed
582

Qiang Xue committed
583
For simplicity, the `deferred` array is equipped with a shortcut method `add()` which automatically creates a Deferred
584
object and adds it to the `deferred` array. Using this method, you can simplify the above example as follows,
Qiang Xue committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        deferred.add(function(def) {
            var img = new Image();
            img.onload = function() {
                if (this.width > 150) {
                    messages.push('Image too wide!!');
                }
                def.resolve();
            }
            var reader = new FileReader();
            reader.onloadend = function() {
                img.src = reader.result;
            }
            reader.readAsDataURL(file);
        });
JS;
}
Alex-Code committed
606 607
```

608

Qiang Xue committed
609 610 611 612 613 614
### AJAX Validation <a name="ajax-validation"></a>

Some validations can only be done on the server side, because only the server has the necessary information.
For example, to validate if a username is unique or not, it is necessary to check the user table on the server side.
You can use AJAX-based validation in this case. It will trigger an AJAX request in the background to validate the
input while keeping the same user experience as the regular client-side validation.
615

Qiang Xue committed
616
To enable AJAX validation for the whole form, you have to set the
617
[[yii\widgets\ActiveForm::enableAjaxValidation]] property to be `true` and specify `id` to be a unique form identifier:
Alexander Makarov committed
618 619 620 621 622 623 624 625 626 627

```php
<?php $form = yii\widgets\ActiveForm::begin([
    'id' => 'contact-form',
    'enableAjaxValidation' => true,
]); ?>
```

You may also turn AJAX validation on or off for individual input fields by configuring their
[[yii\widgets\ActiveField::enableAjaxValidation]] property.
628

Qiang Xue committed
629
You also need to prepare the server so that it can handle the AJAX validation requests.
630
This can be achieved by a code snippet like the following in the controller actions:
631 632 633 634 635 636 637 638

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

Qiang Xue committed
639
The above code will check whether the current request is an AJAX. If yes, it will respond to
640
this request by running the validation and returning the errors in JSON format.
Qiang Xue committed
641 642 643

> Info: You can also use [Deferred Validation](#deferred-validation) to perform AJAX validation.
  However, the AJAX validation feature described here is more systematic and requires less coding effort.