表單主題的使用方式
本文說明如何在您的應用程式中使用 Symfony 提供的任何表單主題,以及如何建立您自己的自訂表單主題。
Symfony 內建表單主題
Symfony 內建了幾個表單主題,當使用一些最受歡迎的 CSS 框架時,它們可以讓您的表單看起來很棒。每個主題都在單一 Twig 範本中定義,並且在 twig.form_themes 選項中啟用
- form_div_layout.html.twig,將每個表單欄位包在
<div>
元素中,並且是 Symfony 應用程式中預設使用的主題,除非您稍後在本文章中說明的設定方式進行設定。 - form_table_layout.html.twig,將整個表單包在
<table>
元素中,並將每個表單欄位包在<tr>
元素中。 - bootstrap_3_layout.html.twig,將每個表單欄位包在
<div>
元素中,並帶有適當的 CSS 類別,以套用 Bootstrap 3 CSS 框架使用的樣式。 - bootstrap_3_horizontal_layout.html.twig,與前一個主題類似,但套用的 CSS 類別是那些用於水平顯示表單的類別 (即,標籤和小工具在同一行)。
- bootstrap_4_layout.html.twig,與
bootstrap_3_layout.html.twig
相同,但針對 Bootstrap 4 CSS 框架樣式進行了更新。 - bootstrap_4_horizontal_layout.html.twig,與
bootstrap_3_horizontal_layout.html.twig
相同,但針對 Bootstrap 4 樣式進行了更新。 - bootstrap_5_layout.html.twig,與
bootstrap_4_layout.html.twig
相同,但針對 Bootstrap 5 CSS 框架樣式進行了更新。 - bootstrap_5_horizontal_layout.html.twig,與
bootstrap_4_horizontal_layout.html.twig
相同,但針對 Bootstrap 5 樣式進行了更新。 - foundation_5_layout.html.twig,將每個表單欄位包在
<div>
元素中,並帶有適當的 CSS 類別,以套用 Foundation CSS 框架第 5 版的預設樣式。 - foundation_6_layout.html.twig,將每個表單欄位包在
<div>
元素中,並帶有適當的 CSS 類別,以套用 Foundation CSS 框架第 6 版的預設樣式。 - tailwind_2_layout.html.twig,將每個表單欄位包在
<div>
元素中,並帶有使其可用的絕對最小樣式。它基於 Tailwind CSS 表單外掛程式。
提示
閱讀關於 Bootstrap 4 Symfony 表單主題 和 Bootstrap 5 Symfony 表單主題 的文章,以了解更多相關資訊。
將主題套用到所有表單
Symfony 表單預設使用 form_div_layout.html.twig
主題。如果您想為應用程式的所有表單使用另一個主題,請在 twig.form_themes
選項中設定它
1 2 3 4
# config/packages/twig.yaml
twig:
form_themes: ['bootstrap_5_horizontal_layout.html.twig']
# ...
您可以將多個主題傳遞給此選項,因為有時表單主題只重新定義一些元素。這樣,如果某些主題沒有覆寫某些元素,Symfony 會在其他主題中尋找。
twig.form_themes
選項中主題的順序很重要。每個主題都會覆寫所有先前的主題,因此您必須將最重要的主題放在列表的末尾。
將主題套用到單一表單
雖然大多數時候您會全域套用表單主題,但您可能需要僅將主題套用到某些特定表單。您可以使用 form_theme Twig 標籤 來執行此操作
1 2 3 4 5 6
{# this form theme will be applied only to the form of this template #}
{% form_theme form 'foundation_5_layout.html.twig' %}
{{ form_start(form) }}
{# ... #}
{{ form_end(form) }}
form_theme
標籤的第一個引數 (本例中為 form
) 是儲存表單視圖物件的變數名稱。第二個引數是定義表單主題的 Twig 範本的路徑。
將多個主題套用到單一表單
也可以透過套用多個主題來自訂表單。若要執行此操作,請使用 with
關鍵字傳遞所有 Twig 範本的路徑作為陣列 (它們的順序很重要,因為每個主題都會覆寫所有先前的主題)
1 2 3 4 5 6 7
{# apply multiple form themes but only to the form of this template #}
{% form_theme form with [
'foundation_5_layout.html.twig',
'form/my_custom_theme.html.twig'
] %}
{# ... #}
將不同主題套用到子表單
您也可以將表單主題套用到表單的特定子表單
1
{% form_theme form.a_child_form 'form/my_custom_theme.html.twig' %}
當您想要為巢狀表單使用與主要表單不同的自訂主題時,這非常有用。指定您的兩個主題
1 2
{% form_theme form 'form/my_custom_theme.html.twig' %}
{% form_theme form.a_child_form 'form/my_other_theme.html.twig' %}
停用單一表單的全域主題
在應用程式中定義的全域表單主題始終套用於所有表單,即使是那些使用 form_theme
標籤來套用其自身主題的表單也是如此。例如,當為可以在不同 Symfony 應用程式上安裝的套件建立管理介面時,您可能想要停用此功能 (因此您無法控制全域啟用了哪些主題)。若要執行此操作,請在表單主題列表後新增 only
關鍵字
1 2 3
{% form_theme form with ['foundation_5_layout.html.twig'] only %}
{# ... #}
警告
當使用 only
關鍵字時,Symfony 的任何內建表單主題 (form_div_layout.html.twig
等) 都將不會套用。為了正確呈現您的表單,您需要自己提供功能完整的表單主題,或者使用 Twig 的 use
關鍵字而不是 extends
擴充其中一個內建表單主題,以重複使用原始主題內容。
1 2 3 4
{# templates/form/common.html.twig #}
{% use "form_div_layout.html.twig" %}
{# ... #}
建立您自己的表單主題
Symfony 使用 Twig 區塊來呈現表單的每個部分 - 欄位標籤、錯誤、<input>
文字欄位、<select>
標籤等。主題是一個 Twig 範本,其中包含您想要在呈現表單時使用的一個或多個區塊。
例如,考慮一個表單欄位,它代表名為 age
的整數屬性。如果您將此新增到範本中
1
{{ form_widget(form.age) }}
產生的 HTML 內容將如下所示 (它會因應用程式中啟用的表單主題而異)
1
<input type="number" id="form_age" name="form[age]" required="required" value="33">
Symfony 使用名為 integer_widget
的 Twig 區塊來呈現該欄位。這是因為欄位類型為 integer
,並且您正在呈現其 widget
(而不是其 label
或 errors
或 help
)。建立表單主題的第一步是知道要覆寫哪個 Twig 區塊,如下節所述。
表單片段命名
表單片段的命名會根據您的需求而有所不同
- 如果您想自訂相同類型的所有欄位 (例如,所有
<textarea>
),請使用field-type_field-part
模式 (例如,textarea_widget
)。 - 如果您只想自訂一個特定欄位 (例如,用於編輯產品的表單的
description
欄位的<textarea>
),請使用_field-id_field-part
模式 (例如,_product_description_widget
)。
在這兩種情況下,field-part
可以是以下任何有效的表單欄位部分
相同類型所有欄位的片段命名
這些片段名稱遵循 type_part
模式,其中 type
對應於正在呈現的欄位類型 (例如,textarea
、checkbox
、date
等),而 part
對應於正在呈現的內容 (例如,label
、widget
等)
片段名稱的一些範例包括
form_row
- 由 form_row() 用於呈現大多數欄位;textarea_widget
- 由 form_widget() 用於呈現textarea
欄位類型;form_errors
- 由 form_errors() 用於呈現欄位的錯誤;
個別欄位的片段命名
這些片段名稱遵循 _id_part
模式,其中 id
對應於欄位 id
屬性 (例如,product_description
、user_age
等),而 part
對應於正在呈現的內容 (例如,label
、widget
等)
id
屬性同時包含表單名稱和欄位名稱 (例如,product_price
)。表單名稱可以手動設定,也可以根據您的表單類型名稱自動產生 (例如,ProductType
等於 product
)。如果您不確定您的表單名稱是什麼,請查看為您的表單呈現的 HTML 程式碼。您也可以使用 block_name
選項明確定義此值
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// ...
$builder->add('name', TextType::class, [
'block_name' => 'custom_name',
]);
}
在此範例中,片段名稱將為 _product_custom_name_widget
,而不是預設的 _product_name_widget
。
個別欄位的自訂片段命名
block_prefix
選項允許表單欄位定義自己的自訂片段名稱。這對於自訂相同欄位的一些實例非常有用,而無需 建立自訂表單類型
1 2 3 4 5 6 7 8 9
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('name', TextType::class, [
'block_prefix' => 'wrapped_text',
]);
}
現在您可以使用 wrapped_text_row
、wrapped_text_widget
等作為區塊名稱。
集合的片段命名
當使用 表單集合時,您有幾種自訂集合及其每個條目的方式。首先,使用以下區塊來自訂所有表單集合的每個部分
1 2 3 4 5
{% block collection_row %} ... {% endblock %}
{% block collection_label %} ... {% endblock %}
{% block collection_widget %} ... {% endblock %}
{% block collection_help %} ... {% endblock %}
{% block collection_errors %} ... {% endblock %}
您也可以使用以下區塊來自訂所有集合的每個條目
1 2 3 4 5
{% block collection_entry_row %} ... {% endblock %}
{% block collection_entry_label %} ... {% endblock %}
{% block collection_entry_widget %} ... {% endblock %}
{% block collection_entry_help %} ... {% endblock %}
{% block collection_entry_errors %} ... {% endblock %}
最後,您可以自訂特定的表單集合,而不是所有表單集合。例如,考慮以下複雜範例,其中 TaskManagerType
具有 TaskListType
的集合,而 TaskListType
又具有 TaskType
的集合
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 28 29 30 31
class TaskManagerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = []): void
{
// ...
$builder->add('taskLists', CollectionType::class, [
'entry_type' => TaskListType::class,
'block_name' => 'task_lists',
]);
}
}
class TaskListType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = []): void
{
// ...
$builder->add('tasks', CollectionType::class, [
'entry_type' => TaskType::class,
]);
}
}
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = []): void
{
$builder->add('name');
// ...
}
}
然後,您會獲得所有以下可自訂的區塊 (其中 *
可以替換為 row
、widget
、label
或 help
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
{% block _task_manager_task_lists_* %}
{# the collection field of TaskManager #}
{% endblock %}
{% block _task_manager_task_lists_entry_* %}
{# the inner TaskListType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_* %}
{# the collection field of TaskListType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_entry_* %}
{# the inner TaskType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_entry_name_* %}
{# the field of TaskType #}
{% endblock %}
在與表單相同的範本中建立表單主題
當在應用程式中對單一表單進行特定自訂時,建議使用此方法,例如變更表單的所有 <textarea>
元素,或自訂將使用 JavaScript 處理的非常特殊的表單欄位。
您只需要將特殊的 {% form_theme form _self %}
標籤新增到呈現表單的同一範本中。這會導致 Twig 在範本內尋找任何覆寫的表單區塊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{# this overrides the widget of any field of type integer, but only in the
forms rendered inside this template #}
{% block integer_widget %}
<div class="...">
{# ... render the HTML element to display this field ... #}
</div>
{% endblock %}
{# this overrides the entire row of the field whose "id" = "product_stock" (and whose
"name" = "product[stock]") but only in the forms rendered inside this template #}
{% block _product_stock_row %}
<div class="..." id="...">
{# ... render the entire field contents, including its errors ... #}
</div>
{% endblock %}
{# ... render the form ... #}
此方法的主要缺點是,它僅在您的範本擴充另一個範本時有效 (上一個範例中的 'base.html.twig'
)。如果您的範本沒有擴充其他範本,您必須將 form_theme
指向個別範本,如下節所述。
另一個缺點是,當在其他範本中呈現其他表單時,無法重複使用自訂的表單區塊。如果那是您需要的,請在個別範本中建立表單主題,如下節所述。
在個別範本中建立表單主題
當建立在整個應用程式中使用,甚至在不同的 Symfony 應用程式中重複使用的表單主題時,建議使用此方法。您只需要在某處建立一個 Twig 範本,並遵循 表單片段命名規則,即可知道要定義哪些 Twig 區塊。
例如,如果您的表單主題很簡單,並且您只想覆寫 <input type="integer">
元素,請建立此範本
1 2 3 4 5 6
{# templates/form/my_theme.html.twig #}
{% block integer_widget %}
{# ... add all the HTML, CSS and JavaScript needed to render this field #}
{% endblock %}
現在您需要告訴 Symfony 使用此表單主題來取代 (或除了) 預設主題。如本文前幾節所述,如果您想將主題全域套用到所有表單,請定義 twig.form_themes
選項
1 2 3 4
# config/packages/twig.yaml
twig:
form_themes: ['form/my_theme.html.twig']
# ...
如果您只想將其套用到某些特定表單,請使用 form_theme
標籤
1 2 3 4 5
{% form_theme form 'form/my_theme.html.twig' %}
{{ form_start(form) }}
{# ... #}
{{ form_end(form) }}
重複使用內建表單主題的部分
建立完整的表單主題需要大量工作,因為有太多不同的表單欄位類型。您可以只定義您感興趣的區塊,然後在您的應用程式或範本中設定多個表單主題,而不是定義所有這些 Twig 區塊。這樣做是可行的,因為當呈現自訂主題中未覆寫的區塊時,Symfony 會回退到其他主題。
另一個解決方案是讓您的表單主題範本使用 Twig "use" 標籤 而不是 extends
標籤從其中一個內建主題擴充,這樣您就可以繼承其所有區塊 (如果您不確定,請從預設的 form_div_layout.html.twig
主題擴充)
1 2 3 4
{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}
{# ... override only the blocks you are interested in #}
最後,您也可以使用 Twig parent() 函數 來重複使用內建主題的原始內容。當您只想進行細微的變更 (例如,使用某些元素包裝產生的 HTML) 時,這非常有用
1 2 3 4 5 6 7 8
{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}
{% block integer_widget %}
<div class="some-custom-class">
{{ parent() }}
</div>
{% endblock %}
當在呈現表單的同一範本中定義表單主題時,此技術也適用。但是,從內建主題匯入區塊會稍微複雜一些
1 2 3 4 5 6 7 8 9 10 11 12 13
{% form_theme form _self %}
{# import a block from the built-in theme and rename it so it doesn't
conflict with the same block defined in this template #}
{% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}
{% block integer_widget %}
<div class="some-custom-class">
{{ block('base_integer_widget') }}
</div>
{% endblock %}
{# ... render the form ... #}
自訂表單驗證錯誤
如果您為物件定義了驗證規則,當提交的資料無效時,您會看到一些驗證錯誤訊息。這些訊息會使用 form_errors() 函數顯示,並且可以使用任何表單主題中的 form_errors
Twig 區塊進行自訂,如前幾節所述。
需要考慮的重要事項是,某些錯誤與整個表單相關聯,而不是與特定欄位相關聯。為了區分全域錯誤和本機錯誤,請使用 表單中可用的變數 之一,稱為 compound
。如果它是 true
,則表示目前呈現的是欄位集合 (例如,整個表單),而不僅僅是個別欄位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
{# templates/form/my_theme.html.twig #}
{% block form_errors %}
{% if errors|length > 0 %}
{% if compound %}
{# ... display the global form errors #}
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% else %}
{# ... display the errors for a single field #}
{% endif %}
{% endif %}
{% endblock form_errors %}