input-validation.md 32 KB
Newer Older
1 2
入力を検証する
==============
3

4
大まかに言うなら、エンドユーザから受信したデータは決して信用せず、利用する前に検証しなければならない、ということです。
5

6
[モデル](structure-models.md) にユーザの入力が投入されたら、モデルの [[yii\base\Model::validate()]] メソッドを呼んで入力を検証することが出来ます。
7 8
このメソッドはバリデーションが成功したか否かを示す真偽値を返します。
バリデーションが失敗した場合は、[[yii\base\Model::errors]] プロパティからエラーメッセージを取得することが出来ます。
9
例えば、
10 11 12 13

```php
$model = new \app\models\ContactForm;

14
// モデルの属性にユーザ入力を投入する
15 16 17
$model->attributes = \Yii::$app->request->post('ContactForm');

if ($model->validate()) {
18
    // 全ての入力が有効
19
} else {
20
    // バリデーションが失敗。$errors はエラーメッセージを含む配列
21 22 23 24
    $errors = $model->errors;
}
```

25
## 規則を宣言する <a name="declaring-rules"></a>
26

27
`validate()` を現実に動作させるためには、検証する予定の属性に対してバリデーション規則を宣言しなければなりません。
28
規則は [[yii\base\Model::rules()]] メソッドをオーバーライドすることで宣言します。
29
次の例は、`ContactForm` モデルに対してバリデーション規則を宣言する方法を示すものです。
30 31 32 33 34

```php
public function rules()
{
    return [
35
        // 名前、メールアドレス、主題、本文が必須項目
36 37
        [['name', 'email', 'subject', 'body'], 'required'],

38
        // email 属性は有効なメールアドレスでなければならない
39 40 41 42 43
        ['email', 'email'],
    ];
}
```

44
[[yii\base\Model::rules()|rules()]] メソッドは配列を返すべきものですが、配列の各要素は次の形式の配列でなければなりません。
45 46 47

```php
[
48 49 50 51 52
    // 必須。この規則によって検証されるべき属性を指定する。
    // 属性が一つだけの場合は、配列の中に入れずに、属性の名前を直接に書いてもよい。
    ['属性1', '属性2', ...],

    // 必須。この規則のタイプを指定する。
53
    // クラス名、バリデータのエイリアス、または、バリデーションメソッドの名前。
54 55 56 57 58 59 60 61 62 63
    'バリデータ',

    // オプション。この規則が適用されるべき一つまたは複数のシナリオを指定する。
    // 指定しない場合は、この規則が全てのシナリオに適用されることを意味する。
    // "except" オプションを構成して、列挙したシナリオを除く全てのシナリオに
    // この規則が適用されるべきことを指定することも出来る。
    'on' => ['シナリオ1', 'シナリオ2', ...],

    // オプション。バリデータオブジェクトに対する追加の構成情報を指定する。
    'プロパティ1' => '値1', 'プロパティ2' => '値2', ...
64 65 66
]
```

67 68
各規則について、最低限、規則がどの属性に適用されるか、そして、規則がどのタイプであるかを指定しなければなりません。
規則のタイプは、次に挙げる形式のどれか一つを選ぶことが出来ます。
69

70 71
* コアバリデータのエイリアス。例えば、`required``in``date`、等々。
  コアバリデータの完全なリストは [コアバリデータ](tutorial-core-validators.md) を参照してください。
