バリデーション

  • JSONスキーマでリソースAPIを定義する事ができます。
  • @Valid, @OnValidateアノテーションでバリデーションコードを分離する事ができます。
  • Webフォームによるバリデーションはフォームをご覧ください。

JSONスキーマ

JSON スキーマとは、JSON objectの記述と検証のための標準です。#[JsonSchema]アトリビュートが付加されたリソースクラスのメソッドが返すリソースbodyに対してJSONスキーマによる検証が行われます。

インストール

全てのコンテキストで常にバリデーションを行うならAppModule、開発中のみバリデーションを行うならDevModuleなどのクラスを作成してその中でインストールします。

use BEAR\Resource\Module\JsonSchemaModule; // この行を追加
use BEAR\Package\AbstractAppModule;

class AppModule extends AbstractAppModule
{
    protected function configure(): void
    {
        // ...
        $this->install(
            new JsonSchemaModule(
                $appDir . '/var/json_schema',
                $appDir . '/var/json_validate'
            )
        );   // この行を追加
    }
}

ディレクトリ作成

mkdir var/json_schema
mkdir var/json_validate

var/json_schema/にリソースのbodyの仕様となるJSONスキーマファイル、var/json_validate/には入力バリデーションのためのJSONスキーマファイルを格納します。

#[JsonSchema]アトリビュート

リソースクラスのメソッドで#[JsonSchema]のアトリビュートを加えます。schemaプロパティにはJSONスキーマファイル名を指定します。

schema

src/Resource/App/User.php


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

class User extends ResourceObject
{
    #[JsonSchema('user.json')]
    public function onGet(): static
    {
        $this->body = [
            'firstName' => 'mucha',
            'lastName' => 'alfons',
            'age' => 12
        ];

        return $this;
    }
}

JSONスキーマを設置します。

/var/json_schema/user.json

{
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "maxLength": 30,
      "pattern": "[a-z\\d~+-]+"
    },
    "lastName": {
      "type": "string",
      "maxLength": 30,
      "pattern": "[a-z\\d~+-]+"
    }
  },
  "required": ["firstName", "lastName"]
}

key

bodyにインデックスキーがある場合にはアノテーションのkeyプロパティで指定します。


use BEAR\Resource\Annotation\JsonSchema; // Add this line

class User extends ResourceObject
{
    #[JsonSchema(key:'user', schema:'user.json')]
    public function onGet()
    {
        $this->body = [
            'user' => [
                'firstName' => 'mucha',
                'lastName' => 'alfons',
                'age' => 12
            ]
        ];        

        return $this;
    }
}

params

params プロパティには引数のバリデーションのためのJSONスキーマファイル名を指定します。

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

class Todo extends ResourceObject
{
    #[JsonSchema(key:'user', schema:'user.json', params:'todo.post.json')]
    public function onPost(string $title)

JSONスキーマを設置します。

/var/json_validate/todo.post.json

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "/todo POST request validation",
  "properties": {
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40
    }
}

独自ドキュメントの代わりに標準化された方法で常に検証することで、その仕様が人間にもマシンにも理解できる確実なものになります。

target

ResourceObjectのbodyに対してでなく、リソースオブジェクトの表現(レンダリングされた結果)に対してスキーマバリデーションを適用にするにはtarget='view'オプションを指定します。 HALフォーマットで_linkのスキーマが記述できます。

#[JsonSchema(schema: 'user.json', target: 'view')]

関連リンク

@Validアノテーション

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

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

インストール

composerインストール

composer require ray/validate-module

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

use Ray\Validation\ValidateModule;

class AppModule extends AbstractAppModule
{
    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なのでコンテキストによってバリデーションを変えることもできます。