2 フォロワー

入力のバリデーション

原則として、エンドユーザーから受信したデータは決して信頼せず、有効活用する前に常にバリデーションする必要があります。

ユーザー入力で埋めたモデルがあるとします。 yii\base\Model::validate() メソッドを呼び出すことで、入力をバリデーションできます。このメソッドは、バリデーションが成功したかどうかを示すブール値を返します。成功しなかった場合、yii\base\Model::$errors プロパティからエラーメッセージを取得できます。例:

$model = new \app\models\ContactForm();

// populate model attributes with user inputs
$model->load(\Yii::$app->request->post());
// which is equivalent to the following:
// $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;
}

ルールの宣言

validate() を実際に機能させるには、バリデーションする属性に対してバリデーションルールを宣言する必要があります。これは、yii\base\Model::rules() メソッドをオーバーライドすることで行います。次の例は、ContactForm モデルのバリデーションルールの宣言方法を示しています。

public function rules()
{
    return [
        // the name, email, subject and body attributes are required
        [['name', 'email', 'subject', 'body'], 'required'],

        // the email attribute should be a valid email address
        ['email', 'email'],
    ];
}

rules() メソッドは、ルールの配列を返す必要があります。各ルールは、次の形式の配列です。

[
    // required, specifies which attributes should be validated by this rule.
    // For a single attribute, you can use the attribute name directly
    // without having it in an array
    ['attribute1', 'attribute2', ...],

    // 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
    // You may also configure the "except" option if you want to apply the rule
    // to all scenarios except the listed ones
    'on' => ['scenario1', 'scenario2', ...],

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

各ルールでは、少なくともルールが適用される属性とルールのタイプを指定する必要があります。ルールのタイプは、次のいずれかの形式で指定できます。

  • requiredindate など、コアバリデータのエイリアス。コアバリデータの完全なリストについては、コアバリデータ を参照してください。
  • モデルクラスのバリデーションメソッド名、または無名関数。詳細については、インラインバリデータ のセクションを参照してください。
  • 完全修飾バリデータクラス名。詳細については、スタンドアロンバリデータ のセクションを参照してください。

ルールは1つまたは複数の属性の検証に使用でき、属性は1つまたは複数のルールによって検証される場合があります。ルールは、on オプションを指定することで、特定のシナリオでのみ適用できます。on オプションを指定しない場合、ルールはすべてのシナリオに適用されます。

validate() メソッドが呼び出されると、検証を実行するために次の手順を実行します。

  1. 現在のシナリオを使用してyii\base\Model::scenarios()から属性リストを取得することにより、検証する属性を決定します。これらの属性はアクティブな属性と呼ばれます。
  2. 現在のシナリオを使用してyii\base\Model::rules()からルールリストを取得することにより、使用する検証ルールを決定します。これらのルールはアクティブなルールと呼ばれます。
  3. 各アクティブなルールを使用して、ルールに関連付けられている各アクティブな属性を検証します。検証ルールは、リストされている順序で評価されます。

上記の検証手順によると、属性が検証されるのは、scenarios()で宣言されたアクティブな属性であり、rules()で宣言された1つまたは複数のアクティブなルールに関連付けられている場合のみです。

注記: ルールに名前を付けることは便利です。

public function rules()
{
    return [
        // ...
        'password' => [['password'], 'string', 'max' => 60],
    ];
}

子モデルで使用できます。

public function rules()
{
    $rules = parent::rules();
    unset($rules['password']);
    return $rules;
}

エラーメッセージのカスタマイズ

ほとんどのバリデータには、属性が検証に失敗した場合に検証対象のモデルに追加されるデフォルトのエラーメッセージがあります。たとえば、requiredバリデータは、username属性がこのバリデータを使用したルールに失敗した場合、モデルに「ユーザー名は空にできません。」というメッセージを追加します。

ルールのエラーメッセージは、ルールを宣言する際にmessageプロパティを指定してカスタマイズできます。例を以下に示します。

public function rules()
{
    return [
        ['username', 'required', 'message' => 'Please choose a username.'],
    ];
}

一部のバリデータは、検証の失敗のさまざまな原因をより正確に記述するために、追加のエラーメッセージをサポートしています。たとえば、numberバリデータは、検証対象の値が大きすぎる場合と小さすぎる場合に、それぞれ検証の失敗を記述するためにtooBigtooSmallをサポートしています。これらのエラーメッセージは、検証ルールのバリデータの他のプロパティを構成する場合と同様に構成できます。

検証イベント

yii\base\Model::validate()が呼び出されると、検証プロセスをカスタマイズするためにオーバーライドできる2つのメソッドが呼び出されます。

  • yii\base\Model::beforeValidate():デフォルトの実装では、yii\base\Model::EVENT_BEFORE_VALIDATEイベントがトリガーされます。このメソッドをオーバーライドするか、このイベントに応答して、検証の前にいくつかの前処理作業(例:データ入力の正規化)を実行できます。このメソッドは、検証を続行するかどうかを示すブール値を返す必要があります。
  • yii\base\Model::afterValidate():デフォルトの実装では、yii\base\Model::EVENT_AFTER_VALIDATEイベントがトリガーされます。このメソッドをオーバーライドするか、このイベントに応答して、検証が完了した後にいくつかの後処理作業を実行できます。

条件付き検証

特定の条件が適用される場合のみ属性を検証するには(たとえば、ある属性の検証が別の属性の値に依存する場合)、whenプロパティを使用してそのような条件を定義できます。例を以下に示します。

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

whenプロパティは、次のシグネチャを持つPHP callableを取ります。

/**
 * @param Model $model the model being validated
 * @param string $attribute the attribute being validated
 * @return bool whether the rule should be applied
 */
function ($model, $attribute)

クライアント側の条件付き検証もサポートする必要がある場合は、whenClientプロパティを構成する必要があります。このプロパティは、JavaScript関数を表す文字列を取ります。この関数の戻り値によって、ルールを適用するかどうかが決まります。例を以下に示します。

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

データフィルタリング

ユーザー入力は、多くの場合、フィルタリングまたは前処理する必要があります。たとえば、username入力の周りのスペースをトリミングしたい場合があります。検証ルールを使用してこの目標を達成できます。

次の例は、trimおよびdefaultコアバリデータを使用して、入力のスペースをトリミングし、空の入力をnullに変換する方法を示しています。

return [
    [['username', 'email'], 'trim'],
    [['username', 'email'], 'default'],
];

より複雑なデータフィルタリングを実行するには、より一般的なfilterバリデータを使用することもできます。

ご覧のとおり、これらの検証ルールは実際に入力を検証しません。代わりに、値を処理して、検証対象の属性に保存し直します。

ユーザー入力の完全な処理を次の例コードに示します。これにより、属性には整数値のみが保存されます。

['age', 'trim'],
['age', 'default', 'value' => null],
['age', 'integer', 'min' => 0],
['age', 'filter', 'filter' => 'intval', 'skipOnEmpty' => true],

上記のコードは、入力に対して次の操作を実行します。

  1. 入力値から空白をトリミングします。
  2. 空の入力をデータベースにnullとして保存します。値が「設定されていない」ことと、実際の値0を区別します。nullが許可されていない場合は、ここで別のデフォルト値を設定できます。
  3. 空でない場合、値が0より大きい整数であることを検証します。通常のバリデータでは、$skipOnEmptytrueに設定されています。
  4. 値が整数型であることを確認します(たとえば、文字列'42'を整数42にキャストします)。ここでは、$skipOnEmptytrueに設定します。これは、filterバリデータではデフォルトでfalseです。

空の入力の処理

HTMLフォームから入力データが送信されると、空の場合に入力にデフォルト値を割り当てる必要があることがよくあります。defaultバリデータを使用してこれを行うことができます。例を以下に示します。

return [
    // 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],
];

デフォルトでは、入力が空文字列、空の配列、またはnullの場合、空と見なされます。PHP callableを使用してyii\validators\Validator::isEmpty()プロパティを構成することで、デフォルトの空の検出ロジックをカスタマイズできます。例を以下に示します。

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

注記: ほとんどのバリデータは、yii\validators\Validator::$skipOnEmptyプロパティがデフォルト値trueの場合、空の入力を処理しません。関連付けられた属性が空の入力を受け取った場合、検証中に単にスキップされます。コアバリデータのうち、captchadefaultfilterrequiredtrimバリデータのみが空の入力を処理します。

随時検証

場合によっては、どのモデルにもバインドされていない値に対して随時検証を行う必要があります。

1種類の検証のみを実行する必要がある場合(例:メールアドレスの検証)、次のように目的のバリデータのvalidate()メソッドを呼び出すことができます。

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

if ($validator->validate($email, $error)) {
    echo 'Email is valid.';
} else {
    echo $error;
}

注記: すべてのバリデータがこのタイプの検証をサポートしているわけではありません。例としては、モデルでのみ動作するように設計されているuniqueコアバリデータがあります。

注記: yii\base\Validator::skipOnEmptyプロパティは、yii\base\Modelの検証のみに使用されます。モデルなしで使用しても効果はありません。

複数の値に対して複数の検証を実行する必要がある場合は、属性とルールを動的に宣言できるyii\base\DynamicModelを使用できます。使用方法を以下に示します。

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

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

yii\base\DynamicModel::validateData()メソッドは、DynamicModelのインスタンスを作成し、指定されたデータ(この例ではnameemail)を使用して属性を定義し、次に指定されたルールでyii\base\Model::validate()を呼び出します。

または、随時データ検証を実行するために、次のより「従来の」構文を使用できます。

public function actionSearch($name, $email)
{
    $model = new DynamicModel(['name' => $name, 'email' => $email]);
    $model->addRule(['name', 'email'], 'string', ['max' => 128])
        ->addRule('email', 'email')
        ->validate();

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

検証後、hasErrors()メソッドを呼び出して検証が成功したかどうかを確認し、通常のモデルと同様にerrorsプロパティから検証エラーを取得できます。モデルインスタンスを介して定義された動的な属性(例:$model->name$model->email)にアクセスすることもできます。

バリデータの作成

Yiiリリースに含まれるコアバリデータを使用することに加えて、独自のバリデータを作成することもできます。インラインバリデータまたはスタンドアロンバリデータを作成できます。

インラインバリデータ

インラインバリデータは、モデルメソッドまたは無名関数で定義されたものです。メソッド/関数のシグネチャは次のとおりです。

/**
 * @param string $attribute the attribute currently being validated
 * @param mixed $params the value of the "params" given in the rule
 * @param \yii\validators\InlineValidator $validator related InlineValidator instance.
 * This parameter is available since version 2.0.11.
 * @param mixed $current the currently validated value of attribute.
 * This parameter is available since version 2.0.36.
 */
function ($attribute, $params, $validator, $current)

属性が検証に失敗した場合、メソッド/関数はyii\base\Model::addError()を呼び出してモデルにエラーメッセージを保存する必要があります。これにより、後でエンドユーザーに提示するために取得できます。

いくつかの例を以下に示します。

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, $validator) {
                if (!ctype_alnum($this->$attribute)) {
                    $this->addError($attribute, 'The token must contain letters or digits.');
                }
            }],
        ];
    }

    public function validateCountry($attribute, $params, $validator)
    {
        if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {
            $this->addError($attribute, 'The country must be either "USA" or "Indonesia".');
        }
    }
}