72
* モデルクラス内のバリデーションメソッドの名前、または無名関数。詳細は、[インラインバリデータ](#inline-validators) の項を参照してください。
73
* 完全修飾のバリデータクラス名。詳細は [スタンドアロンバリデータ](#standalone-validators) の項を参照してください。
74

75 76 77 78
一つの規則は、一つまたは複数の属性を検証するために使用することが出来ます。
そして、一つの属性は、一つまたは複数の規則によって検証され得ます。
`on` オプションを指定することで、規則を特定の [シナリオ](structure-models.md#scenarios) においてのみ適用することが出来ます。
`on` オプションを指定しない場合は、規則が全てのシナリオに適用されることになります。
79

80
`validate()` メソッドが呼ばれると、次のステップを踏んでバリデーションが実行されます。
81

82 83
1. 現在の [[yii\base\Model::scenario|シナリオ]] を使って [[yii\base\Model::scenarios()]] から属性のリストを取得し、どの属性が検証されるべきかを決定します。
   検証されるべき属性が *アクティブな属性* と呼ばれます。
84
2. 現在の [[yii\base\Model::scenario|シナリオ]] を使って [[yii\base\Model::rules()]] から規則のリストを取得し、どのバリデーション規則が使用されるべきかを決定します。
85
   使用されるべき規則が *アクティブな規則* と呼ばれます。
86
3. 全てのアクティブな規則を一つずつ使って、その規則に関連付けられた全てのアクティブな属性を一つずつ検証します。
87
   バリデーション規則はリストに挙げられている順に評価されます。
88

89
属性は、上記のバリデーションのステップに従って、`scenarios()` でアクティブな属性であると宣言されており、かつ、`rules()` で宣言された一つまたは複数のアクティブな規則と関連付けられている場合に、また、その場合に限って、検証されます。
90 91


92
### エラーメッセージをカスタマイズする <a name="customizing-error-messages"></a>
93

94
たいていのバリデータはデフォルトのエラーメッセージを持っていて、属性のバリデーションが失敗した場合にそれを検証の対象であるモデルに追加します。
95
例えば、[[yii\validators\RequiredValidator|required]] バリデータは、このバリデータを使って `username` 属性を検証したとき、規則に合致しない場合は「ユーザ名は空ではいけません。」というエラーメッセージをモデルに追加します。
96

97
規則のエラーメッセージは、次に示すように、規則を宣言するときに `message` プロパティを指定することによってカスタマイズすることが出来ます。
98 99 100 101 102

```php
public function rules()
{
    return [
103
        ['username', 'required', 'message' => 'ユーザ名を選んでください。'],
104 105 106 107
    ];
}
```

108 109 110
バリデータの中には、バリデーションを失敗させたさまざまな原因をより詳しく説明するための追加のエラーメッセージをサポートしているものがあります。
例えば、[[yii\validators\NumberValidator|number]] バリデータは、検証される値が大きすぎたり小さすぎたりしたときにバリデーションの失敗を説明するために、それぞれ、[[yii\validators\NumberValidator::tooBig|tooBig]] および [[yii\validators\NumberValidator::tooSmall|tooSmall]] のメッセージをサポートしています。
これらのエラーメッセージも、バリデータの他のプロパティと同様、バリデーション規則の中で構成することが出来ます。
111 112


113
### バリデーションのイベント <a name="validation-events"></a>
114

115
[[yii\base\Model::validate()]] は、呼び出されると、バリデーションプロセスをカスタマイズするためにオーバーライドできる二つのメソッドを呼び出します。
116

117
* [[yii\base\Model::beforeValidate()]]: 既定の実装は [[yii\base\Model::EVENT_BEFORE_VALIDATE]] イベントをトリガするものです。
118 119
  このメソッドをオーバーライドするか、または、イベントに反応して、バリデーションが実行される前に、何らかの前処理 (例えば入力されたデータの正規化) をすることが出来ます。
  このメソッドは、バリデーションを続行すべきか否かを示す真偽値を返さなくてはなりません。
120
* [[yii\base\Model::afterValidate()]]: 既定の実装は [[yii\base\Model::EVENT_AFTER_VALIDATE]] イベントをトリガするものです。
121
  このメソッドをオーバーライドするか、または、イベントに反応して、バリデーションが完了した後に、何らかの後処理をすることが出来ます。
122 123


124
### 条件付きバリデーション <a name="conditional-validation"></a>
125

126
特定の条件が満たされる場合に限って属性を検証したい場合、例えば、ある属性のバリデーションが他の属性の値に依存する場合には、[[yii\validators\Validator::when|when]] プロパティを使って、そのような条件を定義することが出来ます。
127
例えば、
128 129 130 131 132 133 134 135 136

```php
[
    ['state', 'required', 'when' => function($model) {
        return $model->country == 'USA';
    }],
]
```

137
[[yii\validators\Validator::when|when]] プロパティは、次のシグニチャを持つ PHP コーラブルを値として取ります。
138 139 140

```php
/**
141 142 143
 * @param Model $model 検証されるモデル
 * @param string $attribute 検証される属性
 * @return boolean 規則が適用されるか否か
144 145 146 147
 */
function ($model, $attribute)
```

148
クライアント側でも条件付きバリデーションをサポートする必要がある場合は、[[yii\validators\Validator::whenClient|whenClient]] プロパティを構成しなければなりません。
149 150
このプロパティは、規則を適用すべきか否かを返す JavaScript 関数を表す文字列を値として取ります。
例えば、
151 152 153 154 155 156 157 158 159 160 161 162

```php
[
    ['state', 'required', 'when' => function ($model) {
        return $model->country == 'USA';
    }, 'whenClient' => "function (attribute, value) {
        return $('#country').val() == 'USA';
    }"],
]
```


163
### データのフィルタリング <a name="data-filtering"></a>
164

165 166
ユーザ入力をフィルタまたは前処理する必要があることがよくあります。
例えば、`username` の入力値の前後にある空白を除去したいというような場合です。
167
この目的を達するためにバリデーション規則を使うことが出来ます。
168

169
次の例では、入力値の前後にある空白を除去して、空の入力値を null に変換することを、[trim](tutorial-core-validators.md#trim) および [default](tutorial-core-validators.md#default) のコアバリデータで行っています。
170 171 172 173 174 175 176 177

```php
[
    [['username', 'email'], 'trim'],
    [['username', 'email'], 'default'],
]
```

178
もっと汎用的な [filter](tutorial-core-validators.md#filter) バリデータを使って、もっと複雑なデータフィルタリングをすることも出来ます。
179
お分かりかと思いますが、これらのバリデーション規則は実際には入力を検証しません。そうではなくて、検証される属性の値を処理して書き戻すのです。
180 181


182
### 空の入力値を扱う <a name="handling-empty-inputs"></a>
183

184 185 186
HTML フォームから入力データが送信されたとき、入力値が空である場合には何らかのデフォルト値を割り当てなければならないことがよくあります。
[default](tutorial-core-validators.md#default) バリデータを使ってそうすることが出来ます。
例えば、
187 188 189

```php
[
190
    // 空の時は "username" と "email" を null にする
191 192
    [['username', 'email'], 'default'],

193
    // 空の時は "level" を 1 にする
194 195 196 197
    ['level', 'default', 'value' => 1],
]
```

198 199 200
既定では、入力値が空であると見なされるのは、それが、空文字列であるか、空配列であるか、null であるときです。
空を検知するこのデフォルトのロジックは、[[yii\validators\Validator::isEmpty]] プロパティを PHP コーラブルで構成することによって、カスタマイズすることが出来ます。
例えば、
201 202 203 204 205 206 207 208 209

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

210
> Note|注意: たいていのバリデータは、[[yii\base\Validator::skipOnEmpty]] プロパティがデフォルト値 `true` を取っている場合は、空の入力値を処理しません。
211
  そのようなバリデータは、関連付けられた属性が空の入力値を受け取ったときは、バリデーションの過程ではスキップされるだけになります。
212
  [コアバリデータ](tutorial-core-validators.md) の中では、`captcha``default``filter``required`、そして `trim` だけが空の入力値を処理します。
213 214


215
## アドホックなバリデーション <a name="ad-hoc-validation"></a>
216

217
時として、何らかのモデルに結び付けられていない値に対する *アドホックなバリデーション* を実行しなければならない場合があります。
218

219
実行する必要があるバリデーションが一種類 (例えば、メールアドレスの検証) だけである場合は、使いたいバリデータの [[yii\validators\Validator::validate()|validate()]] メソッドを次のように呼び出すことが出来ます。
220 221 222 223 224 225

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

if ($validator->validate($email, $error)) {
226
    echo 'メールアドレスは有効。';
227 228 229 230 231
} else {
    echo $error;
}
```

232
> Note|注意: 全てのバリデータがこの種のバリデーションをサポートしている訳ではありません。
233
  その一例が [unique](tutorial-core-validators.md#unique) コアバリデータであり、これはモデルとともに使用されることだけを念頭にして設計されています。
234

235
いくつかの値に対して複数のバリデーションを実行する必要がある場合は、属性と規則の両方をその場で宣言することが出来る [[yii\base\DynamicModel]] を使うことが出来ます。
236
これは、次のような使い方をします。
237 238 239 240 241 242 243 244 245 246

```php
public function actionSearch($name, $email)
{
    $model = DynamicModel::validateData(compact('name', 'email'), [
        [['name', 'email'], 'string', 'max' => 128],
        ['email', 'email'],
    ]);

    if ($model->hasErrors()) {
247
        // バリデーションが失敗
248
    } else {
249
        // バリデーションが成功
250 251 252 253
    }
}
```

254
[[yii\base\DynamicModel::validateData()]] メソッドは `DynamicModel` のインスタンスを作成し、与えられた値 (この例では `name``email`) を使って属性を定義し、そして、与えられた規則で [[yii\base\Model::validate()]] を呼び出します。
255

256
別の選択肢として、次のように、もっと「クラシック」な構文を使って、アドホックなデータバリデーションを実行することも出来ます。
257 258 259 260 261 262 263 264 265 266

```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()) {
267
        // バリデーションが失敗
268
    } else {
269
        // バリデーションが成功
270 271 272 273
    }
}
```

274
検証を実行した後は、通常のモデルで行うのと同様に、バリデーションが成功したか否かを [[yii\base\DynamicModel::hasErrors()|hasErrors()]] メソッドを呼んでチェックして、[[yii\base\DynamicModel::errors|errors]] プロパティからバリデーションエラーを取得することが出来ます。
275
また、このモデルのインスタンスによって定義された動的な属性に対しても、例えば `$model->name``$model->email` のようにして、アクセスすることが出来ます。
276 277


278
## バリデータを作成する <a name="creating-validators"></a>
279

280 281
Yii のリリースに含まれている [コアバリデータ](tutorial-core-validators.md) を使う以外に、あなた自身のバリデータを作成することも出来ます。
インラインバリデータとスタンドアロンバリデータを作ることが出来ます。
282 283


284
### インラインバリデータ <a name="inline-validators"></a>
285

286 287
インラインバリデータは、モデルのメソッドまたは無名関数として定義されるバリデータです。
メソッド/関数 のシグニチャは、
288 289 290

```php
/**
291 292
 * @param string $attribute 現在検証されている属性
 * @param array $params 規則に与えられる追加の「名前-値」のペア
293 294 295 296
 */
function ($attribute, $params)
```

297
属性がバリデーションに失敗した場合は、メソッド/関数 は [[yii\base\Model::addError()]] を呼んでエラーメッセージをモデルに保存し、後で読み出してエンドユーザに示ことが出来るようにしなければなりません。
298

299
下記にいくつかの例を示します。
300 301 302 303 304 305 306 307 308 309 310 311

```php
use yii\base\Model;

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

    public function rules()
    {
        return [
312
            // モデルメソッド validateCountry() として定義されるインラインバリデータ
313 314
            ['country', 'validateCountry'],

315
            // 無名関数として定義されるインラインバリデータ
316 317
            ['token', function ($attribute, $params) {
                if (!ctype_alnum($this->$attribute)) {
318
                    $this->addError($attribute, 'トークンは英数字で構成しなければなりません。');
319 320 321 322 323 324 325 326
                }
            }],
        ];
    }

    public function validateCountry($attribute, $params)
    {
        if (!in_array($this->$attribute, ['USA', 'Web'])) {
327
            $this->addError($attribute, '国は "USA" または "Web" でなければなりません。');
328 329 330 331 332
        }
    }
}
```

333
> Note|注意: 既定では、インラインバリデータは、関連付けられている属性が空の入力値を受け取ったり、既に何らかのバリデーション規則に失敗したりしている場合には、適用されません。
334 335
> 規則が常に適用されることを保証したい場合は、規則の宣言において [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] および/または [[yii\validators\Validator::skipOnError|skipOnError]] のプロパティを false に設定することが出来ます。
> 例えば、
336 337 338 339 340 341 342 343
>
> ```php
> [
>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],
> ]
> ```


344
### スタンドアロンバリデータ <a name="standalone-validators"></a>
345

346 347
スタンドアロンバリデータは、[[yii\validators\Validator]] またはその子クラスを拡張するクラスです。
[[yii\validators\Validator::validateAttribute()]] メソッドをオーバーライドすることによって、その検証ロジックを実装することが出来ます。
348
[インラインバリデータ](#inline-validators) でするのと同じように、属性がバリデーションに失敗した場合は、[[yii\base\Model::addError()]] を呼んでエラーメッセージをモデルに保存します。
349
例えば、
350 351 352 353 354 355 356 357 358 359 360

```php
namespace app\components;

use yii\validators\Validator;

class CountryValidator extends Validator
{
    public function validateAttribute($model, $attribute)
    {
        if (!in_array($model->$attribute, ['USA', 'Web'])) {
361
            $this->addError($model, $attribute, '国は "USA" または "Web" でなければなりません。');
362 363 364 365 366
        }
    }
}
```

367

368
あなたのバリデータで、モデル無しの値のバリデーションをサポートしたい場合は、[[yii\validators\Validator::validate()]] もオーバーライドしなければなりません。
369 370
または、`validateAttribute()``validate()` の代りに、[[yii\validators\Validator::validateValue()]] をオーバーライドしても構いません。
と言うのは、前の二つは、デフォルトでは、`validateValue()` を呼び出すことによって実装されているからです。
371 372


373
## クライアント側でのバリデーション <a name="client-side-validation"></a>
374

375 376 377
エンドユーザが HTML フォームで値を入力する際には、JavaScript に基づくクライアント側でのバリデーションを提供することが望まれます。
というのは、クライアント側でのバリデーションは、ユーザが入力のエラーを早く見つけることが出来るようにすることによって、より良いユーザ体験を提供するものだからです。
あなたも、サーバ側でのバリデーション *に加えて* クライアント側でのバリデーションをサポートするバリデータを使用したり実装したりすることが出来ます。
378

379
> Info|情報: クライアント側でのバリデーションは望ましいものですが、不可欠なものではありません。
380
  その主たる目的は、ユーザにより良い体験を提供することにあります。
381 382
  エンドユーザから来る入力値と同じように、クライアント側でのバリデーションを決して信用してはいけません。
  この理由により、これまでの項で説明したように、常に [[yii\base\Model::validate()]] を呼び出してサーバ側でのバリデーションを実行しなければなりません。
383 384


385
### クライアント側でのバリデーションを使う <a name="using-client-side-validation"></a>
386

387
多くの [コアバリデータ](tutorial-core-validators.md) は、そのままで、クライアント側でのバリデーションをサポートしています。
388 389 390 391
あなたがする必要があるのは、[[yii\widgets\ActiveForm]] を使って HTML フォームを作るということだけです。
例えば、下の `LoginForm` は二つの規則を宣言しています。
一つは、[required](tutorial-core-validators.md#required) コアバリデータを使っていますが、これはクライアント側とサーバ側の両方でサポートされています。
もう一つは `validatePassword` インラインバリデータを使っていますが、こちらはサーバ側でのみサポートされています。
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

```php
namespace app\models;

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

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

    public function rules()
    {
        return [
407
            // username と password はともに必須
408 409
            [['username', 'password'], 'required'],

410
            // password は validatePassword() によって検証される
411 412 413 414 415 416 417 418 419
            ['password', 'validatePassword'],
        ];
    }

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

        if (!$user || !$user->validatePassword($this->password)) {
420
            $this->addError('password', 'ユーザ名またはパスワードが違います。');
421 422 423 424 425
        }
    }
}
```

426 427
次のコードによって構築される HTML フォームは、`username``password` の二つの入力フィールドを含みます。
何も入力せずにこのフォームを送信すると、何かを入力するように要求するエラーメッセージが、サーバと少しも交信することなく、ただちに表示されることに気付くでしょう。
428 429 430 431 432

```php
<?php $form = yii\widgets\ActiveForm::begin(); ?>
    <?= $form->field($model, 'username') ?>
    <?= $form->field($model, 'password')->passwordInput() ?>
