驗證(Validation)
驗證(Validation )是在Web程式中極為常見的任務。表單中輸入的資料需要驗證。寫入到資料庫或傳送到Web服務時,資料也需要驗證。
Symfony自帶了一個 Validator 元件,它讓校驗工作變得簡單和透明。此元件基於 JSR303 Bean校驗規範。
驗證的基礎 ¶
理解驗證的最好方法就是看它的實際應用。在開始之前,假設你創建了一個原生php對象,它用在程式需要的某個地方:
// src/AppBundle/Entity/Author.phpnamespace AppBundle\Entity; class Author{ public $name;}
目前為止,這只是一個為你的程式提供某些用途的普通類別。驗證的目的就是要告訴你,物件中的資料是否有效。為此,你需要配置一個規則清單(稱為 constraints/約束 ),物件必須遵循之方能有效。這些規則可以透過多種不同的格式(YAML、XML、annotations或PHP)來指定。
例如,要保證屬性$name
不為空,加入以下內容:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints\NotBlank; class Author{ public $name; public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('name', new NotBlank()); }}
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8" ?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Author"> <property name="name"> <constraint name="NotBlank" /> </property> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: name: - NotBlank: ~
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\NotBlank() */ public $name;}
# Protected和private屬性以及「getter」方法也可以被驗證(請參閱約束的投放範圍)。
使用驗證服務 ¶
接下來,要真正的校驗Author
對象,使用validator
服務(Validator
類別)的validate
方法。 validator
的工作很簡單:讀取一個類別的約束規則來校驗物件資料是否滿足這些約束。如果驗證失敗,一個非空的錯誤清單(ConstraintViolationList
類別)將會傳回。在控制器中實作這個簡單範例:
// ...use Symfony\Component\HttpFoundation\Response;use AppBundle\Entity\Author; // ...public function authorAction(){ $author = new Author(); // ... do something to the $author object // ... 对 $author 对象做一些事 $validator = $this->get('validator'); $errors = $validator->validate($author); if (count($errors) > 0) { /* * Uses a __toString method on the $errors variable which is a * ConstraintViolationList object. This gives us a nice string * for debugging. * 对 $errors 变量,即 ConstraintViolationList 对象,使用 __toString 方法。 * 这给了我们一个美观的字符串用于调试。 */ $errorsString = (string) $errors; return new Response($errorsString); } return new Response('The author is valid! Yes!');}
如果$name
屬性是空的,你會看到一個錯誤訊息:
1 2 | # AppBundle\Author.name: This value should not be blank |
如果你為 name
屬性插入一個值,令人高興的成功訊息就會出現。
多數時候,你不需要直接跟 validator
服務互動,或不必為輸出錯誤訊息擔心。多數情況下,你在處理提交過來的表單資料時,間接地使用validation驗證。參考 驗證與表單 以了解更多。
你也可以傳遞「錯誤訊息集合」(collection of errors)到模版中:
if (count($errors) > 0) { return $this->render('author/validation.html.twig', array( 'errors' => $errors, ));}
在模版中,你可以根據需要精確輸出錯誤清單:
PHP:<!-- app/Resources/views/author/validation.html.php --> <h3>The author has the following errors</h3> <ul><?php foreach ($errors as $error): ?> <li><?php echo $error->getMessage() ?></li><?php endforeach ?></ul>
Twig:{# app/Resources/views/author/validation.html.twig #}<h3>The author has the following errors</h3> <ul>{% for error in errors %} <li>{{ error.message }}</li>{% endfor %}</ul>
#每一個驗證錯誤(被稱為「constraint violation/約束違反」)都由一個ConstraintViolation
物件來呈現。
設定 ¶
Symfony的validator是預設開啟的,但如果你使用annotation方式來指定約束,必須明確開啟(用於驗證的)annotation:
PHP:// app/config/config.php$container->loadFromExtension('framework', array( 'validation' => array( 'enable_annotations' => true, ),));
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:validation enable-annotations="true" /> </framework:config></container>
YAML:# app/config/config.ymlframework: validation: { enable_annotations: true }
約束規則 ¶
Validator
被設計成針對 約束(即規則)來驗證物件。要驗證一個對象,只需把一或多個限制映射到它要驗證的類,然後再把它傳遞給 validator
服務。
在幕後,一個限制條件就是一個PHP對象,它可以產生一個用於決斷的聲明。在實踐中,一個約束可以是「蛋糕不能燒」。在Symfony中,約束是類似的:它們就是「條件是否為真」的斷言。給定一個值,約束會告訴你這個值是否遵守了你的約束規則。
支援的約束 ¶
Symfony封裝了許多最常用的限制:
基本約束 ¶
###################################################### ##這些是基本的限制:使用它們來斷言屬性值相關的非常基礎的東西,或斷言你程式中的方法之傳回值。 ###IsNull
- IsTrue
- ##IsFalse
- Type
字串約束
- ################### ###########NotEqualTo###################IdenticalTo###################NotIdenticalTo ##################LessThan###################LessThanOrEqual############# ######GreaterThan##################GreaterThanOrEqual##############日期約束 ###¶### ###############Date###################DateTime########
- Time
Collection約束 ¶
檔案限制¶
Image
#Iban
##All######UserPassword##################Valid########################Valid###### #########你也可以建立自己的自訂限制。 ###如何建立自訂的驗證約束### 一文覆寫了此主題。 ###約束的配置 ¶
一些約束,像是NotBlank 較為簡單,而其他一些像是 Choice 約束,有許多可用的配置選項。假設Author
類別有另外一個屬性叫gender
,該屬性可以設定為「male」、「female」 或「other」:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ public $gender; // ... public static function loadValidatorMetadata(ClassMetadata $metadata) { // ... $metadata->addPropertyConstraint('gender', new Assert\Choice(array( 'choices' => array('male', 'female', 'other'), 'message' => 'Choose a valid gender.', ))); }}
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8" ?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Author"> <property name="gender"> <constraint name="Choice"> <option name="choices"> <value>male</value> <value>female</value> <value>other</value> </option> <option name="message">Choose a valid gender.</option> </constraint> </property> <!-- ... --> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: gender: - Choice: { choices: [male, female, other], message: Choose a valid gender. } # ...
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\Choice( * choices = { "male", "female", "other" }, * message = "Choose a valid gender." * ) */ public $gender; // ...}
約束的選項始終可以透過一個陣列來傳遞的。有些約束也允許你傳入一個 ”default” 選項的值來取代這個陣列。在 Choice
限制條件中,choices
選項就可以透過這種方式指定。
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ protected $gender; public static function loadValidatorMetadata(ClassMetadata $metadata) { // ... $metadata->addPropertyConstraint( 'gender', new Assert\Choice(array('male', 'female', 'other')) ); }}
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8" ?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Author"> <property name="gender"> <constraint name="Choice"> <value>male</value> <value>female</value> <value>other</value> </constraint> </property> <!-- ... --> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: gender: - Choice: [male, female, other] # ...
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\Choice({"male", "female", "other"}) */ protected $gender; // ...}
這純粹是為了讓最常見的配置選項在用起來時更加的簡單快速。
如果你不確定如何指定一個選項,要嘛去查看API文檔,要嘛為了保險起見,透過一個選項數組來傳入(即上面第一種方式)。
約束的目標 ¶
限制可以被應用到類別的屬性(如name
)或一個公共的getter方法(如getFullName
)甚至整個類別上。屬性約束最常用也最簡單,而Getter約束則允許你指定更複雜的驗證規則。最後,如果,類別約束的使用場景是,你要將類別作為整體進行驗證。
屬性約束 ¶
類別屬性的驗證是一個最基本的驗證技巧。 Symfony允許你校驗 private, protected 或 public 屬性。下面程式碼展示如何配置 Author
物件的 $firstName
屬性,令其至少有3個字元:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ private $firstName; public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('firstName', new Assert\NotBlank()); $metadata->addPropertyConstraint( 'firstName', new Assert\Length(array("min" => 3)) ); }}
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8" ?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Author"> <property name="firstName"> <constraint name="NotBlank" /> <constraint name="Length"> <option name="min">3</option> </constraint> </property> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: firstName: - NotBlank: ~ - Length: min: 3
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\NotBlank() * @Assert\Length(min=3) */ private $firstName;}
Getters約束 ¶
約束也可以套用於方法的回傳值。 Symfony允許你把一個限制加到任何「get」、「is」 或 「has」開頭的public方法。這一類方法被稱為“getters”。
這種技巧的好處是允許你動態驗證你的物件。例如,假設你要確保密碼欄位不能符合到使用者的firstname(出於安全原因)。你可以透過建立一個isPasswordLegal
方法,然後斷言該方法必須回傳true
# 來實作:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addGetterConstraint('passwordLegal', new Assert\IsTrue(array( 'message' => 'The password cannot match your first name', ))); }}
XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8" ?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="AppBundle\Entity\Author"> <getter property="passwordLegal"> <constraint name="IsTrue"> <option name="message">The password cannot match your first name</option> </constraint> </getter> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: getters: passwordLegal: - 'IsTrue': { message: 'The password cannot match your first name' }
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\IsTrue(message = "The password cannot match your first name") */ public function isPasswordLegal() { // ... return true or false }}
現在,建立一個isPasswordLegal()
方法,含有你所需的邏輯:
public function isPasswordLegal(){ return $this->firstName !== $this->password;}
眼尖的人可能會注意到,在YAML, XML和PHP的約束配置格式中,getter的前綴(“get”、”is” 或者“has”) 在映射時被忽略了。這能讓你在不改變驗證邏輯的前提下,把一個約束移到後面的一個同名屬性之上(反之亦然)。
類別約束 ¶
#有一些約束可以應用到被驗證的整個類別。例如,Callback 類型的約束,就是一個可以作用到類別本身的通用約束。當類別被驗證時,約束所指定的方法將直接執行,以便提供更多的自訂驗證。
總結 ¶
Symfony的validator
(驗證)是一個強大的工具,它可以用來保證任何物件資料的合法性。它的強大來源自約束規則(constraints),你可以把它應用到物件的屬性和getter方法上。雖然,多數情況下都是在使用表單時間接應用了驗證框架,記得,它可以用在任何地方去驗證任何物件。