input-validation.md 24.9 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 9
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
succeeds 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
```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;
}
```

Behind the scene, the `validate()` method does the following steps to perform validation:
Larry Ullman committed
26

Qiang Xue committed
27 28 29 30 31 32 33 34 35 36 37 38 39
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*.
3. Use each active rule to validate each active attribute associated with that rule. If the rule fails,
   keep an error message for the attribute in the model.


## 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:
40

Qiang Xue committed
41 42 43 44 45 46
```php
public function rules()
{
    return [
        // the name, email, subject and body attributes are required
        [['name', 'email', 'subject', 'body'], 'required'],
47

Qiang Xue committed
48 49 50 51 52
        // the email attribute should be a valid email address
        ['email', 'email'],
    ];
}
```
53

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

Qiang Xue committed
57 58 59
```php
[
    // required, specifies which attributes should be validated by this rule.
Qiang Xue committed
60
    // For a single attribute, you can use the attribute name directly
Qiang Xue committed
61 62
    // without having it in an array instead of an array
    ['attribute1', 'attribute2', ...],
63

Qiang Xue committed
64 65 66 67 68 69
    // 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
70 71
    // You may also configure the "except" option if you want to apply the rule
    // to all scenarios except the listed ones
Qiang Xue committed
72 73 74 75 76 77 78
    'on' => ['scenario1', 'scenario2', ...],

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

Qiang Xue committed
79 80 81 82 83 84 85
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
86
* a fully qualified validator class name. Please refer to the [Standalone Validators](#standalone-validators)
Qiang Xue committed
87
  subsection for more details.
Qiang Xue committed
88

Qiang Xue committed
89 90 91
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
92 93 94 95 96 97 98 99

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

1. Determine which attributes should be validated by checking the current [[yii\base\Model::scenario|scenario]]
   against the scenarios declared in [[yii\base\Model::scenarios()]]. These attributes are the active attributes.
2. Determine which rules should be applied by checking the current [[yii\base\Model::scenario|scenario]]
   against the rules declared in [[yii\base\Model::rules()]]. These rules are the active rules.
3. Use each active rule to validate each active attribute which is associated with the rule.
100
   The validation rules are evaluated in the order they are listed.
Qiang Xue committed
101 102

According to the above validation steps, an attribute will be validated if and only if it is
Qiang Xue committed
103
an active attribute declared in `scenarios()` and is associated with one or multiple active rules
Qiang Xue committed
104 105
declared in `rules()`.

Qiang Xue committed
106

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

Qiang Xue committed
109 110
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
111
a message "Username cannot be blank." to a model when the `username` attribute fails the rule using this validator.
Qiang Xue committed
112

Qiang Xue committed
113 114
You can customize the error message of a rule by specifying the `message` property when declaring the rule,
like the following,
Qiang Xue committed
115 116 117 118 119

```php
public function rules()
{
    return [
Qiang Xue committed
120
        ['username', 'required', 'message' => 'Please choose a username.'],
Qiang Xue committed
121 122 123 124
    ];
}
```

Qiang Xue committed
125 126 127 128 129
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
130 131


132 133 134 135 136 137 138 139 140 141 142 143 144 145
### 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
146
### Conditional Validation <a name="conditional-validation"></a>
Qiang Xue committed
147

Qiang Xue committed
148 149 150
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
151

Qiang Xue committed
152 153 154 155 156 157 158
```php
[
    ['state', 'required', 'when' => function($model) {
        return $model->country == 'USA';
    }],
]
```
Qiang Xue committed
159

Qiang Xue committed
160
The [[yii\validators\Validator::when|when]] property takes a PHP callable with the following signature:
Qiang Xue committed
161 162

```php
Qiang Xue committed
163 164 165 166 167 168
/**
 * @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
169 170
```

Qiang Xue committed
171 172 173
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
174 175

```php
Qiang Xue committed
176 177 178 179
[
    ['state', 'required', 'when' => function ($model) {
        return $model->country == 'USA';
    }, 'whenClient' => "function (attribute, value) {
Kshitiz Singh committed
180
        return $('#country').val() == 'USA';
Qiang Xue committed
181 182
    }"],
]
Qiang Xue committed
183 184 185
```


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

Qiang Xue committed
188
User inputs often need to be filtered or preprocessed. For example, you may want to trim the spaces around the
Qiang Xue committed
189 190 191 192
`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
193

Qiang Xue committed
194 195
```php
[
Qiang Xue committed
196 197
    [['username', 'email'], 'trim'],
    [['username', 'email'], 'default'],
Qiang Xue committed
198 199
]
```
Qiang Xue committed
200

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

Qiang Xue committed
204 205
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
206

Qiang Xue committed
207

Qiang Xue committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
### 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
241
## Ad Hoc Validation <a name="ad-hoc-validation"></a>
Qiang Xue committed
242

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

Qiang Xue committed
245 246
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:
247 248 249 250

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

Qiang Xue committed
252
if ($validator->validate($email, $error)) {
253
    echo 'Email is valid.';
254
} else {
255
    echo $error;
256 257
}
```
Alexander Makarov committed
258

Qiang Xue committed
259 260
> Note: Not all validators support such kind of validation. An example is the [unique](tutorial-core-validators.md#unique)
  core validator which is designed to work with a model only.
Qiang Xue committed
261

Qiang Xue committed
262 263
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
264 265 266 267 268

```php
public function actionSearch($name, $email)
{
    $model = DynamicModel::validateData(compact('name', 'email'), [
Ryadnov Andrey committed
269
        [['name', 'email'], 'string', 'max' => 128],
Qiang Xue committed
270 271
        ['email', 'email'],
    ]);
Qiang Xue committed
272

Qiang Xue committed
273 274 275 276 277 278 279 280
    if ($model->hasErrors()) {
        // validation fails
    } else {
        // validation succeeds
    }
}
```

Qiang Xue committed
281 282 283 284 285
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
286

Qiang Xue committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
```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
    }
}
```

After validation, you can check if the validation succeeds or not by calling the
[[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
306 307 308
You may also access the dynamic attributes defined through the model instance, e.g.,
`$model->name` and `$model->email`.

Qiang Xue committed
309 310 311 312 313 314 315 316 317 318 319

## 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
320 321

```php
Qiang Xue committed
322 323 324 325
/**
 * @param string $attribute the attribute currently being validated
 * @param array $params the additional name-value pairs given in the rule
 */