433
    <?= Html::submitButton('ログイン') ?>
434 435 436
<?php yii\widgets\ActiveForm::end(); ?>
```

437 438
舞台裏では、[[yii\widgets\ActiveForm]] がモデルで宣言されているバリデーション規則を読んで、クライアント側のバリデーションをサポートするバリデータのために適切な JavaScript コードを生成します。
ユーザが入力フィールドの値を変更したりフォームを送信したりすると、クライアント側バリデーションの JavaScript が起動されます。
439

440 441
クライアント側のバリデーションを完全に無効にしたい場合は、[[yii\widgets\ActiveForm::enableClientValidation]] プロパティを false に設定することが出来ます。
また、個々の入力フィールドごとにクライアント側のバリデーションを無効にしたい場合は、入力フィールドの [[yii\widgets\ActiveField::enableClientValidation]] プロパティを false に設定することも出来ます。
442 443


444
### クライアント側バリデーションを実装する <a name="implementing-client-side-validation"></a>
445

446
クライアント側バリデーションをサポートするバリデータを作成するためには、クライアント側でのバリデーションを実行する JavaScript コードを返す [[yii\validators\Validator::clientValidateAttribute()]] メソッドを実装しなければなりません。
447
その JavaScript の中では、次の事前定義された変数を使用することが出来ます。
448

449 450 451 452
- `attribute`: 検証される属性の名前。
- `value`: 検証される値。
- `messages`: 属性に対する検証のエラーメッセージを保持するために使用される配列。
- `deferred`: Deferred オブジェクトをプッシュして入れることが出来る配列 (次の項で説明します)。
453

454
次の例では、既存のステータスのデータに含まれる有効な値が入力されたかどうかを検証する `StatusValidator` を作成します。
455
このバリデータは、サーバ側とクライアント側の両方のバリデーションをサポートします。
456 457 458 459 460 461 462 463 464 465 466 467

```php
namespace app\components;

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