注記: バージョン2.0.11以降は、エラーを追加するためにyii\validators\InlineValidator::addError()を使用できます。このようにして、yii\i18n\I18N::format()を使用してすぐにエラーメッセージをフォーマットできます。属性ラベル(手動で取得する必要はありません)と属性値をそれぞれ参照するには、エラーメッセージに{attribute}{value}を使用します。

$validator->addError($this, $attribute, 'The value "{value}" is not acceptable for {attribute}.');

注記: デフォルトでは、関連付けられた属性が空の入力を受け取った場合、または既にいくつかの検証ルールに失敗している場合は、インラインバリデータは適用されません。ルールが常に適用されるようにする場合は、ルール宣言でskipOnEmptyおよび/またはskipOnErrorプロパティをfalseに構成できます。例を以下に示します。

[
    ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],
]

スタンドアロンバリデータ

スタンドアロンバリデータは、yii\validators\Validatorまたはその子クラスを拡張するクラスです。yii\validators\Validator::validateAttribute()メソッドをオーバーライドすることにより、その検証ロジックを実装できます。属性が検証に失敗した場合、インラインバリデータと同様に、yii\base\Model::addError()を呼び出してモデルにエラーメッセージを保存します。

たとえば、上記のインラインバリデータは、新しい[[components/validators/CountryValidator]]クラスに移動できます。この場合、yii\validators\Validator::addError()を使用して、モデルのカスタマイズされたメッセージを設定できます。

