Symfony Advent Calendar 2019 2日目の記事です。
昨日は@77webさんのSymfony4.4正式リリース前に4.4.x-devで開発し始めたプロジェクトを4.4.0正式版にアップデートするでした。
ptyhard/JsonSchemaBundle
ptyhard/JsonSchemaBundleを作りました。
@kojirock5260さんの
LaravelでJsonSchema使いたい を読んで刺激を受けて、作り始めました。
SymfonyでJsonSchemaを利用したValidationを行う場合、いくつかのBundleを使えば簡単にJsonSchema Validationができるかと思います。
使用したことはありませんがApi Platformでもサポートしているようです。
ただDoctrineとべったりな感じが個人的にはあまり好きにはなれません。
ptyhard/JsonSchemaBundleのコンセプト
- JsonSchemaをアノテーションで表現する
- 入力値をクラスにマッピングする
この2つの機能を実現するために作っています。
JsonSchemaをアノテーションで表現する
ptyhard/JsonSchemaBundleはjsonSchemaをAnnotationで表現できます。
- ArrayProperty
- CollectionProperty
- NumberProperty
- ObjectProperty
- StringProperty
をサポートしています。
(なぜBooleanのサポートがないのか???あとで対応しておきます)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<?php
namespace App\Schema;
use Ptyhard\JsonSchemaBundle\Annotations\Property as SchemaProperty;
use Ptyhard\JsonSchemaBundle\Annotations\SchemaClass;
/**
* @SchemaClass({ required: {"id"} })
*/
class User
{
/**
* @SchemaProperty\NumberProperty()
* @var int
*/
private $id;
/**
* @SchemaProperty\StringProperty(minLength=1, maxLength=125)
* @var string
*/
private $name;
/**
* @SchemaProperty\StringProperty()
* @var string
*/
private $description;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
}
|
入力値をオブジェクトにマッピングする
クライアントから送信されたデータをオブジェクトにマッピングします。
コントローラのアクションの引数にスキーマを定義したクラスを指定すれば勝手にマッピングされるようになっています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?php
namespace App\Controller;
use App\Schema\User;
use Polidog\SimpleApiBundle\Annotations\Api;
use Symfony\Component\Routing\Annotation\Route;
class UserController
{
/**
* @Route("/user", methods={"POST"})
* @Api(statusCode=200)
* @param User $user
* @return User
*/
public function __invoke(User $user): User
{
}
}
|
上記のようにUserクラスを引数で渡せば実行時にオブジェクトに値をマッピングします。
requestクラスからめんどくさいオブジェクトへの値マッピングの実装をしなくていいので便利ですよね。
ファイルを指定することもできる
スキーマ定義のjsonファイルをを用意している場合には@SchemaFile
を使ってバリデーションを行う事もできます。
public/json_schema
のディレクトリにschema.jsonを入れてもらいクラスファイルに以下のように書いてください。
それから、Controllerのアクションに利用するjsonSchemaのファイルを指定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
namespace App\Controller;
use Polidog\SimpleApiBundle\Annotations\Api;
use Ptyhard\JsonSchemaBundle\Annotations\SchemaFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class UserController
{
/**
* @Route("/user", methods={"POST"})
* @Api(statusCode=200)
* @SchemaFile(file="user.json")
*/
public function __invoke(): JsonResponse
{
return new JsonResponse(['status' => 'ok']);
}
}
|
jsonのファイルを置く位置を変更することもできます。
1
2
3
4
|
// config/ptyhard_json_schema.yaml
ptyhard_json_schema:
json_file_directory: '/public/schema'
|
レスポンスする値を検証する
レスポンスする値に対してバリデーションを行う事もできます。
@SchemaObject
の場合は戻り地の型がSchemaObjectのAnnotationがあるオブジェクトであれば勝手にバリデーションします。
(オブジェクトになっているのでバリデーションいらない気もしますが)
@SchemaFile
場合は設定が必要になります。以下のようにAnnotationに記述すればレスポンスする値も検証できます。
1
|
@SchemaFile(file="user.json", type={"response"})
|
このようにtypeにrequest,responseを定義すると指定したタイミングでバリデーションができます。
最後に
小さなSPA的なアプリケーションでpolidog/simple-api-bundle と組み合わせ使いましたが、なかなかの使いやすさがありました。
まだまだBundle自体にはバグがあるとは思いますが、ぜひ使ってみてフィードバックやPull Requestいただけたら嬉しいです。