class StatusValidator extends Validator
{
    public function init()
    {
        parent::init();
468
        $this->message = '無効なステータスが入力されました。';
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
    }

    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());
        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        return <<<JS
if (!$.inArray(value, $statuses)) {
    messages.push($message);
}
JS;
    }
}
```

492
> Tip|ヒント: 上記のコード例の主たる目的は、クライアント側バリデーションをサポートする方法を説明することにあります。
493
> 実際の仕事では、[in](tutorial-core-validators.md#in) コアバリデータを使って、同じ目的を達することが出来ます。
494
> 次のようにバリデーション規則を書けばよいのです。
495 496 497 498 499 500 501
>
> ```php
> [
>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],
> ]
> ```

502
### Deferred バリデーション <a name="deferred-validation"></a>
503

504 505
非同期のクライアント側バリデーションをサポートする必要がある場合は、[Defered オブジェクト](http://api.jquery.com/category/deferred-object/) を作成することが出来ます。
例えば、AJAX によるカスタムバリデーションを実行するために、次のコードを使うことが出来ます。
506 507 508 509 510 511 512 513 514 515 516 517 518 519

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        deferred.push($.get("/check", {value: value}).done(function(data) {
            if ('' !== data) {
                messages.push(data);
            }
        }));
JS;
}
```