namespace app\components;

use yii\validators\Validator;

class CountryValidator extends Validator
{
    public function validateAttribute($model, $attribute)
    {
        if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {
            $this->addError($model, $attribute, 'The country must be either "{country1}" or "{country2}".', ['country1' => 'USA', 'country2' => 'Indonesia']);
        }
    }
}

モデルなしで値の検証をサポートするバリデータを作成する場合は、yii\validators\Validator::validate()もオーバーライドする必要があります。validateAttribute()validate()の代わりにyii\validators\Validator::validateValue()をオーバーライドすることもできます。これは、デフォルトで後者の2つのメソッドはvalidateValue()を呼び出すことによって実装されているためです。

以下は、上記のバリデータクラスをモデル内で使用する方法の例です。

namespace app\models;

use Yii;
use yii\base\Model;
use app\components\validators\CountryValidator;

class EntryForm extends Model
{
    public $name;
    public $email;
    public $country;

    public function rules()
    {
        return [
            [['name', 'email'], 'required'],
            ['country', CountryValidator::class],
            ['email', 'email'],
        ];
    }
}

複数属性のバリデーション

バリデータは複数の属性に関係することがあります。以下のフォームを考えてみましょう。

class MigrationForm extends \yii\base\Model
{
    /**
     * Minimal funds amount for one adult person
     */
    const MIN_ADULT_FUNDS = 3000;
    /**
     * Minimal funds amount for one child
     */
    const MIN_CHILD_FUNDS = 1500;

