forked from nette/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path@home.texy
500 lines (346 loc) · 17.6 KB
/
@home.texy
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
スキーマデータバリデーション
**************
.[perex]
与えられたスキーマに対してデータ構造を検証し、正規化するための実用的なライブラリで、スマートで理解しやすいAPIを備えています。
インストール方法
```shell
composer require nette/schema
```
基本的な使い方 .[#toc-basic-usage]
---------------------------
変数`$schema` には検証スキーマ(これが何を意味し、どのように作成するかは後述します)があり、変数`$data` には検証および正規化したいデータ構造があります。これは例えば、APIや設定ファイルなどを通してユーザーから送られたデータであることができます。
このタスクは[api:Nette\Schema\Processor] クラスによって処理され、入力を処理して正規化されたデータを返すか、エラー時に[api:Nette\Schema\ValidationException] 例外を投げます。
```php
$processor = new Nette\Schema\Processor;
try {
$normalized = $processor->process($schema, $data);
} catch (Nette\Schema\ValidationException $e) {
echo 'Data is invalid: ' . $e->getMessage();
}
```
Method`$e->getMessages()` は全てのメッセージ文字列の配列を返し、`$e->getMessageObjects()` は全てのメッセージを"NetteSchema FilterMessage":https://api.nette.org/3.1/Nette/Schema/Message.htmlオブジェクトとして返す。
スキーマの定義 .[#toc-defining-schema]
-------------------------------
そして、今度はスキーマを作成してみましょう。[api:Nette\Schema\Expect] というクラスを使って定義します。実際に、データがどのようなものであるべきかという期待値を定義します。入力データは、bool型の要素`processRefund` とint型の要素`refundAmount` を含む構造体(例えば配列)でなければならないとします。
```php
use Nette\Schema\Expect;
$schema = Expect::structure([
'processRefund' => Expect::bool(),
'refundAmount' => Expect::int(),
]);
```
このようにスキーマを定義することで、初めて見る人でもわかりやすくなったと思います。
以下のデータを送って検証してみましょう。
```php
$data = [
'processRefund' => true,
'refundAmount' => 17,
];
$normalized = $processor->process($schema, $data); // OK, it passes
```
出力、つまり値`$normalized` は、オブジェクト`stdClass` です。出力を配列にしたい場合は、スキーマにキャストを追加します。 `Expect::structure([...])->castTo('array')`.
構造体のすべての要素はオプションであり,デフォルト値`null` を持ちます.例
```php
$data = [
'refundAmount' => 17,
];
$normalized = $processor->process($schema, $data); // OK, it passes
// $normalized = {'processRefund' => null, 'refundAmount' => 17}
```
例:デフォルト値が`null` であることは、入力データ`'processRefund' => null` で受け入れられることを意味しない。いいえ、入力はブーリアンでなければなりません。つまり、`true` または`false` だけです。`null` を`Expect::bool()->nullable()` 経由で明示的に許可しなければならないでしょう。
項目は`Expect::bool()->required()` を使って必須とすることができる。`Expect::bool()->default(false)` を使ってデフォルト値を`false` に変更したり、`Expect::bool(false)` を使ってデフォルト値を間もなく変更したりします。
そして、ブーリアン以外に`1` and `0` も受け入れたい場合はどうすればよいでしょうか。そこで、許容される値をリストアップし、これもbooleanに正規化することにします。
```php
$schema = Expect::structure([
'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
'refundAmount' => Expect::int(),
]);
$normalized = $processor->process($schema, $data);
is_bool($normalized->processRefund); // true
```
これでスキーマがどのように定義され、構造の個々の要素がどのように動作するかの基本がわかったと思います。これから、スキーマを定義する際に、他のすべての要素がどのように使用できるかを紹介します。
データ型:type() .[#toc-data-types-type]
-----------------------------------
PHP の標準的なデータ型は、すべてスキーマにリストアップすることができます。
```php
Expect::string($default = null)
Expect::int($default = null)
Expect::float($default = null)
Expect::bool($default = null)
Expect::null()
Expect::array($default = [])
```
そして、[Validators がサポートする |utils:en:validators#Expected Types]すべての型を`Expect::type('scalar')` あるいは`Expect::scalar()` のように省略した形式で指定します。また、クラス名やインターフェイス名も使用できます。例えば`Expect::type('AddressEntity')` のようになります。
また、ユニオン表記も使用できます。
```php
Expect::type('bool|string|array')
```
デフォルト値は,`array` と`list` を除いて,常に`null` で,これは空の配列である.(リストは,ゼロから数値キーの昇順でインデックスが付けられた配列,つまり非結合型配列です).
値の配列: arrayOf() listOf() .[#toc-array-of-values-arrayof-listof]
---------------------------------------------------------------
配列はあまりにも一般的な構造なので、どのような要素を含むことができるかを正確に指定する方が便利です。例えば、要素が文字列のみ可能な配列。
```php
$schema = Expect::arrayOf('string');
$processor->process($schema, ['hello', 'world']); // OK
$processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
$processor->process($schema, ['key' => 123]); // ERROR: 123 is not a string
```
2 番目のパラメータで、キーを指定することができます (バージョン 1.2 以降)。
```php
$schema = Expect::arrayOf('string', 'int');
$processor->process($schema, ['hello', 'world']); // OK
$processor->process($schema, ['a' => 'hello']); // ERROR: 'a' is not int
```
リストはインデックス付き配列となります。
```php
$schema = Expect::listOf('string');
$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 123]); // ERROR: 123 is not a string
$processor->process($schema, ['key' => 'a']); // ERROR: is not a list
$processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: is not a list
```
パラメータはスキーマでもよいので、次のように書きます。
```php
Expect::arrayOf(Expect::bool())
```
デフォルト値は空の配列です。default value を指定すると、渡されたデータにマージされます。これを無効にするには、`mergeDefaults(false)` (バージョン 1.1 以降) を使用します。
列挙:anyOf() .[#toc-enumeration-anyof]
------------------------------------
`anyOf()` は、ある値が取りうる値やスキーマの集合です。以下は、`'a'`,`true`,`null` のいずれかになりうる要素の配列の書き方です。
```php
$schema = Expect::listOf(
Expect::anyOf('a', true, null),
);
$processor->process($schema, ['a', true, null, 'a']); // OK
$processor->process($schema, ['a', false]); // ERROR: false does not belong there
```
列挙された要素はスキーマになることもできます。
```php
$schema = Expect::listOf(
Expect::anyOf(Expect::string(), true, null),
);
$processor->process($schema, ['foo', true, null, 'bar']); // OK
$processor->process($schema, [123]); // ERROR
```
`anyOf()` メソッドはバリアントを配列としてではなく、個々のパラメータとして受け取ります。値の配列を渡すには、アンパッキングオペレータ`anyOf(...$variants)` を使ってください。
デフォルト値は`null` です。最初の要素をデフォルトにするには、`firstIsDefault()` メソッドを使います。
```php
// default is 'hello'
Expect::anyOf(Expect::string('hello'), true, null)->firstIsDefault();
```
構造体 .[#toc-structures]
----------------------
構造体は、定義されたキーを持つオブジェクトである。このキー => 値のペアをそれぞれ「プロパティ」と呼びます。
構造体は配列やオブジェクトを受け入れ、オブジェクトを返す`stdClass` (`castTo('array')`, などで変更しない限り)。
デフォルトでは、すべてのプロパティはオプションであり、デフォルト値は`null` です。必須プロパティは`required()` を使って定義できます。
```php
$schema = Expect::structure([
'required' => Expect::string()->required(),
'optional' => Expect::string(), // the default value is null
]);
$processor->process($schema, ['optional' => '']);
// ERROR: option 'required' is missing
$processor->process($schema, ['required' => 'foo']);
// OK, returns {'required' => 'foo', 'optional' => null}
```
デフォルト値のみを持つプロパティを出力したくない場合は、`skipDefaults()` を使用します。
```php
$schema = Expect::structure([
'required' => Expect::string()->required(),
'optional' => Expect::string(),
])->skipDefaults();
$processor->process($schema, ['required' => 'foo']);
// OK, returns {'required' => 'foo'}
```
`null` は`optional` プロパティのデフォルト値であるが、入力データでは許されない(値は文字列でなければならない)。`null` を受け入れるプロパティは、`nullable()` を使って定義します。
```php
$schema = Expect::structure([
'optional' => Expect::string(),
'nullable' => Expect::string()->nullable(),
]);
$processor->process($schema, ['optional' => null]);
// ERROR: 'optional' expects to be string, null given.
$processor->process($schema, ['nullable' => null]);
// OK, returns {'optional' => null, 'nullable' => null}
```
デフォルトでは、入力データに余分な項目は存在できない。
```php
$schema = Expect::structure([
'key' => Expect::string(),
]);
$processor->process($schema, ['additional' => 1]);
// ERROR: Unexpected item 'additional'
```
これは`otherItems()` で変更可能です。パラメータとして、各追加要素のスキーマを指定します。
```php
$schema = Expect::structure([
'key' => Expect::string(),
])->otherItems(Expect::int());
$processor->process($schema, ['additional' => 1]); // OK
$processor->process($schema, ['additional' => true]); // ERROR
```
非推奨事項 .[#toc-deprecations]
--------------------------
非推奨のプロパティは `deprecated([string $message])`メソッドを使って非推奨にすることができます。非推奨の通知は`$processor->getWarnings()` で返されます。
```php
$schema = Expect::structure([
'old' => Expect::int()->deprecated('The item %path% is deprecated'),
]);
$processor->process($schema, ['old' => 1]); // OK
$processor->getWarnings(); // ["The item 'old' is deprecated"]
```
範囲: min() max() .[#toc-ranges-min-max]
--------------------------------------
`min()` と`max()` を使って、配列の要素数を制限します。
```php
// array, at least 10 items, maximum 20 items
Expect::array()->min(10)->max(20);
```
文字列の場合は、その長さを制限する。
```php
// string, at least 10 characters long, maximum 20 characters
Expect::string()->min(10)->max(20);
```
数値の場合は、値を制限する。
```php
// integer, between 10 and 20 inclusive
Expect::int()->min(10)->max(20);
```
もちろん、`min()` だけ、あるいは`max()` だけに言及することも可能です。
```php
// string, maximum 20 characters
Expect::string()->max(20);
```
正規表現: pattern() .[#toc-regular-expressions-pattern]
---------------------------------------------------
`pattern()` を使うと、入力文字列の **全体** にマッチしなければならない正規表現を指定できます (つまり、入力文字列が`^` a `$` という文字でくくられたようなものです)。
```php
// just 9 digits
Expect::string()->pattern('\d{9}');
```
カスタムアサーション: assert() .[#toc-custom-assertions-assert]
-----------------------------------------------------
`assert(callable $fn)` を使って、その他の制限を追加することができます。
```php
$countIsEven = fn($v) => count($v) % 2 === 0;
$schema = Expect::arrayOf('string')
->assert($countIsEven); // the count must be even
$processor->process($schema, ['a', 'b']); // OK
$processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 is not even
```
または
```php
Expect::string()->assert('is_file'); // the file must exist
```
各アサーションに独自の説明を追加することができます。これはエラーメッセージの一部となります。
```php
$schema = Expect::arrayOf('string')
->assert($countIsEven, 'Even items in array');
$processor->process($schema, ['a', 'b', 'c']);
// Failed assertion "Even items in array" for item with value array.
```
このメソッドは、複数の制約を追加するために繰り返し呼び出すことができる。これは、`transform()` と`castTo()` の呼び出しと混在させることができます。
変換: transform() .[#toc-transformation-transform]
------------------------------------------------
バリデーションに成功したデータは、カスタム関数を使用して変更することができます:
```php
// conversion to uppercase:
Expect::string()->transform(fn(string $s) => strtoupper($s));
```
このメソッドを繰り返し呼び出すことで、複数の変換を加えることができる。このメソッドは、`assert()` や`castTo()` の呼び出しと混在させることができる。操作は宣言された順番に実行される:
```php
Expect::type('string|int')
->castTo('string')
->assert('ctype_lower', 'All characters must be lowercased')
->transform(fn(string $s) => strtoupper($s)); // conversion to uppercase
```
`transform()` メソッドは、値の変換と検証を同時に行うことができる。これは、`transform()` と`assert()` を連鎖させるよりも単純で冗長性が少ないことが多い。この目的のために、この関数は`addError()` メソッドを持つ[Context |api:Nette\Schema\Context]オブジェクトを受け取ります:
```php
Expect::string()
->transform(function (string $s, Nette\Schema\Context $context) {
if (!ctype_lower($s)) {
$context->addError('All characters must be lowercased', 'my.case.error');
return null;
}
return strtoupper($s);
});
```
キャスト: castTo() .[#toc-casting-castto]
-------------------------------------
バリデーションに成功したデータをキャストすることができます:
```php
Expect::scalar()->castTo('string');
```
PHP ネイティブの型に加えて、クラスへのキャストも可能です。これは、コンストラクタのない単純なクラスなのかコンストラクタのあるクラスなのかを区別します。コンストラクタのないクラスの場合は、そのインスタンスが作成され、 構造体のすべての要素がそのプロパティに書き込まれます:
```php
class Info
{
public bool $processRefund;
public int $refundAmount;
}
Expect::structure([
'processRefund' => Expect::bool(),
'refundAmount' => Expect::int(),
])->castTo(Info::class);
// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount
```
コンストラクタを持つクラスの場合、構造体の要素は名前付きパラメータとしてコンストラクタに渡されます:
```php
class Info
{
public function __construct(
public bool $processRefund,
public int $refundAmount,
) {
}
}
// creates $obj = new Info(processRefund: ..., refundAmount: ...)
```
キャストとスカラー・パラメータを組み合わせると、オブジェクトが生成され、その値が唯一のパラメータとしてコンストラクタに渡されます:
```php
Expect::string()->castTo(DateTime::class);
// creates new DateTime(...)
```
正規化: before() .[#toc-normalization-before]
------------------------------------------
バリデーションの前に、`before()` メソッドを使用してデータを正規化することができます。例として、文字列の配列でなければならない要素(例:³³)があるとします。 `['a', 'b', 'c']`が、文字列`a b c` という形式で入力を受け取るとします。
```php
$explode = fn($v) => explode(' ', $v);
$schema = Expect::arrayOf('string')
->before($explode);
$normalized = $processor->process($schema, 'a b c');
// OK, returns ['a', 'b', 'c']
```
オブジェクトへのマッピング: from() .[#toc-mapping-to-objects-from]
-----------------------------------------------------
クラスから構造スキーマを生成することができます。例
```php
class Config
{
public string $name;
public string|null $password;
public bool $admin = false;
}
$schema = Expect::from(new Config);
$data = [
'name' => 'jeff',
];
$normalized = $processor->process($schema, $data);
// $normalized instanceof Config
// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}
```
匿名クラスもサポートされています:
```php
$schema = Expect::from(new class {
public string $name;
public ?string $password;
public bool $admin = false;
});
```
クラス定義から得られる情報だけでは十分でない可能性があるため、2番目のパラメータで要素のカスタム・スキーマを追加できます:
```php
$schema = Expect::from(new Config, [
'name' => Expect::string()->pattern('\w:.*'),
]);
```
{{leftbar: nette:@menu-topics}}