ネットスキーマ
スマートでわかりやすい API を使用して、特定のスキーマに対してデータ構造を検証および正規化するための実用的なライブラリです。
ドキュメントは Web サイトでご覧いただけます。
インストール:
composer require nette/schema
PHP バージョン 8.1 が必要で、8.4 までの PHP をサポートします。
ネットスキーマが好きですか?新しい機能を楽しみにしていますか?
ありがとう!
変数$schema
には検証スキーマ (これが何を意味するのか、その作成方法は後で説明します) があり、変数$data
には検証および正規化するデータ構造があります。これは、たとえば、API や構成ファイルなどを通じてユーザーによって送信されたデータです。
このタスクは NetteSchemaProcessor クラスによって処理され、入力を処理して正規化されたデータを返すか、エラー時に NetteSchemaValidationException 例外をスローします。
$ processor = new Nette Schema Processor ;
try {
$ normalized = $ processor -> process ( $ schema , $ data );
} catch ( Nette Schema ValidationException $ e ) {
echo ' Data is invalid: ' . $ e -> getMessage ();
}
メソッド$e->getMessages()
はすべてのメッセージ文字列の配列を返し、 $e->getMessageObjects()
すべてのメッセージを NetteSchemaMessage オブジェクトとして返します。
それでは、スキーマを作成しましょう。 NetteSchemaExpect クラスを使用して定義します。実際には、データがどのように見えるかという期待値を定義します。入力データは、bool 型のprocessRefund
要素と int 型のrefundAmount
要素を含む構造体 (配列など) でなければならないとします。
use Nette Schema Expect ;
$ schema = Expect:: structure ([
' processRefund ' => Expect:: bool (),
' refundAmount ' => Expect:: int (),
]);
初めて見た場合でも、スキーマ定義は明確に見えると思います。
検証のために次のデータを送信してみましょう。
$ data = [
' processRefund ' => true ,
' refundAmount ' => 17 ,
];
$ normalized = $ processor -> process ( $ schema , $ data ); // OK, it passes
出力、つまり値$normalized
はオブジェクトstdClass
です。出力を配列にしたい場合は、スキーマExpect::structure([...])->castTo('array')
にキャストを追加します。
構造体のすべての要素はオプションであり、デフォルト値はnull
です。例:
$ data = [
' refundAmount ' => 17 ,
];
$ normalized = $ processor -> process ( $ schema , $ data ); // OK, it passes
// $normalized = {'processRefund' => null, 'refundAmount' => 17}
デフォルト値がnull
であるという事実は、それが入力データ'processRefund' => null
で受け入れられることを意味するものではありません。いいえ、入力はブール値、つまりtrue
またはfalse
のみである必要があります。 Expect::bool()->nullable()
を介して明示的にnull
を許可する必要があります。
Expect::bool()->required()
を使用して項目を必須にすることができます。 Expect::bool()->default(false)
を使用するか、すぐにExpect::bool(false)
を使用して、デフォルト値をfalse
に変更します。
ブール値以外に1
と0
受け入れたい場合はどうすればよいでしょうか?次に、許可される値をリストします。これもブール値に正規化します。
$ 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
これで、スキーマがどのように定義され、構造の個々の要素がどのように動作するかについての基本を理解できました。次に、スキーマの定義に他のすべての要素を使用できることを示します。
すべての標準 PHP データ型をスキーマにリストできます。
Expect:: string ( $ default = null )
Expect:: int ( $ default = null )
Expect:: float ( $ default = null )
Expect:: bool ( $ default = null )
Expect::null()
Expect:: array ( $ default = [])
そして、 Expect::type('scalar')
または省略形のExpect::scalar()
を介してバリデータによってサポートされるすべての型。また、クラス名またはインターフェイス名も受け入れられます (例: Expect::type('AddressEntity')
。
共用体表記も使用できます。
Expect:: type ( ' bool|string|array ' )
デフォルト値は、空の配列であるarray
とlist
を除き、常にnull
です。 (リストは、0 から数値キーの昇順にインデックスが付けられた配列、つまり非連想配列です)。
配列は一般的な構造なので、どの要素を含めることができるかを正確に指定する方が便利です。たとえば、要素が文字列のみである配列の場合は次のようになります。
$ 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 以降)。
$ schema = Expect:: arrayOf ( ' string ' , ' int ' );
$ processor -> process ( $ schema , [ ' hello ' , ' world ' ]); // OK
$ processor -> process ( $ schema , [ ' a ' => ' hello ' ]); // ERROR: 'a' is not int
リストはインデックス付き配列です。
$ 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
パラメーターはスキーマにすることもできるため、次のように記述できます。
Expect:: arrayOf (Expect:: bool ())
デフォルト値は空の配列です。デフォルト値を指定してmergeDefaults()
呼び出すと、渡されたデータとマージされます。
anyOf()
値または値が取り得るスキーマのセットです。 'a'
、 true
、またはnull
のいずれかになる要素の配列を記述する方法は次のとおりです。
$ 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
列挙要素はスキーマにすることもできます。
$ 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()
メソッドを使用して、最初の要素をデフォルトにします。
// default is 'hello'
Expect:: anyOf (Expect:: string ( ' hello ' ), true , null )-> firstIsDefault ();
構造体は、定義されたキーを持つオブジェクトです。これらのキー => 値のペアはそれぞれ「プロパティ」と呼ばれます。
構造体は配列とオブジェクトを受け入れ、オブジェクトstdClass
を返します ( castTo('array')
などで変更しない限り)。
デフォルトでは、すべてのプロパティはオプションであり、デフォルト値はnull
です。 required()
使用して必須プロパティを定義できます。
$ 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()
を使用します。
$ schema = Expect:: structure ([
' required ' => Expect:: string ()-> required (),
' optional ' => Expect:: string (),
])-> skipDefaults ();
$ processor -> process ( $ schema , [ ' required ' => ' foo ' ]);
// OK, returns {'required' => 'foo'}
null
はoptional
プロパティのデフォルト値ですが、入力データでは使用できません (値は文字列である必要があります)。 null
受け入れるプロパティは、 nullable()
を使用して定義されます。
$ 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}
デフォルトでは、入力データに余分な項目を含めることはできません。
$ schema = Expect:: structure ([
' key ' => Expect:: string (),
]);
$ processor -> process ( $ schema , [ ' additional ' => 1 ]);
// ERROR: Unexpected item 'additional'
これはotherItems()
で変更できます。パラメータとして、各追加要素のスキーマを指定します。
$ schema = Expect:: structure ([
' key ' => Expect:: string (),
])-> otherItems (Expect:: int ());
$ processor -> process ( $ schema , [ ' additional ' => 1 ]); // OK
$ processor -> process ( $ schema , [ ' additional ' => true ]); // ERROR
deprecated([string $message])
メソッドを使用してプロパティを非推奨にすることができます。非推奨通知は$processor->getWarnings()
によって返されます。
$ 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()
を使用して、配列の要素数を制限します。
// array, at least 10 items, maximum 20 items
Expect:: array ()-> min ( 10 )-> max ( 20 );
文字列の場合は、長さを制限します。
// string, at least 10 characters long, maximum 20 characters
Expect:: string ()-> min ( 10 )-> max ( 20 );
数値の場合は、その値を制限します。
// integer, between 10 and 20 inclusive
Expect:: int ()-> min ( 10 )-> max ( 20 );
もちろん、 min()
のみ、またはmax()
のみを指定することも可能です。
// string, maximum 20 characters
Expect:: string ()-> max ( 20 );
pattern()
を使用すると、入力文字列全体が一致する必要がある正規表現を指定できます (つまり、文字^
a $
でラップされているかのように)。
// just 9 digits
Expect:: string ()-> pattern ( ' d{9} ' );
assert(callable $fn)
を使用して他の制限を追加できます。
$ 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
または
Expect:: string ()-> assert ( ' is_file ' ); // the file must exist
各アサーションに独自の説明を追加できます。これはエラー メッセージの一部になります。
$ 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()
の呼び出しと混在させることができます。
正常に検証されたデータは、カスタム関数を使用して変更できます。
// conversion to uppercase:
Expect:: string ()-> transform ( fn ( string $ s ) => strtoupper ( $ s ));
このメソッドを繰り返し呼び出して、複数の変換を追加できます。これは、 assert()
およびcastTo()
の呼び出しと混在させることができます。操作は宣言された順序で実行されます。
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()
メソッドで NetteSchemaContext オブジェクトを受け取ります。これを使用して検証の問題に関する情報を追加できます。
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 );
});
正常に検証されたデータはキャストできます。
Expect:: scalar ()-> castTo ( ' string ' );
ネイティブ 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
クラスにコンストラクターがある場合、構造体の要素は名前付きパラメーターとしてコンストラクターに渡されます。
class Info
{
public function __construct (
public bool $ processRefund ,
public int $ refundAmount ,
) {
}
}
// creates $obj = new Info(processRefund: ..., refundAmount: ...)
スカラー パラメーターと組み合わせたキャストによりオブジェクトが作成され、その値が唯一のパラメーターとしてコンストラクターに渡されます。
Expect:: string ()-> castTo (DateTime::class);
// creates new DateTime(...)
検証自体の前に、 before()
メソッドを使用してデータを正規化できます。例として、文字列の配列 (例['a', 'b', 'c']
) である必要があるが、文字列abc
の形式で入力を受け取る要素があるとします。
$ explode = fn ( $ v ) => explode ( ' ' , $ v );
$ schema = Expect:: arrayOf ( ' string ' )
-> before ( $ explode );
$ normalized = $ processor -> process ( $ schema , ' a b c ' );
// OK, returns ['a', 'b', 'c']
クラスから構造スキーマを生成できます。例:
class Config
{
public string $ name ;
public ? string $ 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}
匿名クラスもサポートされています。
$ schema = Expect:: from ( new class {
public string $ name ;
public ? string $ password ;
public bool $ admin = false ;
});
クラス定義から取得した情報では十分ではない可能性があるため、2 番目のパラメーターを使用して要素のカスタム スキーマを追加できます。
$ schema = Expect:: from ( new Config , [
' name ' => Expect:: string ()-> pattern ( ' w:.* ' ),
]);