    public $personalSalary;
    public $spouseSalary;
    public $childrenCount;
    public $description;

    public function rules()
    {
        return [
            [['personalSalary', 'description'], 'required'],
            [['personalSalary', 'spouseSalary'], 'integer', 'min' => self::MIN_ADULT_FUNDS],
            ['childrenCount', 'integer', 'min' => 0, 'max' => 5],
            [['spouseSalary', 'childrenCount'], 'default', 'value' => 0],
            ['description', 'string'],
        ];
    }
}

バリデータの作成

家族の収入が子供の養育費として十分かどうかを確認する必要があるとします。そのため、childrenCountが0より大きい場合にのみ実行されるインラインバリデータvalidateChildrenFundsを作成できます。

バリデータの添付時に、すべての検証済み属性(['personalSalary', 'spouseSalary', 'childrenCount'])を使用することはできません。これは、同じバリデータが各属性に対して実行されるため(合計3回)、属性セット全体に対して1回だけ実行する必要があるためです。

これらの属性のいずれかを使用するか、最も関連性の高いと思われる属性を使用できます。

['childrenCount', 'validateChildrenFunds', 'when' => function ($model) {
    return $model->childrenCount > 0;
}],

validateChildrenFundsの実装は次のようになります。

public function validateChildrenFunds($attribute, $params)
{
    $totalSalary = $this->personalSalary + $this->spouseSalary;
    // Double the minimal adult funds if spouse salary is specified
    $minAdultFunds = $this->spouseSalary ? self::MIN_ADULT_FUNDS * 2 : self::MIN_ADULT_FUNDS;
    $childFunds = $totalSalary - $minAdultFunds;
    if ($childFunds / $this->childrenCount < self::MIN_CHILD_FUNDS) {
        $this->addError('childrenCount', 'Your salary is not enough for children.');
    }
}

検証は単一の属性に関連していないため、$attributeパラメータは無視できます。

エラーの追加

複数の属性の場合のエラーの追加方法は、目的のフォームデザインによって異なります。

  • 最も関連性の高いフィールドを選択し、その属性にエラーを追加します。
$this->addError('childrenCount', 'Your salary is not enough for children.');
  • 複数の重要な関連属性またはすべての属性を選択し、それらに同じエラーメッセージを追加します。コードをDRYに保つために、addErrorに渡す前にメッセージを別の変数に格納できます。
$message = 'Your salary is not enough for children.';
$this->addError('personalSalary', $message);
$this->addError('wifeSalary', $message);
$this->addError('childrenCount', $message);