520 521
上のコードにおいて、`deferred` は Yii が提供する変数で、Deferred オブジェクトの配列です。
jQuery の `$.get()` メソッドによって作成された Deferred オブジェクトが `deferred` 配列にプッシュされています。
522

523 524
Deferred オブジェクトを明示的に作成して、非同期のコールバックが呼ばれたときに、Deferred オブジェクトの `resolve()` メソッドを呼ぶことも出来ます。
次の例は、アップロードされる画像ファイルの大きさをクライアント側で検証する方法を示すものです。
525 526 527 528 529 530 531 532 533

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        var def = $.Deferred();
        var img = new Image();
        img.onload = function() {
            if (this.width > 150) {
534
                messages.push('画像の幅が大きすぎます。');
535 536 537 538 539 540 541 542 543 544 545 546 547 548
            }
            def.resolve();
        }
        var reader = new FileReader();
        reader.onloadend = function() {
            img.src = reader.result;
        }
        reader.readAsDataURL(file);

        deferred.push(def);
JS;
}
```

549
> Note|注意: 属性が検証された後に、`resolve()` メソッドを呼び出さなければなりません。
550
  そうしないと、主たるフォームのバリデーションが完了しません。
551

552 553
簡潔に記述できるように、`deferred` 配列はショートカットメソッド `add()` を装備しており、このメソッドを使うと、自動的に Deferred オブジェクトを作成して `deferred` 配列に追加することが出来ます。
このメソッドを使えば、上記の例は次のように簡潔に記すことが出来ます。
554 555 556 557 558 559 560 561 562

```php
public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        deferred.add(function(def) {
            var img = new Image();
            img.onload = function() {
                if (this.width > 150) {
563
	                messages.push('画像の幅が大きすぎます。');
564 565 566 567 568 569 570 571 572 573 574 575 576 577
                }
                def.resolve();
            }
            var reader = new FileReader();
            reader.onloadend = function() {
                img.src = reader.result;
            }
            reader.readAsDataURL(file);
        });
