驗證
驗證在網路應用程式中是非常常見的工作。表單中輸入的資料需要驗證。資料在寫入資料庫或傳遞到網路服務之前也需要驗證。
Symfony 提供了一個 Validator 元件來為您處理這個問題。此元件基於 JSR303 Bean Validation 規範。
安裝
在使用 Symfony Flex 的應用程式中,請執行此命令以安裝驗證器後再使用
1
$ composer require symfony/validator
注意
如果您的應用程式未使用 Symfony Flex,您可能需要進行一些手動設定以啟用驗證。請查看驗證設定參考。
驗證基礎
理解驗證的最佳方法是實際操作。首先,假設您建立了一個普通的 PHP 物件,您需要在應用程式的某處使用它
1 2 3 4 5 6 7
// src/Entity/Author.php
namespace App\Entity;
class Author
{
private string $name;
}
到目前為止,這是一個普通的類別,在您的應用程式中具有某些用途。驗證的目標是告訴您物件的資料是否有效。為此,您將設定物件必須遵循的一系列規則(稱為約束)才能有效。這些規則通常使用 PHP 程式碼或屬性定義,但也可以在 config/validator/
目錄中定義為 .yaml
或 .xml
檔案
例如,要指示 $name
屬性不得為空,請新增以下內容
1 2 3 4 5 6 7 8 9 10 11
// src/Entity/Author.php
namespace App\Entity;
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
#[Assert\NotBlank]
private string $name;
}
單獨新增此設定本身並不能保證該值不會為空白;如果您願意,您仍然可以將其設定為空白值。為了真正保證該值符合約束,必須將物件傳遞給驗證器服務進行檢查。
提示
Symfony 的驗證器使用 PHP 反射以及"getter" 方法來取得任何屬性的值,因此它們可以是 public、private 或 protected(請參閱驗證)。
使用驗證器服務
接下來,要實際驗證 Author
物件,請使用驗證器服務(實作 ValidatorInterface)上的 validate()
方法。驗證器的工作是讀取類別的約束(即規則),並驗證物件上的資料是否滿足這些約束。如果驗證失敗,則會傳回非空的錯誤列表(ConstraintViolationList 類別)。以下是從控制器內部取得的簡單範例
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
// ...
use App\Entity\Author;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;
// ...
public function author(ValidatorInterface $validator): Response
{
$author = new Author();
// ... do something to the $author object
$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.
*/
$errorsString = (string) $errors;
return new Response($errorsString);
}
return new Response('The author is valid! Yes!');
}
如果 $name
屬性為空,您將看到以下錯誤訊息
1 2
Object(App\Entity\Author).name:
This value should not be blank.
如果您在 name
屬性中插入一個值,則會出現成功的訊息。
提示
在大多數情況下,您不會直接與 validator
服務互動,也不需要擔心列印錯誤。在大多數情況下,您將在處理提交的表單資料時間接使用驗證。如需更多資訊,請參閱如何驗證 Symfony 表單。
您也可以將錯誤集合傳遞到範本中
1 2 3 4 5
if (count($errors) > 0) {
return $this->render('author/validation.html.twig', [
'errors' => $errors,
]);
}
在範本中,您可以根據需要輸出錯誤列表
1 2 3 4 5 6 7
{# templates/author/validation.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
注意
每個驗證錯誤(稱為「約束違規」)都由 ConstraintViolation 物件表示。此物件允許您(除其他外)透過 ConstraintViolation::getConstraint()
方法取得導致此違規的約束。
驗證 Callable
Validation
也允許您建立一個閉包,以針對一組約束驗證值(例如,在驗證 Console 命令答案或驗證 OptionsResolver 值時很有用)
createCallable()
- 當約束不符時,這會傳回一個拋出
ValidationFailedException
的閉包。 createIsValidCallable()
- 當約束不符時,這會傳回一個傳回
false
的閉包。
約束
validator
的設計目的是針對約束(即規則)驗證物件。為了驗證物件,只需將一個或多個約束對應到其類別,然後將其傳遞給 validator
服務。
在幕後,約束只是一個 PHP 物件,它會做出斷言陳述。在現實生活中,約束可能是:「蛋糕不得燒焦」。在 Symfony 中,約束是相似的:它們是對條件為真的斷言。給定一個值,約束會告訴您該值是否符合約束的規則。
支援的約束
Symfony 打包了許多最常用的約束
其他約束
- All
- AtLeastOneOf
- Callback
- Cascade
- Collection
- Compound
- Count
- Expression
- GroupSequence
- Sequentially
- Traverse
- Valid
- When
您也可以建立自己的自訂約束。本主題在〈如何建立自訂驗證約束〉文章中介紹。
約束設定
有些約束(例如 NotBlank)很簡單,而另一些約束(例如 Choice 約束)則有幾個可用的設定選項。假設 Author
類別還有另一個名為 genre
的屬性,用於定義主要與作者相關聯的文學類型,可以設定為「小說」或「非小說」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Entity/Author.php
namespace App\Entity;
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
#[Assert\Choice(
choices: ['fiction', 'non-fiction'],
message: 'Choose a valid genre.',
)]
private string $genre;
// ...
}
約束的選項始終可以作為陣列傳遞。但是,某些約束也允許您傳遞一個「預設」選項的值來代替陣列。在 Choice
約束的情況下,可以透過這種方式指定 choices
選項。
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Entity/Author.php
namespace App\Entity;
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
#[Assert\Choice(['fiction', 'non-fiction'])]
private string $genre;
// ...
}
這純粹是為了使約束最常見選項的設定更簡短和快速。
如果您不確定如何指定選項,請檢查 Symfony\Component\Validator\Constraints
命名空間中的約束,或始終傳遞選項陣列(如上所示的第一種方法)以確保安全。
表單類別中的約束
可以在建構表單時透過表單欄位的 constraints
選項定義約束
1 2 3 4 5 6 7 8 9
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('myField', TextType::class, [
'required' => true,
'constraints' => [new Length(['min' => 3])],
])
;
}
約束目標
約束可以應用於類別屬性(例如 name
)、getter 方法(例如 getFullName()
)或整個類別。屬性約束是最常見且易於使用的。Getter 約束允許您指定更複雜的驗證規則。最後,類別約束適用於您要驗證整個類別的情況。
屬性
驗證類別屬性是最基本的驗證技術。Symfony 允許您驗證 private、protected 或 public 屬性。以下列表顯示如何設定 Author
類別的 $firstName
屬性至少包含 3 個字元。
1 2 3 4 5 6 7 8 9 10 11
// src/Entity/Author.php
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
#[Assert\NotBlank]
#[Assert\Length(min: 3)]
private string $firstName;
}
警告
如果類型化的屬性未初始化,驗證器將使用值 null
。如果屬性在初始化時包含值,這可能會導致意外行為。為了避免這種情況,請確保在驗證屬性之前初始化所有屬性。
Getter
約束也可以應用於方法的傳回值。Symfony 允許您將約束新增到任何名稱以 "get"、"is" 或 "has" 開頭的 private、protected 或 public 方法。在本指南中,這些類型的方法稱為「getter」。
此技術的好處是它允許您動態驗證物件。例如,假設您要確保密碼欄位與使用者的名字不符(基於安全原因)。您可以透過建立 isPasswordSafe()
方法,然後斷言此方法必須傳回 true
來完成此操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Entity/Author.php
namespace App\Entity;
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
#[Assert\IsTrue(message: 'The password cannot match your first name')]
public function isPasswordSafe(): bool
{
// ... return true or false
}
}
現在,建立 isPasswordSafe()
方法並包含您需要的邏輯
1 2 3 4
public function isPasswordSafe(): bool
{
return $this->firstName !== $this->password;
}
注意
眼尖的人會注意到,在 YAML、XML 和 PHP 格式的對應中,getter 的前綴(「get」、「is」或「has」)被省略了。這允許您稍後將約束移動到具有相同名稱的屬性(或反之亦然),而無需變更您的驗證邏輯。
驗證具有繼承的物件
當您驗證擴展另一個類別的物件時,驗證器也會自動驗證在父類別中定義的約束。
即使子屬性覆寫了父屬性中定義的約束,父屬性中定義的約束也將應用於子屬性。Symfony 將始終合併每個屬性的父約束。
您無法變更此行為,但您可以透過在不同的驗證群組中定義父約束和子約束,然後在驗證每個物件時選擇適當的群組來克服它。
偵錯約束
使用 debug:validator
命令列出給定類別的驗證約束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
$ php bin/console debug:validator 'App\Entity\SomeClass'
App\Entity\SomeClass
-----------------------------------------------------
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
| Property | Name | Groups | Options |
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
| | | | "message" => "This value should not be blank.", |
| | | | "allowNull" => false, |
| | | | "normalizer" => null, |
| | | | "payload" => null |
| | | | ] |
| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
| | | | "message" => "This value is not a valid email address.", |
| | | | "mode" => null, |
| | | | "normalizer" => null, |
| | | | "payload" => null |
| | | | ] |
+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
您也可以驗證儲存在給定目錄中的所有類別
1
$ php bin/console debug:validator src/Entity
總結
Symfony validator
是一個強大的工具,可以用來保證任何物件的資料都是「有效」的。驗證背後的強大功能在於「約束」,您可以將規則應用於物件的屬性或 getter 方法。雖然您在使用表單時最常間接使用驗證框架,但請記住,它可以隨時隨地用於驗證任何物件。