亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > PHP > 正文

Symfony2框架學習筆記之表單用法詳解

2024-05-04 23:44:14
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了Symfony2框架學習筆記之表單用法,結合實例形式詳細分析了Symfony2針對表單的創建,校驗,提交等各種常用技巧,需要的朋友可以參考下
 

本文實例講述了Symfony2框架表單用法。分享給大家供大家參考,具體如下:

對于一個Web開發者來說,處理HTML表單是一個最為普通又具挑戰的任務。Symfony2集成了一個Form組件,讓處理表單變的容易起來。在這一節里,我們將

從基礎開始創建一個復雜的表單,學習表單類庫中最重要的內容。

Symfony2 的Form組件是一個獨立的類庫,你可以在Symfony2項目之外使用它。

創建一個簡單的表單:

假設你要創建一個應用程序的todo列表,需要顯示一些任務。因為你的用戶需要編輯和創建任務,所以你需要創建一個表單。在你開始之前,首先來看通用的Task類,用來表示和存儲一個單一任務的數據:

// src/Acme/TaskBundle/Entity/Task.phpnamespace Acme/TaskBundle/Entity;class Task{  protected $task;  protected $dueDate;  public function getTask()  {    return $this->task;  }  public function setTask($task)  {    $this->task = $task;  }  public function getDueDate()  {    return $this->dueDate;  }  public function setDueDate(/DateTime $dueDate = null)  {    $this->dueDate = $dueDate;  }}

如果你是按照我們提供的示例編碼,那么你需要先創建一個AcmeTaskBundle:

$ php app/console generate:bundle --namespace=Acme/TaskBundle

該類是一個普通的PHP對象類,因為他們沒有任何Symfony或者其它類庫引用。非常簡單的一個PHP對象類,它直接解決的是你程序中表現task的數據。當然,在本節的最后,你將能夠通過HTML表單提交一個Task實例數據,校驗它的數值,并把它持久化到數據庫。

創建一個Form

現在已經創建了一個Task類,下一步就是創建和渲染一個真正的HTML表單了。在symfony2中,它是通過創建一個表單對象并渲染到模板的?,F在,可以從controller內部處理form。

//src/Acme/TaskBundle/Controller/DefaultController.phpnamespace Acme/TaskBundle/Controller;use Symfony/Bundle/FrameworkBundle/Controller/Controller;use Symfony/Component/HttpFoundation/Request;use Acme/TaskBundle/Entity/Task;class DefaultController extends Controller{    //創建一個任務并給它一些假數據作為示例    $task = new Task();    $task->setTask('Write a blog post');    $task->setDueDate(new /DateTime('tomorrow'));    $form = $this->createFormBuilder($task)       ->add('task','text')       ->add('dueDate','date')       ->getForm();    return $this->render('AcmeTaskBundle:Default:new.html.twig',array(        'form' =>$form->createView(),    ));}

上面的示例顯示了如何直接在Controller中創建一個表單,為了可以讓表單重用你完全可以在一個單獨的類文件中創建表單。

因為Symfony2通過一個表單生成器“form builder"來創建表單對象,所以你可以使用很少的代碼就能完成創建表單任務。表單生成器的目的是讓你能編寫簡單的表單創建方法,讓它來負責繁重的創建任務。

在這個示例中,你已經添加了兩個字段到你的表單,一個是task一個是dueDate。它們關聯到Task類的task和dueDate屬性。你已經為它們分別指定了類型(比如,text,date等),由這些類型來決定為這些字段生成什么樣的HTML表單標簽。

Symfony2 擁有許多內建的類型,接下來我們將簡單的介紹。

渲染一個表單

表單創建以后,下一步就是渲染它。這是通過傳遞一個特定的表單”view"對象(就是上例中的 $form->createView()返回的view對象)到你的模板并通過一些列的表單幫助函數來實現的。

Twig格式:

{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}<form action="{{ path('task_new') }}" method ="post" {{ form_enctype(form) }}>  {{ form_widget(form) }}   <input type="submit" /></form>

PHP代碼格式:

<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php --><form action="<?php echo $view['router']->generate('task_new') ?>" method="post" <?php echo $view['form']->enctype($form) ?> >   <?php echo $view['form']->widget($form) ?>   <input type="submit" /></form>

在這里假設你已經創建了一個名叫task_new的路由指向AcmeTaskBundle:Default:new Controller。

就是這些了,通過打印form_widget(form),表單中的每個字段都會被渲染出來。同時還有一個文本標簽和錯誤信息。是不是很簡單,不過現在它還不夠靈活。通常情況下,我們渴望單獨渲染表單中的每一個字段,這樣我們可以更好的控制表單的樣式。我們會在在模板中渲染表單一節介紹。

在繼續下去之前,我們注意到,為什么我們渲染出來的task輸入框中有一個來自$task對象的屬性值“Write a blog post"。這是表單的第一個工作:從一個對象中獲取數據并把它轉換為合適的格式渲染到一個HTML表單中。

