เน็ตเต้ สคีมา

ไลบรารีที่ใช้งานได้จริงสำหรับการตรวจสอบความถูกต้องและการทำให้โครงสร้างข้อมูลเป็นมาตรฐานเทียบกับสคีมาที่กำหนดด้วย API ที่ชาญฉลาดและเข้าใจง่าย
เอกสารสามารถพบได้บนเว็บไซต์
การติดตั้ง:
composer require nette/schemaต้องใช้ PHP เวอร์ชัน 8.1 และรองรับ PHP สูงสุด 8.4
คุณชอบ Nette Schema ไหม? คุณกำลังรอคอยคุณสมบัติใหม่หรือไม่?
ขอบคุณ!
ในตัวแปร $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 ใช้เพื่อกำหนด จริงๆ แล้วเรากำหนดความคาดหวังว่าข้อมูลควรมีลักษณะอย่างไร สมมติว่าข้อมูลที่ป้อนต้องเป็นโครงสร้าง (เช่นอาร์เรย์) ที่มีองค์ประกอบ processRefund ประเภทบูลและ refundAmount ประเภท int
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 เท่านั้น เราจะต้องอนุญาตอย่างชัดเจนให้ null ผ่าน Expect::bool()->nullable()
รายการสามารถบังคับได้โดยใช้ Expect::bool()->required() เราเปลี่ยนค่าเริ่มต้นเป็น false โดยใช้ Expect::bool()->default(false) หรือใช้ Expect::bool(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 ' ) ค่าเริ่มต้นจะเป็น null เสมอ ยกเว้น array และ list โดยที่เป็นอาร์เรย์ว่าง (รายการคืออาร์เรย์ที่จัดทำดัชนีโดยเรียงคีย์ตัวเลขจากศูนย์จากน้อยไปมาก นั่นคืออาร์เรย์ที่ไม่เชื่อมโยง)
อาร์เรย์มีโครงสร้างที่กว้างเกินไป จะมีประโยชน์มากกว่าในการระบุว่าองค์ประกอบใดบ้างที่สามารถมีได้ ตัวอย่างเช่น อาร์เรย์ที่องค์ประกอบสามารถเป็นสตริงได้เท่านั้น:
$ 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พารามิเตอร์ตัวที่สองสามารถใช้เพื่อระบุคีย์ (ตั้งแต่เวอร์ชัน 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() เพื่อจุดประสงค์นี้ ฟังก์ชันจะได้รับวัตถุ NetteSchemaContext พร้อมด้วยเมธอด addError() ซึ่งสามารถใช้เพื่อเพิ่มข้อมูลเกี่ยวกับปัญหาการตรวจสอบได้:
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 ดั้งเดิมแล้ว คุณยังสามารถส่งไปยังคลาสได้อีกด้วย มันแยกความแตกต่างว่าเป็นคลาสธรรมดาที่ไม่มีคอนสตรัคเตอร์หรือคลาสที่มีคอนสตรัคเตอร์ หากคลาสไม่มี Constructor จะมีการสร้างอินสแตนซ์ขึ้นมาและองค์ประกอบทั้งหมดของโครงสร้างจะถูกเขียนไปยังคุณสมบัติ:
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 ;
});เนื่องจากข้อมูลที่ได้รับจากคำจำกัดความของคลาสอาจไม่เพียงพอ คุณจึงเพิ่มสคีมาที่กำหนดเองสำหรับองค์ประกอบด้วยพารามิเตอร์ตัวที่สองได้:
$ schema = Expect:: from ( new Config , [
' name ' => Expect:: string ()-> pattern ( ' w:.* ' ),
]);