Kevin LEVRON committed
326
function ($attribute, $params)
Qiang Xue committed
327 328
```

Qiang Xue committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
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
357
    public function validateCountry($attribute, $params)
Qiang Xue committed
358 359 360 361 362 363 364 365
    {
        if (!in_array($this->$attribute, ['USA', 'Web'])) {
            $this->addError($attribute, 'The country must be either "USA" or "Web".');
        }
    }
}
```

Qiang Xue committed
366 367
> 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
368 369
  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
370
>
Kevin LEVRON committed
371
> ```php
Qiang Xue committed
372 373 374 375
> [
>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],
> ]
> ```
Qiang Xue committed
376

Qiang Xue committed
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394

### 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
395
            $this->addError($model, $attribute, 'The country must be either "USA" or "Web".');
Qiang Xue committed
396 397 398 399 400 401 402 403 404 405 406
        }
    }
}
```

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()`.


407 408 409 410 411 412
## 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
it allows users to find out input errors faster and thus provides better user experience. You may use or implement
a validator that supports client-side validation *in addition to* server-side validation.

413
> Info: While client-side validation is desirable, it is not a must. Its main purpose is to provide users better
414 415 416 417 418 419 420 421 422
  experience. Like 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()]], like
  described in the previous subsections.


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

Many [core validators](tutorial-core-validators.md) support client-side validation out-of-box. All you need to do
is just to use [[yii\widgets\ActiveForm]] to build your HTML forms. For example, `LoginForm` below declares two
Qiang Xue committed
423
rules: one uses the [required](tutorial-core-validators.md#required) core validator which is supported on both
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 467 468 469 470 471 472 473 474 475
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
476 477 478
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]]
479 480 481 482 483 484 485 486 487 488 489 490 491
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
492
- `deferred`: an array which deferred objects can be pushed into (explained in the next subsection).
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

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());
522
        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
523 524 525 526 527 528 529 530 531 532
        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
533 534 535
> 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
536
> ```php
Qiang Xue committed
537 538 539 540
> [
>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],
> ]
> ```
541

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

Qiang Xue committed
544 545
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
546

Qiang Xue committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
```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
565 566 567 568 569

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
Qiang Xue committed
570 571 572 573 574 575 576
        var def = $.Deferred();
        var img = new Image();
        img.onload = function() {
            if (this.width > 150) {
                messages.push('Image too wide!!');
            }
            def.resolve();
Alex-Code committed
577
        }
Qiang Xue committed
578 579 580 581 582
        var reader = new FileReader();
        reader.onloadend = function() {
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
Alex-Code committed
583

Qiang Xue committed
584
        deferred.push(def);
Alex-Code committed
585 586 587 588
JS;
}
```

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

Qiang Xue committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
For simplicity, the `deferred` array is equipped with a shortcut method `add()` which automatically creates a Deferred
object and add it to the `deferred` array. Using this method, you can simplify the above example as follows,

```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
615 616
```

617

Qiang Xue committed
618 619 620 621 622 623
### 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.
624

Qiang Xue committed
625
To enable AJAX validation for the whole form, you have to set the
Alexander Makarov committed
626 627 628 629 630 631 632 633 634 635 636
[[yii\widgets\ActiveForm::enableAjaxValidation]] property to be `true` and specify `id` to be unique form identifier:

```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.
637

Qiang Xue committed
638 639
You also need to prepare the server so that it can handle the AJAX validation requests.
This can be achieved by a code snippet like the following in controller actions:
640 641 642 643 644 645 646 647

```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
648
The above code will check whether the current request is an AJAX. If yes, it will respond to
649
this request by running the validation and returning the errors in JSON format.
Qiang Xue committed
650 651 652

> 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.