注意,表單系統已經足夠聰明,它們能夠通過像getTask()和setTask()方法來訪問Task類中受保護的屬性task。除非一個是公共屬性,否則必須有一個getter和setter方法被定義來用于表單組件從這些屬性中獲取和保持數據。對于布爾型的屬性,你可以使用一個”isser"方法(比如 isPublished())替代getter方法(getPublished())。

處理表單提交

表單系統的第二個任務就是傳遞用戶提交的數據回到一個對象的屬性中。要做到這一點,用戶提交的數據必須綁定到表單才行。添加如下代碼到你的Controller類:

//...public function newAction(Request $request){     //只是創建一個新的$task對象(不需要假數據)    $task = new Task();    $form= $this->createFormBuilder($task)       ->add('task','text')       ->add('dueDate','date')       ->getForm();    if($request->getMethod() == "POST"){       $form->bindRequest($request);       if($form->isValid()){           //執行一些行為,比如保持task到數據庫           return $this->redirect($this->generateUrl('task_success'));       }    }    //...}

現在,當表單被提交時,Controller可以綁定被提交的數據到表單,表單會把數據傳回$task對象的task和dueDate屬性。這些都在bindRequest()方法中完成。只要bindRequest()方法被調用,提交的數據就會立刻被傳輸到底層對象。不管數據是否被真正的校驗通過。

controller一般會遵循一個通用的模式來處理表單,它有三個可能的途徑:

1.當在瀏覽器初始加載一個頁面時,請求方法是GET,表單處理僅僅是創建和渲染。

2.當用戶提交帶有不合法數據的表單(方法為POST)時,表單會并綁定然后渲染,這時候顯示所有校驗錯誤。

3.當用戶提交的表單帶有的數據均合法時,表單綁定并且在頁面跳轉之前你有機會去使用數據去執行一些業務邏輯活動,比如持久化它到數據庫)。

表單校驗

在前面我們提到了,如何提交一個帶有合法數據和非法數據的表單。在Symfony2中,校驗是在底層對象上進行的。換句話說,form表單合法與否不重要,主要看在表單提交數據以后,底層對象比如$task對象是否合法。調用$form->isvalid() 是一個詢問底層對象是否獲得合法數據的快捷方式。

校驗是通過添加一些列規則(約束)到一個類來完成的。我們給Task類添加規則和約束,使它的task屬性不能為空,duDate字段不能空并且是一個合法的DateTime對象。

YAML格式:

# Acme/TaskBundle/Resources/config/validation.ymlAcme/TaskBundle/Entity/Task:  properties:    task:      - NotBlank: ~    dueDate:      - NotBlank: ~      - Type: /DateTime

在Task類中聲明格式:

// Acme/TaskBundle/Entity/Task.phpuse Symfony/Component/Validator/Constraints as Assert;class Task{  /**   * @Assert/NotBlank()   */  public $task;  /**   * @Assert/NotBlank()   * @Assert/Type("/DateTime")   */  protected $dueDate;}

XML格式:

<!-- Acme/TaskBundle/Resources/config/validation.xml --><class name="Acme/TaskBundle/Entity/Task">  <property name="task">    <constraint name="NotBlank" />  </property>  <property name="dueDate">    <constraint name="NotBlank" />    <constraint name="Type">      <value>/DateTime</value>    </constraint>  </property></class>

PHP代碼格式:

// Acme/TaskBundle/Entity/Task.phpuse Symfony/Component/Validator/Mapping/ClassMetadata;use Symfony/Component/Validator/Constraints/NotBlank;use Symfony/Component/Validator/Constraints/Type;class Task{  // ...  public static function loadValidatorMetadata(ClassMetadata $metadata)  {    $metadata->addPropertyConstraint('task', new NotBlank());    $metadata->addPropertyConstraint('dueDate', new NotBlank());    $metadata->addPropertyConstraint('dueDate', new Type('/DateTime'));  }}

就是這樣了,如果你現在再提交包含非法數據的表單,你將會看到相應的錯誤被打印在表單上。

HTML5 校驗

作為HTML5,許多瀏覽器都加強了客戶端某些校驗約束。最常用的校驗活動是在一個必須的字段上渲染一個required屬性。對于支持HTML5的瀏覽器來說,如果用戶此時提交一個空字段到表單時,瀏覽器會顯示提示信息。生成的表單廣泛吸收了這些新內容的優點,通過添加一些HTML屬性來監控校驗??蛻舳诵r灴梢酝ㄟ^添加novalidate屬性到form標簽或者formnovalidate 到提交標簽而關閉。這對你想檢查服務端校驗規則時非常有用。

校驗分組

如果你的對象想從校驗組中受益,你需要指定你的表單使用哪個校驗組。

$form = $this->createFormBuilder($users, array(  'validation_groups' => array('registration'),))->add(...);

如果你創建表單類,你需要添加羨慕的getDefaultOptions()方法:

public function getDefaultOptions(array $options){  return array(    'validation_groups' => array('registration')  );}

在這兩種情況下,只有registration 校驗組將被用于校驗底層對象。

內建字段類型

Symfony標準版含有大量的字段類型,它們幾乎涵蓋了所有通用表單的字段和數據類型。

文本字段:
text
textarea
email
integer
money
number
password
percent
search
url

選擇字段:
choice
entity
country
language
locale
timezone