またはループを使用します。

$attributes = ['personalSalary', 'wifeSalary', 'childrenCount'];
foreach ($attributes as $attribute) {
    $this->addError($attribute, 'Your salary is not enough for children.');
}
  • 共通のエラー(特定の属性に関連しないエラー)を追加します。エラーを追加するために、存在しない属性名(例:*)を使用できます。この時点では属性の存在はチェックされません。
$this->addError('*', 'Your salary is not enough for children.');

その結果、フォームフィールドの近くにエラーメッセージが表示されません。表示するには、ビューにエラーサマリーを含めることができます。

<?= $form->errorSummary($model) ?>

注記:複数の属性を一度に検証するバリデータの作成については、コミュニティクックブックで詳しく説明されています。

クライアントサイドバリデーション

エンドユーザーがHTMLフォームを介して入力を行う場合、JavaScriptに基づくクライアントサイドバリデーションが望ましいです。これは、ユーザーが入力エラーをより迅速に発見できるようにし、より優れたユーザーエクスペリエンスを提供するためです。サーバーサイドバリデーションに加えて、クライアントサイドバリデーションをサポートするバリデータを使用または実装できます。

情報:クライアントサイドバリデーションは望ましいものですが、必須ではありません。その主な目的は、ユーザーにより良いエクスペリエンスを提供することです。エンドユーザーからの入力データと同様に、クライアントサイドバリデーションを信頼すべきではありません。このため、前述のサブセクションで説明されているように、常にyii\base\Model::validate()を呼び出してサーバーサイドバリデーションを実行する必要があります。

クライアントサイドバリデーションの使用

多くのコアバリデータは、すぐに使用できるクライアントサイドバリデーションをサポートしています。必要なのは、yii\widgets\ActiveFormを使用してHTMLフォームを作成することだけです。たとえば、以下のLoginFormは2つのルールを宣言しています。1つはクライアントサイドとサーバーサイドの両方でサポートされているrequiredコアバリデータを使用し、もう1つはサーバーサイドのみでサポートされているvalidatePasswordインラインバリデータを使用します。

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.');
        }
    }
}

以下のコードで作成されたHTMLフォームには、usernamepasswordの2つの入力フィールドが含まれています。何も入力せずにフォームを送信すると、サーバーとの通信なしですぐに何かを入力する必要があるというエラーメッセージが表示されます。

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

内部的には、yii\widgets\ActiveFormはモデルで宣言された検証ルールを読み取り、クライアントサイドバリデーションをサポートするバリデータに対して適切なJavaScriptコードを生成します。ユーザーが入力フィールドの値を変更するか、フォームを送信すると、クライアントサイドバリデーションJavaScriptがトリガーされます。

クライアントサイドバリデーションを完全に無効にする場合は、yii\widgets\ActiveForm::$enableClientValidationプロパティをfalseに設定できます。個々の入力フィールドのクライアントサイドバリデーションを無効にするには、yii\widgets\ActiveField::$enableClientValidationプロパティをfalseに設定することもできます。enableClientValidationが入力フィールドレベルとフォームレベルの両方で設定されている場合、前者が優先されます。

情報:バージョン2.0.11以降、yii\validators\Validatorを拡張するすべてのバリデータは、個別のメソッド - yii\validators\Validator::getClientOptions()からクライアントサイドオプションを受け取ります。これを使用できます。

  • 独自のクライアントサイドバリデーションを実装するが、サーバーサイドバリデータオプションとの同期を維持したい場合。
  • 特定のニーズに合わせて拡張またはカスタマイズする場合。
public function getClientOptions($model, $attribute)
{
    $options = parent::getClientOptions($model, $attribute);
    // Modify $options here

    return $options;
}

クライアントサイドバリデーションの実装

クライアントサイドバリデーションをサポートするバリデータを作成するには、クライアントサイドでバリデーションを実行するJavaScriptコードを返すyii\validators\Validator::clientValidateAttribute()メソッドを実装する必要があります。JavaScriptコード内では、以下の事前定義変数を使用できます。

  • attribute:検証対象の属性名。
  • value:検証対象の値。
  • messages:属性のバリデーションエラーメッセージを格納するために使用される配列。
  • deferred:延期オブジェクトをプッシュできる配列(次のサブセクションで説明)。

