CollectionType 欄位
此欄位類型用於渲染某個欄位或表單的「集合」。在最簡單的意義上,它可以是 TextType
欄位的陣列,用於填充 emails
值的陣列。在更複雜的範例中,您可以嵌入整個表單,這在建立公開一對多關係的表單時非常有用(例如,您可以從產品管理多個相關的產品照片)。
渲染時,現有的集合條目會以作為集合類型欄位資料傳遞的陣列的鍵值來索引。
渲染為 | 取決於 entry_type 選項 |
預設無效訊息 | 集合無效。 |
父類型 | FormType |
類別 | CollectionType |
提示
此表單類型定義和繼承的完整選項列表可透過在您的應用程式中執行此命令來取得
1 2
# replace 'FooType' by the class name of your form type
$ php bin/console debug:form FooType
注意
如果您正在使用 Doctrine 實體集合,請特別注意 allow_add、allow_delete 和 by_reference 選項。您也可以在 如何嵌入表單集合 文章中查看完整的範例。
基本用法
當您想要在表單中管理相似項目的集合時,會使用此類型。例如,假設您有一個 emails
欄位,對應到電子郵件地址的陣列。在表單中,您想要將每個電子郵件地址公開為其自己的輸入文字方塊
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
// ...
$builder->add('emails', CollectionType::class, [
// each entry in the array will be an "email" field
'entry_type' => EmailType::class,
// these options are passed to each "email" type
'entry_options' => [
'attr' => ['class' => 'email-box'],
],
]);
最簡單的渲染方式是一次全部渲染
1
{{ form_row(form.emails) }}
更彈性的方法看起來會像這樣
1 2 3 4 5 6 7 8 9 10 11
{{ form_label(form.emails) }}
{{ form_errors(form.emails) }}
<ul>
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
在這兩種情況下,除非您的 emails
資料陣列已包含一些電子郵件,否則不會渲染輸入欄位。
在這個簡單的範例中,仍然無法新增新的地址或移除現有的地址。透過使用 allow_add 選項(以及可選的 prototype 選項)可以新增新的地址(請參閱下面的範例)。使用 allow_delete 選項可以從 emails
陣列中移除電子郵件。
欄位選項
allow_add
類型:boolean
預設值:false
如果設定為 true
,則如果未識別的項目提交到集合,它們將作為新項目新增。結束陣列將包含現有項目以及提交資料中的新項目。如需更多詳細資訊,請參閱上面的範例。
prototype 選項可用於協助渲染原型項目,該項目可以使用 JavaScript 在用戶端動態建立新的表單項目。如需更多資訊,請參閱上面的範例和 如何嵌入表單集合。
警告
如果您要嵌入整個其他表單以反映一對多資料庫關係,您可能需要手動確保正確設定這些新物件的外鍵。如果您使用 Doctrine,這不會自動發生。如需更多詳細資訊,請參閱上面的連結。
allow_delete
類型:boolean
預設值:false
如果設定為 true
,則如果現有項目未包含在提交的資料中,它將正確地從最終的項目陣列中移除。這表示您可以透過 JavaScript 實作「刪除」按鈕,從 DOM 中移除表單元素。當使用者提交表單時,其從提交資料中移除,表示它會從最終陣列中移除。
如需更多資訊,請參閱 如何嵌入表單集合。
警告
當您嵌入物件集合時,請謹慎使用此選項。在這種情況下,如果移除任何嵌入的表單,它們將正確地從最終物件陣列中遺失。但是,根據您的應用程式邏輯,當其中一個物件被移除時,您可能想要刪除它,或至少移除其對主要物件的外鍵參考。這些都不是自動處理的。如需更多資訊,請參閱 如何嵌入表單集合。
delete_empty
類型:Boolean
或 callable
預設值:false
如果您想要從表單中明確移除完全空白的集合條目,您必須將此選項設定為 true
。但是,只有在您啟用 allow_delete 選項時,才會刪除現有的集合條目。否則,將保留空白值。
警告
只有當正規化的值為 null
時,delete_empty
選項才會移除項目。如果巢狀的 entry_type 是複合表單類型,您必須將 required
選項設定為 false
,或將 empty_data
選項設定為 null
。entry_options 內部可以設定這兩個選項。請閱讀關於 表單的 empty_data 選項,以了解為什麼這是必要的。
只有在正規化的值為 null
時,才會從集合中刪除值。但是,您也可以將選項值設定為可調用的值,這將針對提交集合中的每個值執行。如果可調用的值傳回 true
,則會從集合中移除該值。例如
1 2 3 4 5 6 7 8 9
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
// ...
$builder->add('users', CollectionType::class, [
// ...
'delete_empty' => function (?User $user = null): bool {
return null === $user || empty($user->getFirstName());
},
]);
在複合表單類型的情況下,使用可調用的值特別有用,複合表單類型可以定義將其視為空白的複雜條件。
entry_options
類型:array
預設值:[]
這是傳遞到 entry_type 選項中指定的表單類型的陣列。例如,如果您使用 ChoiceType 作為您的 entry_type 選項(例如,對於下拉式選單的集合),那麼您至少需要將 choices
選項傳遞到基礎類型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
// ...
$builder->add('favoriteCities', CollectionType::class, [
'entry_type' => ChoiceType::class,
'entry_options' => [
'choices' => [
'Nashville' => 'nashville',
'Paris' => 'paris',
'Berlin' => 'berlin',
'London' => 'london',
],
],
]);
prototype_options
類型:array
預設值:[]
這是傳遞到 entry_type 選項中指定的表單類型,以便在建立其原型時使用。它允許您根據您是在新增新條目還是編輯現有條目而擁有不同的選項
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder->add('names', CollectionType::class, [
'entry_type' => TextType::class,
'entry_options' => [
'help' => 'You can edit this name here.',
],
'prototype_options' => [
'help' => 'You can enter a new name here.',
],
]);
entry_type
類型:string
預設值:Symfony
這是此集合中每個項目的欄位類型(例如 TextType
、ChoiceType
等)。例如,如果您有一個電子郵件地址陣列,您可以使用 EmailType。如果您想要嵌入其他表單的集合,請將表單類型類別作為此選項傳遞(例如 MyFormType::class
)。
keep_as_list
類型:boolean
預設值:false
當設定為 true
時,keep_as_list
選項會影響集合中巢狀表單名稱的重新編製索引。當使用集合類型並在表單提交期間從集合中移除項目時,此功能特別有用。
當此選項設定為 false
時,如果您有 3 個項目的集合,並且您移除了第二個項目,則在驗證集合時,索引將為 0
和 2
。但是,透過啟用 keep_as_list
選項並將其設定為 true
,索引將重新編製索引為 0
和 1
。這確保索引保持連續且沒有間隙,為您的巢狀表單提供更清晰且更可預測的結構。
7.1
keep_as_list
選項是在 Symfony 7.1 中引入的。
prototype
類型:boolean
預設值:true
當使用 allow_add 選項時,此選項非常有用。如果 true
(並且如果 allow_add 也為 true
),則可以使用特殊的「原型」屬性,以便您可以在頁面上渲染「範本」範例,說明新元素的外觀。__name__
是給定此元素的 name
屬性。這允許您透過 JavaScript 新增「新增另一個」按鈕,該按鈕讀取原型,將 __name__
替換為一些唯一名稱或數字,並將其渲染到您的表單中。提交後,由於 allow_add 選項,它將被新增到您的基礎陣列中。
原型欄位可以透過集合欄位中的 prototype
變數來渲染
1
{{ form_row(form.emails.vars.prototype) }}
請注意,您真正需要的只是「widget」,但根據您渲染表單的方式,擁有整個「表單列」可能對您來說更容易。
提示
如果您一次渲染整個集合欄位,則原型表單列會自動在包圍您的集合的元素(例如 div
或 table
)的 data-prototype
屬性上提供。
如需關於如何實際使用此選項的詳細資訊,請參閱上面的範例以及 如何嵌入表單集合。
prototype_data
類型:mixed
預設值:null
允許您為原型定義特定資料。每個新增的列最初都將包含由此選項設定的資料。預設情況下,將使用為具有 entry_options 選項的所有條目設定的資料
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder->add('tags', CollectionType::class, [
'entry_type' => TextType::class,
'allow_add' => true,
'prototype' => true,
'prototype_data' => 'New Tag Placeholder',
]);
覆寫的選項
invalid_message
類型:string
預設值:此值無效
如果輸入到此欄位的資料沒有意義(即驗證失敗),則會使用此驗證錯誤訊息。
例如,如果使用者在 TimeType 欄位中輸入無法轉換為真實時間的無意義字串,或者如果使用者在數字欄位中輸入字串(例如 apple
),則可能會發生這種情況。
正常的(業務邏輯)驗證(例如設定欄位的最小長度)應使用帶有驗證規則的驗證訊息來設定(參考)。
繼承的選項
這些選項繼承自 FormType。並非所有選項都列在此處 - 僅列出最適用於此類型的選項
attr
類型:array
預設值:[]
如果您想要將額外的屬性新增至 HTML 欄位表示法,您可以使用 attr
選項。它是一個關聯陣列,HTML 屬性作為鍵值。當您需要為某些 widget 設定自訂類別時,這會很有用
1 2 3
$builder->add('body', TextareaType::class, [
'attr' => ['class' => 'tinymce'],
]);
另請參閱
如果您想要將這些屬性新增到 表單類型列 元素,請使用 row_attr
選項。
by_reference
類型:boolean
預設值:true
在大多數情況下,如果您有 author
欄位,那麼您會期望在基礎物件上呼叫 setAuthor()
。但是,在某些情況下,可能不會呼叫 setAuthor()
。將 by_reference
設定為 false
可確保在所有情況下都呼叫 setter。
為了進一步解釋這一點,這裡有一個簡單的範例
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder = $this->createFormBuilder($article);
$builder
->add('title', TextType::class)
->add(
$builder->create('author', FormType::class, ['by_reference' => ?])
->add('name', TextType::class)
->add('email', EmailType::class)
)
如果 by_reference
為 true,則當您在表單上呼叫 submit()
(或 handleRequest()
)時,幕後會發生以下情況
1 2 3
$article->setTitle('...');
$article->getAuthor()->setName('...');
$article->getAuthor()->setEmail('...');
請注意,不會呼叫 setAuthor()
。作者是透過參考修改的。
如果您將 by_reference
設定為 false,則提交看起來像這樣
1 2 3 4 5
$article->setTitle('...');
$author = clone $article->getAuthor();
$author->setName('...');
$author->setEmail('...');
$article->setAuthor($author);
所以,by_reference=false
實際上所做的就是複製物件,這會強制框架呼叫父物件上的 setter。
同樣地,如果您正在使用 CollectionType 欄位,而您的底層集合資料是一個物件(例如 Doctrine 的 ArrayCollection
),那麼如果您需要呼叫 adder 和 remover(例如 addAuthor()
和 removeAuthor()
),則必須將 by_reference
設定為 false
。
empty_data
type: mixed
預設值為 []
(空陣列)。
此選項決定當提交的值為空 (或遺失) 時,欄位將傳回的值。如果表單在視圖中呈現時未提供初始值,則此選項不會設定初始值。
這表示它可協助您處理具有空白欄位的表單提交。例如,如果您希望在未選取任何值時,將 name
欄位明確設定為 John Doe
,您可以這樣做
1 2 3 4
$builder->add('name', null, [
'required' => false,
'empty_data' => 'John Doe',
]);
這仍然會呈現一個空的文字方塊,但在提交後,將會設定 John Doe
值。使用 data
或 placeholder
選項,以在呈現的表單中顯示此初始值。
注意
如果表單是複合的,您可以將 empty_data
設定為陣列、物件或閉包。您可以為整個表單類別設定此選項,請參閱 如何為表單類別設定空資料 文章,以取得有關這些選項的更多詳細資訊。
警告
表單資料轉換器 仍然會套用至 empty_data
值。這表示空字串將會轉換為 null
。如果您明確想要傳回空字串,請使用自訂資料轉換器。
error_bubbling
類型:boolean
預設值:true
如果 true
,則此欄位的任何錯誤都將傳遞至父欄位或表單。例如,如果在一般欄位上設定為 true
,則該欄位的任何錯誤都將附加到主表單,而不是特定的欄位。
error_mapping
類型:array
預設值:[]
此選項可讓您修改驗證錯誤的目標。
想像一下,您有一個名為 matchingCityAndZipCode()
的自訂方法,用於驗證城市和郵遞區號是否匹配。不幸的是,您的表單中沒有 matchingCityAndZipCode
欄位,因此 Symfony 所能做的就是將錯誤顯示在表單頂部。
透過自訂錯誤映射,您可以做得更好:將錯誤映射到城市欄位,使其顯示在其上方
1 2 3 4 5 6 7 8
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'error_mapping' => [
'matchingCityAndZipCode' => 'city',
],
]);
}
以下是映射左側和右側的規則
- 左側包含屬性路徑;
- 如果違規是在類別的屬性或方法上產生,則其路徑為
propertyName
; - 如果違規是在
array
或ArrayAccess
物件的條目上產生,則屬性路徑為[indexName]
; - 您可以透過串連它們來建構巢狀屬性路徑,並以點分隔屬性。例如:
addresses[work].matchingCityAndZipCode
; - 右側包含表單中欄位的名稱。
預設情況下,任何未映射屬性的錯誤都會冒泡到父表單。您可以使用左側的點 (.
) 將所有未映射屬性的錯誤映射到特定欄位。例如,若要將所有這些錯誤映射到 city
欄位,請使用
1 2 3 4 5
$resolver->setDefaults([
'error_mapping' => [
'.' => 'city',
],
]);
help
type: string
或 TranslatableInterface
default: null
允許您為表單欄位定義說明訊息,預設情況下,該訊息會呈現在欄位下方
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Translation\TranslatableMessage;
$builder
->add('zipCode', null, [
'help' => 'The ZIP/Postal code for your credit card\'s billing address.',
])
// ...
->add('status', null, [
'help' => new TranslatableMessage('order.status', ['%order_id%' => $order->getId()], 'store'),
])
;
help_attr
類型:array
預設值:[]
設定用於顯示表單欄位說明訊息之元素的 HTML 屬性。其值為關聯陣列,其中 HTML 屬性名稱作為鍵。這些屬性也可以在範本中設定
1 2 3
{{ form_help(form.name, 'Your name', {
'help_attr': {'class': 'CUSTOM_LABEL_CLASS'}
}) }}
help_html
類型:boolean
預設值:false
預設情況下,help
選項的內容會在範本中呈現之前逸出。將此選項設定為 true
以不逸出它們,當說明包含 HTML 元素時,這非常有用。
label
type: string
或 TranslatableMessage
default: 標籤是從欄位名稱「猜測」而來
設定呈現欄位時將使用的標籤。設定為 false
將會抑制標籤
1 2 3 4 5 6 7 8
use Symfony\Component\Translation\TranslatableMessage;
$builder
->add('zipCode', null, [
'label' => 'The ZIP/Postal code',
// optionally, you can use TranslatableMessage objects as the label content
'label' => new TranslatableMessage('address.zipCode', ['%country%' => $country], 'address'),
])
標籤也可以在範本中設定
1
{{ form_label(form.name, 'Your name') }}
label_attr
類型:array
預設值:[]
設定 <label>
元素的 HTML 屬性,該元素將在呈現欄位的標籤時使用。它是一個關聯陣列,其中 HTML 屬性作為鍵。這些屬性也可以直接在範本內設定
1 2 3
{{ form_label(form.name, 'Your name', {
'label_attr': {'class': 'CUSTOM_LABEL_CLASS'}
}) }}
label_html
類型:boolean
預設值:false
預設情況下,label
選項的內容會在範本中呈現之前逸出。將此選項設定為 true
以不逸出它們,當標籤包含 HTML 元素時,這非常有用。
label_format
type: string
default: null
設定用作欄位標籤的字串,以防未設定 label
選項。當使用 關鍵字翻譯訊息 時,這非常有用。
如果您使用關鍵字翻譯訊息作為標籤,您通常最終會為同一個標籤擁有多個關鍵字訊息(例如 profile_address_street
、invoice_address_street
)。這是因為標籤是為每個欄位的「路徑」建構的。為了避免重複的關鍵字訊息,您可以將標籤格式設定為靜態值,例如
1 2 3 4 5 6 7 8
// ...
$profileFormBuilder->add('address', AddressType::class, [
'label_format' => 'form.address.%name%',
]);
$invoiceFormBuilder->add('invoice', AddressType::class, [
'label_format' => 'form.address.%name%',
]);
此選項由子類型繼承。使用上面的程式碼,這兩個表單的 street
欄位的標籤都將使用 form.address.street
關鍵字訊息。
標籤格式中有兩個變數可用
%id%
- 欄位的唯一識別碼,由欄位的完整路徑和欄位名稱組成(例如
profile_address_street
); %name%
- 欄位名稱(例如
street
)。
預設值 (null
) 會產生欄位名稱的 「人性化」版本。
注意
label_format
選項在表單主題中評估。如果您 自訂表單主題,請務必更新您的範本。
required
類型:boolean
預設值:true
如果為 true,則會呈現 HTML5 required 屬性。對應的 label
也會以 required
類別呈現。
這是表面上的,並且獨立於驗證。在最好的情況下,如果您讓 Symfony 猜測您的欄位類型,那麼此選項的值將從您的驗證資訊中猜測出來。
注意
required 選項也會影響如何處理每個欄位的空資料。有關更多詳細資訊,請參閱 empty_data 選項。
欄位變數
變數 | 類型 | 用法 |
---|---|---|
allow_add | boolean |
allow_add 選項的值。 |
allow_delete | boolean |
allow_delete 選項的值。 |