日期和時間字段:
date
datetime
time
birthday

其它字段:
checkbox
file
radio

字段組:
collection
repeated

隱藏字段:
hidden
csrf

基礎字段:
field
form

當然,你也可以定義自己的字段類型。

字段類型選項

每一個字段類型都有一定數量的選項用于配置。比如,dueDate字段當前被渲染成3個選擇框。而日期字段可以被配置渲染成一個單一的文本框,用戶可以輸入字符串作為日期。

->add('dueData','data', array('widget' = 'single_text'))

required選項:

最常用到的選項是required選項,它可以應用于任何字段。默認情況下它被設置為true。這就意味著支持HTML5的瀏覽器會使用客戶端校驗來判斷字段是否為空。如果你不想讓它發生,或者把在你的字段上把required選項設置為false,或者關閉HTML5校驗。設置required為true并不意味著服務端校驗被應用。換句話說,如果用戶提交一個空數值到該字段,它將接受這個控制除非你使用Symfony的NotBlank或者NotNull校驗約束。也就是說,required選項是很好,但是服務端校驗還是要繼續用。

label選項:

表單字段可以使用label選項設置顯示字符標簽,可以應用于任何字段:

->add('dueDate', 'date',array(  'widget' =>'single_text',  'label' => 'Due Date',))

字段類型猜測:

現在你已經添加了校驗元數據到Task類,Symfony早已經了解一點關于你的字段了。如果你允許,Symfony可以猜到你的字段數據類型并為你設置它。在下面的例子中,Symfony可以根據校驗規則猜測到task字段是一個標準的text字段,dueDate是date字段。

public function newAction(){  $task = new Task();  $form = $this->createFormBuilder($task)    ->add('task')    ->add('dueDate', null, array('widget' => 'single_text'))    ->getForm();}

當你省略了add方法的第二個參數(或者你輸入null)時,Symfony的猜測能力就起作用了。如果你輸入一個選項數組作為第三個參數(比如上面的dueDate),那么這些選項會成為Symfony猜測的依據。如果你的表單使用了指定的校驗數組,字段類型猜測器將還是要考慮所有的校驗規則來綜合猜測你的字段類型。

字段類型可選項猜測

除了猜測字段類型,Symfony還能是這猜測一些可選項字段值。當這些可選項被設置時,字段將會被渲染到特定HTML屬性中,讓HTML5客戶端來提供校驗。

然而,它們不會在服務端生成相應的校驗規則。盡管你需要手動的在服務端添加這些規則,但是這些字段類型選項還是能根據這些信息猜測到。

required: required規則可以在校驗規則或者Doctrine元數據的基礎上猜測到。這當你的客戶端校驗將自動匹配你的校驗規則時很有用。
max_length: 如果字段是一些列文本字段,那么max_length選項可以從校驗規則或者Doctrine元數據中猜到。
如果你喜歡改變一個猜到的數值,你可以通過在可選項數組中傳遞該選項來重寫它。

->add('task',null, array('max_length'=>4))

在模板中渲染表單

到目前為止,我們已經看了一個完整的表單是如何通過一行代碼被渲染的。當然,你通常需要更加靈活的渲染方式:

Twig格式:

{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}<form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>  {{ form_errors(form) }}  {{ form_row(form.task) }}  {{ form_row(form.dueDate) }}  {{ form_rest(form) }}  <input type="submit" /></form>

PHP代碼格式:

<!-- // src/Acme/TaskBundle/Resources/views/Default/newAction.html.php --><form action="<?php echo $view['router']->generate('task_new') ?>" method="post" <?php echo $view['form']->enctype($form) ?>>  <?php echo $view['form']->errors($form) ?>  <?php echo $view['form']->row($form['task']) ?>  <?php echo $view['form']->row($form['dueDate']) ?>  <?php echo $view['form']->rest($form) ?>  <input type="submit" /></form>

讓我們看看這組代碼的詳細:

form_enctype(form) 只要有一個字段是文件上傳,那么它就會義務的設置為 enctype="multipart/form-data";
form_errors(form) 渲染任何整個form的任何錯誤信息(特定字段的錯誤,會顯示在每個字段的下面一行)。
form_row(form.dueDate) 默認情況下,為給定的字段在一個div中渲染一個文本標簽,任何錯誤,和HTML表單部件。
form_rest(form) 渲染沒有指出的其余任何字段,通常在表單的末尾調用它防止遺忘或者渲染一些你不愿意手動設置的隱藏字段。它同時還能為我們提供CSRF保護。

大部分工作是由form_row幫助方法類完成的,它默認在一個div中為每個字段渲染顯示標簽,錯誤信息和HTML表單部件。

注意,你可以通過form.vars.value 來訪問你當前是表當數據:

Twig格式:

{{ form.vars.value.task }}

PHP代碼格式:

<?php echo $view['form']->get('value')->getTask() ?>

手工渲染每一個表單字段

form_row幫助器能讓你很快的渲染你表單中的每一個字段,并且每一行可以被自定義化。但是生活不總是那么簡單的,你也可能要手動的渲染每一個字段。

Twig格式:

