バリデーション

JSONスキーマはJSONデータの構造を検証するための強力なツールです。 リソースがどのような制約を持つデータなのかを規定することができます。

@Validアノテーションによるバリデーションは入力値をユーザーのPHPコードで検証します。 Webフォームによりバリデーションはフォームをご覧ください。

JSONスキーマ

BEAR.Sundayのリソースオブジェクトを@JsonSchemaとアノテートするとリソースオブジェクトのbody(リソース状態)にJSONスキーマによる検証が行われます。 データ表現はJSONである必要はありません。

インストール

プロダクション含めてすべてのコンテキストでバリデーションを行うならAppModule、開発中のみバリデーションを行うなら例えばDevModuleを作成してその中でインストールします。

use BEAR\Resource\Module\JsonSchemalModule; // この行を追加
use Ray\Di\AbstractModule;

class AppModule extends AbstractModule
{
    protected function configure()
    {
        // ...
        $this->install(new JsonSchemalModule);  // この行を追加
    }
}

@JsonSchema アノテーション

検証を行うクラスのonGetメソッドで@JsonSchemaとアノテートします。

Person.php


use BEAR\Resource\Annotation\JsonSchema; // この行を追加

class Person extends ResourceObject
{
    /**
     * @JsonSchema
     */
    public function onGet()
    {
        $this->body = [
            'firstName' => 'mucha',
            'lastName' => 'alfons',
            'age' => 12
        ];

        return $this;
    }
}

同じディレクトリで拡張子をjsonにしてJSONスキーマを記述します。

Person.json

{
  "title": "Person",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "age": {
      "description": "Age in years",
      "type": "integer",
      "minimum": 20
    }
  },
  "required": ["firstName", "lastName"],
  "additionalProperties": false
}

このようにJSONスキーマはリクエストしたリソースがどのような制約を持ったリソースなのかを規定することができます。

開発者が出力されるデータフォーマットを独自のドキュメントに残す代わりに標準化されたJSONスキーマを利用することで、その制約が人間にもマシンにも理解できるものとなり 宣言されたスキーマが確実に正しいと確証を得ることができます。

@Validアノテーション

@Validアノテーションは入力のためのバリデーションです。メソッドの実行前にバリデーションメソッドが実行され、 エラーを検知すると例外が発生されエラー処理のためのメソッドを呼ぶこともできます。

分離したバリデーションのコードは可読性に優れテストが容易です。バリデーションのライブラリはAura.FilterRespect\Validation、あるいはPHP標準のFilterを使います。

インストール

composerインストール

composer require ray/validate-module

アプリケーションモジュールsrc/Module/AppModule.phpValidateModuleをインストールします。

use Ray\Validation\ValidateModule;

class AppModule extends AbstractModule
{
    protected function configure()
    {
        // ...
        $this->install(new ValidateModule);
    }
}

アノテーション

バリデーションのために@Valid@OnValidate@OnFailureの3つのアノテーションが用意されています。

まず、バリデーションを行いたいメソッドに@Validとアノテートします。

use Ray\Validation\Annotation\Valid;
// ...
    /**
     * @Valid
     */
    public function createUser($name)
    {

@OnValidateとアノテートしたメソッドでバリデーションを行います。引数は元のメソッドと同じにします。メソッド名は自由です。

use Ray\Validation\Annotation\OnValidate;
// ...
    /**
     * @OnValidate
     */
    public function onValidate($name)
    {
        $validation = new Validation;
        if (! is_string($name)) {
            $validation->addError('name', 'name should be string');
        }

        return $validation;
    }

バリデーション失敗した要素には要素名エラーメッセージを指定してValidationオブジェクトにaddError()し、最後にValidationオブジェクトを返します。

バリデーションが失敗すればRay\Validation\Exception\InvalidArgumentException例外が投げられますが、 @OnFailureメソッドが用意されていればそのメソッドの結果が返されます。

use Ray\Validation\Annotation\OnFailure;
// ...
    /**
     * @OnFailure
     */
    public function onFailure(FailureInterface $failure)
    {
        // original parameters
        list($this->defaultName) = $failure->getInvocation()->getArguments();

        // errors
        foreach ($failure->getMessages() as $name => $messages) {
            foreach ($messages as $message) {
                echo "Input '{$name}': {$message}" . PHP_EOL;
            }
        }
    }

@OnFailureメソッドには$failureが渡され($failure->getMessages()でエラーメッセージや$failure->getInvocation()でオリジナルメソッド実行のオブジェクトが取得できます。

複数のバリデーション

1つのクラスに複数のバリデーションメソッドが必要なときは以下のようにバリデーションの名前を指定します。

use Ray\Validation\Annotation\Valid;
use Ray\Validation\Annotation\OnValidate;
use Ray\Validation\Annotation\OnFailure;
// ...

    /**
     * @Valid("foo")
     */
    public function fooAction($name, $address, $zip)
    {

    /**
     * @OnValidate("foo")
     */
    public function onValidateFoo($name, $address, $zip)
    {

    /**
     * @OnFailure("foo")
     */
    public function onFailureFoo(FailureInterface $failure)
    {

その他のバリデーション

複雑なバリデーションの時は別にバリデーションクラスをインジェクトして、onValidateメソッドから呼び出してバリデーションを行います。DIなのでコンテキストによってバリデーションを変えることもできます。