JS;
}
```


578
### AJAX バリデーション <a name="ajax-validation"></a>
579

580 581
場合によっては、サーバだけが必要な情報を持っているために、サーバ側でしか検証が実行できないことがあります。
例えば、ユーザ名がユニークであるか否かを検証するためには、サーバ側で user テーブルを調べることが必要になります。
582 583
このような場合には、AJAX ベースのバリデーションを使うことが出来ます。
AJAX バリデーションは、通常のクライアント側でのバリデーションと同じユーザ体験を保ちながら、入力値を検証するためにバックグラウンドで AJAX リクエストを発行します。
584

585
AJAX バリデーションをフォーム全体に対して有効にするためには、[[yii\widgets\ActiveForm::enableAjaxValidation]] プロパティを `true` に設定して、`id` にフォームを特定するユニークな ID を設定しなければなりません。
586 587 588 589 590 591 592 593

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

594
個別の入力フィールドについても、[[yii\widgets\ActiveField::enableAjaxValidation]] プロパティを設定して、AJAX バリデーションを有効にしたり無効にしたりすることが出来ます。
595

596
また、サーバ側では、AJAX バリデーションのリクエストを処理できるように準備しておく必要があります。
597
これは、コントローラのアクションにおいて、次のようなコード断片を使用することで達成できます。
598 599 600 601 602 603 604 605

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

606
上記のコードは、現在のリクエストが AJAX であるかどうかをチェックします。
607
もし AJAX であるなら、リクエストに応えてバリデーションを実行し、エラーを JSON 形式で返します。
608

609 610
> Info|情報: AJAX バリデーションを実行するためには、[Deferred バリデーション](#deferred-validation) を使うことも出来ます。
  しかし、ここで説明された AJAX バリデーションの機能の方がより体系化されており、コーディングの労力も少なくて済みます。