{{ form_errors(form) }}<div>  {{ form_label(form.task) }}  {{ form_errors(form.task) }}  {{ form_widget(form.task) }}</div><div>  {{ form_label(form.dueDate) }}  {{ form_errors(form.dueDate) }}  {{ form_widget(form.dueDate) }}</div>{{ form_rest(form) }}

PHP代碼格式:

<?php echo $view['form']->errors($form) ?><div>  <?php echo $view['form']->label($form['task']) ?>  <?php echo $view['form']->errors($form['task']) ?>  <?php echo $view['form']->widget($form['task']) ?></div><div>  <?php echo $view['form']->label($form['dueDate']) ?>  <?php echo $view['form']->errors($form['dueDate']) ?>  <?php echo $view['form']->widget($form['dueDate']) ?></div><?php echo $view['form']->rest($form) ?>

如果自動生成顯示標簽不準確,那么你可以顯式的指定它:

Twig格式:

{{ form_label(form.task, 'Task Description') }}

PHP代碼格式:

<?php echo $view['form']->label($form['task'], 'Task Description') ?>

一些字段類型有一些額外的渲染選項可以傳入widget,一個常用的選項為attr,它允許你修改表單元素的屬性。下面的示例將添加task_field class到渲染的文本輸入字段:

Twig格式:

{{ form_widget(form.task, {'attr': {'class':'task_field'} }) }}

PHP代碼格式:

<?php echo $view['form']->widget($form['task'], array('attr' => array('class' => 'task_field'),)) ?>

如果你想手工渲染表單字段,你可以單獨訪問每個字段的值,比如id,name和label,這里我們獲取id

Twig格式:

{{ form.task.vars.id }}

PHP代碼格式:

<?php echo $form['task']->get('id') ?>

需要獲取表單字段名稱屬性你需要使用full_name值:

Twig格式:

{{ form.task.vars.full_name }}

PHP代碼格式:

<?php echo $form['task']->get('full_name') ?>

創建表單類

正如你看到的,一個表單可以直接在controller類中被創建和使用。然而,一個更好的做法是在一個單獨的PHP類中創建表單。它可以被重用到你應用程序的任何地方。創建一個新類來保存生成task表單的邏輯:

// src/Acme/TaskBundle/Form/Type/TaskType.phpnamespace Acme/TaskBundle/Form/Type;use Symfony/Component/Form/AbstractType;use Symfony/Component/Form/FormBuilder;class TaskType extends AbstractType{  public function buildForm(FormBuilder $builder, array $options)  {    $builder->add('task');    $builder->add('dueDate', null, array('widget' => 'single_text'));  }  public function getName()  {    return 'task';  }}

這個新類包含了所有創建一個task表單所需要的內容,注意getName()方法將返回一個該表單類型的唯一標識,用于快速創建該表單。

// src/Acme/TaskBundle/Controller/DefaultController.php// 在類上添加這個新的引用語句use Acme/TaskBundle/Form/Type/TaskType;public function newAction(){    $task = // ...    $form = $this->createForm(new TaskType(), $task);    // ...}

設置data_class

每個表單都需要知道它底層保存數據的類名稱,(比如Acme/TaskBundle/Entity/Task)。通常情況下,是根據createForm方法的第二個參數來猜測的。以后,當你開始嵌入表單時,這個可能就不怎么充分了,所以,通常一個好的方法是通過添加下面代碼到你的表單類型類來顯式的指定data_class 選項。

public function getDefaultOptions(array $options){   return array(        'data_class' => 'Acme/TaskBundle/Entity/Task',   );}

當然,這種做法也不總是必須的。

當你映射表單到一個對象是,所有的字段都被映射。 表單的任何字段如果在映射的對象上不存在那么就會造成拋出異常。在這種情況下,你需要在表單中獲取字段(比如,一個“你同意這些說法嗎?”復選框)將不能映射到底層對象,那么你需要設置property_path為false以避免拋出異常。

public function buildForm(FormBuilder $builder, array $options){     $builder->add('task');     $builder->add('dueDate', null, array('property_path' => false));}

另外,如果有任何的表單字段沒有被包含著提交的數據中,那么這些字段需要顯式的設置為null。

在controller類中我們可以訪問字段數據:

$form->get('dueDate')->getData();

Forms和Doctrine

表單的目的是把數據從一個底層對象傳遞給一個HTML表單然后把用戶提交的數據傳回到原先的底層對象。因此,底層對象把數據持久化到數據庫就跟表單沒有任何的關系了。但是,如果你已經配置了底層類是通過Doctrine來持久化,(你已經定義了映射元數據在底層類),接下來當表單提交數據后,當表單合法后就可以持久化它了。

if ($form->isValid()) {  $em = $this->getDoctrine()->getEntityManager();  $em->persist($task);  $em->flush();  return $this->redirect($this->generateUrl('task_success'));}

如果處于某種原因,你不想訪問原有的$task對象,你可以從表單中直接獲取數據:

$task = $form->getData();

在這里,關鍵要理解當表單跟底層對象綁定后,用戶提交的數據會立刻傳遞給底層對象。如果你想持久化這些數據,你只需要持久化對象本身即可。

嵌入式表單:(Embedded Forms)