次の例では、既存のステータスデータに対して入力が有効なステータス入力かどうかを検証するStatusValidatorを作成します。このバリデータは、サーバーサイドとクライアントサイドの両方のバリデーションをサポートしています。

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

ヒント:上記のコードは、主にクライアントサイドバリデーションのサポート方法を示すために提供されています。実際には、inコアバリデータを使用して同じ目標を達成できます。検証ルールは次のように記述できます。

[
    ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],
]

ヒント:クライアントバリデーションを手動で操作する必要がある場合(つまり、動的にフィールドを追加したり、カスタムUIロジックを実行したりする場合)、Yii 2.0クックブックのJavaScriptを使用したActiveFormの操作を参照してください。

延期バリデーション

非同期クライアントサイドバリデーションを実行する必要がある場合は、Deferredオブジェクトを作成できます。たとえば、カスタムAJAXバリデーションを実行するには、次のコードを使用できます。

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

上記では、deferred変数はYiiによって提供され、Deferredオブジェクトの配列です。$.get()jQueryメソッドは、deferred配列にプッシュされるDeferredオブジェクトを作成します。

Deferredオブジェクトを明示的に作成し、非同期コールバックがヒットしたときにそのresolve()メソッドを呼び出すこともできます。次の例は、クライアントサイドでアップロードされた画像ファイルのサイズを検証する方法を示しています。

public function clientValidateAttribute($model, $attribute, $view)
{
    return <<<JS
        var def = $.Deferred();
        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);

        deferred.push(def);
JS;
}

注記:resolve()メソッドは、属性が検証された後に呼び出す必要があります。それ以外の場合は、メインフォームの検証が完了しません。

簡潔にするために、deferred配列には、Deferredオブジェクトを自動的に作成してdeferred配列に追加するショートカットメソッドadd()が装備されています。このメソッドを使用すると、上記の例を次のように簡素化できます。

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;
}

AJAXバリデーション

一部のバリデーションは、サーバー側でのみ実行できます。サーバーだけが必要な情報を持っているためです。たとえば、ユーザー名が固有かどうかを検証するには、サーバー側のユーザーテーブルを確認する必要があります。この場合、AJAXベースのバリデーションを使用できます。これにより、通常のクライアントサイドバリデーションと同じユーザーエクスペリエンスを維持しながら、バックグラウンドで入力値を検証するためのAJAXリクエストがトリガーされます。

単一入力フィールドのAJAXバリデーションを有効にするには、そのフィールドのenableAjaxValidationプロパティをtrueに設定し、一意のフォームidを指定します。

use yii\widgets\ActiveForm;

$form = ActiveForm::begin([
    'id' => 'registration-form',
]);

echo $form->field($model, 'username', ['enableAjaxValidation' => true]);

// ...

ActiveForm::end();

フォームのすべての入力のAJAXバリデーションを有効にするには、フォームレベルでenableAjaxValidationtrueに設定します。

$form = ActiveForm::begin([
    'id' => 'contact-form',
    'enableAjaxValidation' => true,
]);

注記:enableAjaxValidationプロパティが入力フィールドレベルとフォームレベルの両方で設定されている場合、前者が優先されます。

AJAXバリデーションリクエストを処理できるようにサーバーを準備する必要もあります。これは、コントローラーアクションで次のコードスニペットを使用することで実現できます。

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

上記のコードは、現在のリクエストがAJAXかどうかをチェックします。はいの場合、このリクエストに応答してバリデーションを実行し、エラーをJSON形式で返します。

情報:延期バリデーションを使用してAJAXバリデーションを実行することもできます。ただし、ここで説明されているAJAXバリデーション機能はより体系的であり、コーディングの労力が少なくなります。

enableClientValidationenableAjaxValidationの両方がtrueに設定されている場合、AJAXバリデーションリクエストはクライアントバリデーションが成功した後にのみトリガーされます。単一フィールドを検証する場合(validateOnChangevalidateOnBlur、またはvalidateOnTypetrueに設定されている場合)、該当するフィールドがクライアントバリデーションに合格したときにAJAXリクエストが送信されます。

タイプミスを見つけた場合、またはこのページを改善する必要があると思われる場合は、
githubで編集してください。 !