通常,你可能想生成一個表單,它包含來自不同對象的字段。比如,一個注冊表單可能包含屬于User對象和Address對象的字段。幸運的是,這些對于form組件來說都是很容易很自然的事。嵌入一個單獨對象:假設每個Task屬于一個Category對象,首先創建這個Category對象:

// src/Acme/TaskBundle/Entity/Category.phpnamespace Acme/TaskBundle/Entity;use Symfony/Component/Validator/Constraints as Assert;class Category{  /**   * @Assert/NotBlank()   */  public $name;}

接下來,添加一個新的category屬性到Task類:

// ...class Task{  // ...  /**   * @Assert/Type(type="Acme/TaskBundle/Entity/Category")   */  protected $category;  // ...  public function getCategory()  {    return $this->category;  }  public function setCategory(Category $category = null)  {    $this->category = $category;  }}

現在我們來相應我們應用程序的一個新需求,需要創建一個 表單可以讓用戶修改Category對象。

// src/Acme/TaskBundle/Form/Type/CategoryType.phpnamespace Acme/TaskBundle/Form/Type;use Symfony/Component/Form/AbstractType;use Symfony/Component/Form/FormBuilder;class CategoryType extends AbstractType{  public function buildForm(FormBuilder $builder, array $options)  {    $builder->add('name');  }  public function getDefaultOptions(array $options)  {    return array(      'data_class' => 'Acme/TaskBundle/Entity/Category',    );  }  public function getName()  {    return 'category';  }}

我們的最終目的是能夠讓用戶在Task表單中修改Category對象,所以,我們需要添加一個類型為CategoryType表單類的category字段到TaskType 表單類。

public function buildForm(FormBuilder $builder, array $options){  // ...  $builder->add('category', new CategoryType());}

這時我們可以在TaskType類字段渲染的旁邊渲染CategoryType類的字段了:
Twig格式:

{# ... #}<h3>Category</h3><div class="category">  {{ form_row(form.category.name) }}</div>{{ form_rest(form) }}{# ... #}

PHP代碼格式:

<!-- ... --><h3>Category</h3><div class="category">  <?php echo $view['form']->row($form['category']['name']) ?></div><?php echo $view['form']->rest($form) ?><!-- ... -->

當用戶提交表單時,提交的Category字段數據被用于創建一個Category實例,然后被設置到Task實例的category字段。該Category實例可以通過Task實例來訪問,同時也能被持久化到數據或者用作它用。

$task->getCategory()

嵌入一個表單集合

你也可以將一個表單集合嵌入到一個表單(想象一個Category 表單和許多Product子表單)。它是通過一個字段類型集合類實現的。

表單主題化

表單的每一部分渲染都是可以被自定義個性化的。你可以自由的改變每一個表單行的渲染,改變渲染錯誤的標志,更或者是textarea標簽應該怎樣顯示等。沒有任何限制,不同的個性化設置能用到不同的區域。

Symfony使用模板渲染每一個或者部分表單,比如label標簽,input標簽,錯誤信息以及任何其它內容。在Twig中,每個表單片段會被一個Twig block來渲染。要個性化渲染表單,你只需要重寫相應的block即可。在PHP模板中,它是通過單獨的模板文件來渲染表單片段的,所以你需要通過編寫新的模板來替代舊的模板即可。在理解了它們是怎么工作的之后,讓我們來個性化form_row片段并添加一個class屬性到包裹每一表單行的div元素。首先創建一個新模板文件用于存放新的標志:

Twig格式:

{# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}{% block field_row %}{% spaceless %}  <div class="form_row">    {{ form_label(form) }}    {{ form_errors(form) }}    {{ form_widget(form) }}  </div>{% endspaceless %}{% endblock field_row %}

PHP代碼格式:

<!-- src/Acme/TaskBundle/Resources/views/Form/field_row.html.php --><div class="form_row">  <?php echo $view['form']->label($form, $label) ?>  <?php echo $view['form']->errors($form) ?>  <?php echo $view['form']->widget($form, $parameters) ?></div>

field_row表單片段會在通過form_row函數渲染大部分的表單字段時使用。 要告訴你的表單組件使用你的新的field_row片段,需要添加下面的內容到你渲染該表單的模板頂部:

Twig格式:

{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %}{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %}<form ...>

PHP代碼格式:

<!-- src/Acme/TaskBundle/Resources/views/Default/new.html.php --><?php $view['form']->setTheme($form, array('AcmeTaskBundle:Form')) ?><?php $view['form']->setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?><form ...>

其中的form_theme 標簽導入前面定義的片段。換句話說,當form_row函數在模板中被調用后,它將從你的自定義主題中使用field_row 塊(替代Symfony已有的field_row block)。你的個性化主題不必重寫所有的塊。當渲染一個你沒有重寫過的塊時,主題引起會找全局的主題(定義在bundle級的主題)使用。

在擁有多個個性化主題的情況下,它會在使用全局主題之前查找定制列表。要個性化你表單的任何部分,你只需要重寫相關的片段即可。

表單片段命名

在symfony中,表單的每一部分都會被渲染,HTML表單元素,錯誤消息,顯示標簽等這些都是被定義在基礎主題里的。它組成了一個Twig的塊集合和一個PHP模板集合。

在Twig中,每個需要的塊都被定義到一個單獨的模板文件中(form_dive_layout.html.twig),它們被保存在Twig Bridge里。在這個文件中,你可以看到渲染一個表單多需要的每一個block和默認的字段類型。

在PHP模板中,片段是單獨的模板文件。 默認情況下它們位于框架bundle的Resources/views/Form 目錄下。每個偏度名稱都遵循相同的基本模式,用一個下劃線(_)分為兩部分,比如:

field_row 用于form_row渲染大部分的字段
textarea_widget 用于form_widget渲染一個textarea字段類型
field_errors 用于form_errors渲染一個字段的錯誤信息

每個片段都命名都遵循:type_part 模式。type部分對應被渲染的字段類型(比如textarea,checkbox,date等),而part部分對應著是什么被渲染(比如label,widget,errors等)

默認情況下,有4種可能的表單part被用來渲染:

label 渲染字段的標簽 如field_label
widget 渲染字段的HTML表示 如field_widget
errors 渲染字段的錯誤信息 如field_errors
row 渲染字段的整個行(包括label,widget和errors) 如 filed_row

還有其它3個part類型,分別是rows,rest和enctype,不過這三個一般不會用到。

通過知道字段類型(比如:textarea)和你想渲染那一部分(比如:widget),你可以創建一個你需要重寫的片段名稱(比如:textarea_widget).

模板片段繼承

在某些情況下,你個性化的片段可能會丟失。比如,在Symfony提供的默認主題中沒有提供textarea_errors片段。那么如何來渲染一個textarea字段的錯誤信息呢?

答案是通過field_errors片段。當Symfony渲染一個textarea類型的錯誤時,它首先查找一個textarea_errors片段,如果沒有找到則會回到field_errors片段。

每個field類型有一個parenttype(textarea的父類型為field),Symfony如果沒有發現本身的片段,就會轉而使用父類片段。

所以,要重寫textarea字段的errors,拷貝field_errors片段,重命名為textarea_errors并個性化它們。為所有字段重寫默認的error渲染,則需要直接拷貝和個性化field_errors片段。

全局表單主題

在上面的示例中,我們使用了form_theme helper來導入自定義個的表單片段到表單。你也可以告訴Symfony在全項目中導入自定義的form。

Twig

為了從所有之前創建的fileds.html.twig模板中自動包含個性化的block,修改你的應用程序配置文件:

YAML格式:

# app/config/config.ymltwig:  form:    resources:      - 'AcmeTaskBundle:Form:fields.html.twig'  # ...

XML格式:

<!-- app/config/config.xml --><twig:config ...>    <twig:form>      <resource>AcmeTaskBundle:Form:fields.html.twig</resource>    </twig:form>    <!-- ... --></twig:config>

PHP代碼格式:

// app/config/config.php$container->loadFromExtension('twig', array(  'form' => array('resources' => array(    'AcmeTaskBundle:Form:fields.html.twig',   ))  // ...));

現在在fields.html.twig模板中的任何塊都可以被廣泛的使用來定義表單輸出了。

自定義表單輸出到一個單一的Twig文件中

在Twig中,你也可以個性化一個表單塊在模板中

{% extends '::base.html.twig'%}{# 導入"_self" 作為一個表單主題 #}{% form_theme form _self %}{# 個性化表單片段 #}{% block field_row %}    {# 自定義字段行輸出 #}{% endblock field_row %}{% block content %}    {# ... #}    {{ form_row(form.task) }}{% endblock %}

這里{% form_theme form _self %}標簽允許表單塊在使用那些自動化內容的模板中被直接自定義化。使用這個方法來快速的生成個性化輸出。

注意,{% form_theme form _self %}的功能只有在繼承自其它模板時才能起作用,如果不是繼承自其它模板,則需要指出form_theme 到單獨模板中。

PHP

從以前在所有模板中創建的Acme/TaskBundle/Resources/views/Form 目錄自動導入個性化模板。修改你的配置文件:

YAML格式:

# app/config/config.ymlframework:  templating:    form:      resources:        - 'AcmeTaskBundle:Form'# ...

XML格式:

<!-- app/config/config.xml --><framework:config ...>  <framework:templating>    <framework:form>      <resource>AcmeTaskBundle:Form</resource>    </framework:form>  </framework:templating>  <!-- ... --></framework:config>

PHP代碼格式:

// app/config/config.php$container->loadFromExtension('framework', array(  'templating' => array('form' =>    array('resources' => array(      'AcmeTaskBundle:Form',   )))  // ...));

此時在Acme/TaskBundle/Resources/views/Form目錄中的任何片段都可以全局范圍內定義表單輸出了。

CSRF 保護

CSRF--Cross-site request forgery,跨站偽造請求 是惡意攻擊者試圖讓你的合法用戶在不知不覺中提交他們本不想提交的數據的一種方法。

幸運的是,CSRF攻擊可以通過在你的表單中使用CSRF 記號來阻止。

默認情況下,Symfony自動為你嵌入一個合法的CSRF令牌。這就意味著你不需要做任何事情就可以得到CSRF保護。CSRF保護是通過在你的表單中添加一個隱藏字段,默認的名叫_token。它包含一個值,這個值只有你和你的用戶知道。這確保了是用戶而不是其它實體在提交數據。Symfony自動校驗該token是否存在以及其準確性。

_token 字段是一個隱藏字段并且會自動的渲染,只要你在你的模板中包含了form_rest()函數。它確保了沒有被渲染過的字段全部渲染出來。CSRF令牌可以按照表單來個性化,比如:

class TaskType extends AbstractType{   // ...   public function getDefaultOptions(array $options)   {      return array(          'data_class' => 'Acme/TaskBundle/Entity/Task',          'csrf_protection' => true,          'csrf_field_name' => '_token',          // 一個唯一的鍵值來保證生成令牌          'intention' => 'task_item',      );   }   // ...}

要關閉CSRF保護,設置csrf_protection 選項為false。intentsion選項是可選的,但為不同的表單生成不同的令牌極大的加強了安全性。

使用一個無底層類表單

大多數情況下,一個表單要綁定一個對象的,并且表單中所有的字段獲取或者保存它們的數據到該對象屬性。但有時候,你可能只想使用一個沒有類的表單,返回一個提交數據的數組,這個非常容易實現:

// 確認你在類上方導入了Request對象use Symfony/Component/HttpFoundation/Request// ...public function contactAction(Request $request){    $defaultData = array('message' => 'Type your message here');    $form = $this->createFormBuilder($defaultData)       ->add('name', 'text')       ->add('email', 'email')       ->add('message', 'textarea')       ->getForm();     if ($request->getMethod() == 'POST') {        $form->bindRequest($request);        // 數據是一個數組并包含 "name", "email", 和"message" 鍵        $data = $form->getData();     }    // ... 渲染表單}

默認情況下,一個表單真的假設你想要一個數據數組而不是數據對象。

這里有兩種方式你可以改變它的行為并綁定一個對象;

1.當創建表單時傳入一個對象(作為createFormBuilder的第一個參數或者createForm的第二個參數)。

2.在你的表單中聲明data_class 選項

如果以上兩種方式都沒有,那么表單會返回一個數組數據。在這個示例中因為$defaultData不是一個對象,又沒有設置data_class選項,則$form->getData()最終返回一個數組。

你也可以通過Request對象直接訪問POST的值,

$this->get('request')->request->get('name');

注意,大多數的情況下我們使用getData()方法是更好一點的選擇。因為它返回的是經過表單框架轉換過的數據。

添加校驗規則

唯一遺漏的地方就是校驗規則了,通常當你調用$form->isvalid()時,對象會調用你在類東提供的校驗規則進行校驗。但如果沒有類,你怎么來添加對你表單數據的約束規則呢?答案是自己創建約束,然后傳入到表單。

// 在controller類前導入命名空間use Symfony/Component/Validator/Constraints/Email;use Symfony/Component/Validator/Constraints/MinLength;use Symfony/Component/Validator/Constraints/Collection;$collectionConstraint = new Collection(array(      'name' => new MinLength(5),      'email' => new Email(array('message' => 'Invalid email address')),));// 創建一個表單沒有默認值,傳入約束選項。$form = $this->createFormBuilder(null, array(           'validation_constraint' => $collectionConstraint,    ))->add('email', 'email')// ...;

現在,當你調用$form->bindRequest($request)時,約束就會被創建并作用于你的表單數據。如果你使用表單類,重寫getDefaultOptions 方法來指定可選項:

namespace Acme/TaskBundle/Form/Type;use Symfony/Component/Form/AbstractType;use Symfony/Component/Form/FormBuilder;use Symfony/Component/Validator/Constraints/Email;use Symfony/Component/Validator/Constraints/MinLength;use Symfony/Component/Validator/Constraints/Collection;class ContactType extends AbstractType{  // ...  public function getDefaultOptions(array $options)  {    $collectionConstraint = new Collection(array(      'name' => new MinLength(5),      'email' => new Email(array('message' => 'Invalid email address')),    ));    return array('validation_constraint' => $collectionConstraint);  }}

這樣你有了足夠的靈活性來創建表單類和約束了,它返回一個數據數組而不是一個對象。大多數情況下,這個是不錯的,而綁定一個表單到一個對象,從某種程度上說更加健壯。對于簡單表單來說是個不錯的選擇。

總結思考

你現在已經了解了所有建造復雜功能性的表單所需要的所有建造塊。當生成表單時,記住一個表單的首要目標是從一個對象把數據傳遞給一個HTML表單以方便用戶修改它們。第二個目標就是把用戶提交的數據重寫提交回對象。

希望本文所述對大家基于Symfony框架的PHP程序設計有所幫助。



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品91久久久久久| 一区二区三区 在线观看视| 高跟丝袜一区二区三区| 美女视频黄免费的亚洲男人天堂| 亚洲新声在线观看| 欧美日韩亚洲91| 亚洲欧美中文日韩在线| 国产成人精彩在线视频九色| 国产午夜精品全部视频播放| 91高清在线免费观看| 久久精品成人一区二区三区| 国产日韩欧美视频| 国产精品成人一区二区三区吃奶| 欧美极度另类性三渗透| 国产一区二区日韩精品欧美精品| 亚洲精品久久久久中文字幕二区| 成人做爰www免费看视频网站| 精品亚洲男同gayvideo网站| 91精品国产91久久久久久吃药| 国产99久久久欧美黑人| 欧美日韩亚洲精品内裤| 日韩美女在线观看| 狠狠久久五月精品中文字幕| 日韩成人激情视频| 国内精品久久久久久影视8| 日韩精品视频在线免费观看| 精品无人区太爽高潮在线播放| 亚洲一区二区三区香蕉| 日韩精品高清视频| 国内精品久久久久久久久| 国产精品嫩草视频| 欧美国产视频一区二区| 亚洲已满18点击进入在线看片| 91精品视频免费看| 久久精品99无色码中文字幕| 亚洲香蕉伊综合在人在线视看| 日韩精品在线免费观看视频| 欧美激情亚洲综合一区| 91免费在线视频| 影音先锋欧美精品| 国产精品高清在线观看| 欧美猛少妇色xxxxx| 国产精品久久一区| 91免费版网站入口| 日本久久久久久| 久久久国产成人精品| 狠狠久久亚洲欧美专区| 亚洲va久久久噜噜噜久久天堂| 日韩精品一二三四区| 久久国产精品久久久| 91麻豆国产语对白在线观看| 亚洲韩国欧洲国产日产av| 亚洲三级av在线| 成人免费大片黄在线播放| 亚洲人午夜精品免费| 欧美国产日韩视频| 欧美乱妇高清无乱码| 国产精品久久久久久久久久| 久久精视频免费在线久久完整在线看| 日韩网站免费观看高清| 国产精品福利片| 久久久久久久香蕉网| 青青久久aⅴ北条麻妃| 欧美日韩裸体免费视频| 亚洲欧美日韩国产中文专区| 日韩av手机在线观看| 欧美视频在线免费| 日韩精品极品在线观看| 日本精品久久久久影院| 国产精品久久久久久久天堂| 亚洲男人7777| 国产精品色视频| 久久久久久久久爱| 国产精品激情自拍| 日韩精品在线私人| 高清欧美性猛交| 亚洲欧美日韩直播| 国产91ⅴ在线精品免费观看| 精品国产一区二区三区四区在线观看| 成人黄色片网站| 久久夜精品香蕉| 日韩精品中文字幕在线| 精品国产欧美一区二区五十路| 亚洲一区二区三区香蕉| 欧美激情欧美激情| 成人春色激情网| 97国产一区二区精品久久呦| 日韩精品久久久久| 国产脚交av在线一区二区| 亚洲人成网站免费播放| 成人国产精品一区| 亚洲影影院av| 亚洲国产精品字幕| 国产999精品久久久影片官网| 福利二区91精品bt7086| 国产一区二区三区免费视频| www.国产一区| 国产一区二区黑人欧美xxxx| x99av成人免费| 亚洲成人在线视频播放| 福利一区视频在线观看| 超碰97人人做人人爱少妇| 免费av在线一区| 日韩美女av在线| 中文字幕欧美精品日韩中文字幕| 日韩中文视频免费在线观看| 欧美高清第一页| 欧美激情第1页| 国产精品老女人精品视频| 亚洲欧洲美洲在线综合| 国产精品久久久久久久久久| 亚州av一区二区| 久久国产精品电影| 国产精品手机播放| 国产精品日日摸夜夜添夜夜av| 136fldh精品导航福利| 国产在线视频91| 九九视频这里只有精品| 久久伊人色综合| 136fldh精品导航福利| 亚洲自拍小视频免费观看| 欧美精品一区二区三区国产精品| 亚洲视频欧美视频| 欧美亚洲日本网站| 黑人精品xxx一区一二区| 亚洲人成77777在线观看网| 91中文在线观看| 色偷偷888欧美精品久久久| 国产欧美精品一区二区三区-老狼| 九九久久国产精品| 亚洲视频免费一区| 成人在线一区二区| 久久国内精品一国内精品| 国产成人高清激情视频在线观看| 国产成人免费91av在线| 亚洲国产另类 国产精品国产免费| 日韩精品一区二区视频| 日韩在线视频线视频免费网站| 日韩在线精品视频| 亚洲国产欧美一区二区三区久久| 久久久精品一区| 欧美在线观看视频| 91精品国产九九九久久久亚洲| 成人两性免费视频| 欧美日韩高清在线观看| 欧美午夜精品久久久久久浪潮| 亚洲成人免费在线视频| 国产精品视频资源| 欧日韩不卡在线视频| 亚洲欧美一区二区精品久久久| 国产亚洲免费的视频看| 国产欧洲精品视频| 国产69精品久久久| 国精产品一区一区三区有限在线| 亚洲日本aⅴ片在线观看香蕉| 欧美xxxx18国产| 中文字幕精品av| 亚洲国产三级网| 97**国产露脸精品国产| 亚洲国产91色在线| 亚洲国产毛片完整版| 在线观看日韩视频| 91久久久亚洲精品| 亚洲欧美综合v|