亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

表單

對一個Web開發(fā)者來說,處理HTML表單是一個最為普通又極具挑戰(zhàn)的任務(wù)。Symfony整合了一個Form組件,讓處理表單變得容易起來。在本章,你將從零開始創(chuàng)建一個復(fù)雜的表單,學(xué)習(xí)表單類庫中的重要功能。

Symfony的Form組件是一個獨立的類庫,你可以在Symfony項目之外使用它。參考 Form組件文檔 以了解更多。

創(chuàng)建一個簡單的表單 ?

假設(shè)你正在構(gòu)建一個簡單的待辦事項列表,來顯示一些“任務(wù)”。你需要創(chuàng)建一個表單來讓你的用戶編輯和創(chuàng)建任務(wù)。在這之前,先來看看 Task 類,它可呈現(xiàn)和存儲一個單一任務(wù)的數(shù)據(jù)。

// src/AppBundle/Entity/Task.phpnamespace AppBundle\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;
    }}

這是一個原生的PHP對象類,因為它沒有和Symfony互動也沒有引用其它類庫。它是非常簡單的一個PHP對象類,直接解決了 程序中的 task (任務(wù))之?dāng)?shù)據(jù)問題。當(dāng)然,在本章的最后,你將能夠通過HTML表單把數(shù)據(jù)提交到一個 Task 實例,驗證它的值,并把它持久化到數(shù)據(jù)庫。

構(gòu)建表單 ?

現(xiàn)在你已經(jīng)創(chuàng)建了一個 Task 類,下一步就是創(chuàng)建和渲染一個真正的html表單了。在Symfony中,這是通過構(gòu)建一個表單對象并將其渲染到模版來完成的?,F(xiàn)在,在控制器里即可完成所有這些:

// src/AppBundle/Controller/DefaultController.phpnamespace AppBundle\Controller; use AppBundle\Entity\Task;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\Form\Extension\Core\Type\TextType;use Symfony\Component\Form\Extension\Core\Type\DateType;use Symfony\Component\Form\Extension\Core\Type\SubmitType; class DefaultController extends Controller{
    public function newAction(Request $request)
    {
        // create a task and give it some dummy data for this example
        // 創(chuàng)建一個task對象,賦一些例程中的假數(shù)據(jù)給它
        $task = new Task();
        $task->setTask('Write a blog post');
        $task->setDueDate(new \DateTime('tomorrow'));         $form = $this->createFormBuilder($task)
            ->add('task', TextType::class)
            ->add('dueDate', DateType::class)
            ->add('save', SubmitType::class, array('label' => 'Create Task'))
            ->getForm();         return $this->render('default/new.html.twig', array(
            'form' => $form->createView(),
        ));
    }}

這個例子說明了如何直接在控制器中構(gòu)建你的form(表單)。后面的 創(chuàng)建表單類 中,你將使用一個獨立的類來構(gòu)建表單,這種方法被推薦,因為表單可以復(fù)用。


創(chuàng)建表單不需要很多代碼,因為Symfony的表單對象是通過一個“form builder(表單生成器)”來創(chuàng)建的。form builder的目的是讓你編寫簡單的表單創(chuàng)建“指令”,而真實創(chuàng)建表單時的全部“重載”任務(wù)則交由builder完成。

本例中,你已經(jīng)添加了兩個字段到表單,即 taskdueDate 。對應(yīng)的是 Task 類中的 taskdueDate 屬性。你已為它們分別指定了FQCN(Full Quilified Class Name/完整路徑類名)的“類型”(如 TextType , DateType ),由類型決定為字段生成哪一種HTML表單標(biāo)簽(標(biāo)簽組)。

最后,你添加了一個帶有自定義label的提交按鈕以向服務(wù)器提交表單。

Symfony附帶了許多內(nèi)置類型,它們將被簡短地介紹(見下面的內(nèi)置表單類型)。

渲染表單 ?

表單創(chuàng)建之后,下一步就是渲染它。這是通過傳遞一個特定的表單“view”對象(注意上例控制器中的  $form->createView() 方法)到你的模板,并通過一系列的表單helper function(幫助函數(shù))來實現(xiàn)的。

TWIG:{# app/Resources/views/default/new.html.twig #}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
PHP:<!-- app/Resources/views/default/new.html.php -->
<?php echo $view['form']->start($form) ?>
<?php echo $view['form']->widget($form) ?>
<?php echo $view['form']->end($form) ?>

1465202253_36066_5344_form-simple.png

本例假設(shè)你以"POST"請求提交表單,并且提交到和“表單顯示(頁面)”相同的URL。后面你將學(xué)習(xí)如何改變請求方法(request method)和表單提交后的目標(biāo)URL。

就是這樣!只需要三行就可以渲染出完整的form表單:

  • form_start(form)
  • 渲染表單的開始標(biāo)簽,包括在使用文件上傳時的正確enctype屬性。
  • form_widget(form)
  • 渲染出全部字段,包含字段元素本身,字段label以及字段驗證的任何錯誤信息。
  • form_end(form)
  • 當(dāng)你手動生成每個字段時,它可以渲染表單結(jié)束標(biāo)簽以及表單中所有尚未渲染的字段。這在渲染隱藏字段以及利用自動的 CSRF Protection 保護(hù)機(jī)制時非常有用。

就是這么簡單,但不太靈活(暫時)。通常情況下,你希望單獨渲染出表單中的每一個字段,以便控制表單的樣式。你將在后面 如何去控制表單渲染 文章中掌握這種方法。

在繼續(xù)下去之前,請注意,為什么渲染出來的 task 輸入框中有一個來自 $task 對象的屬性值(即“Write a blog post”)。這是表單的第一個任務(wù):從一個對象中獲取數(shù)據(jù)并把它轉(zhuǎn)換成一種適當(dāng)?shù)母袷剑员阍贖TML表單中被渲染。

表單系統(tǒng)足夠智能,它們通過 getTask()setTask() 方法來訪問 Task 類中受保護(hù)的 task 屬性。除非是public屬性,否則 必須 有一個 "getter" 和 "setter" 方法被定義,以便表單組件能從這些屬性中獲取和寫入數(shù)據(jù)。對于布爾型的屬性,你可以使用一個 "isser" 和 "hasser" 方法(如  isPublished()hasReminder() )來替代getter方法(getPublished()getReminder())。

處理表單提交 ?

默認(rèn)時,表單會把POST請求,向“渲染它的同一個控制器”提交回去。

此處,表單的第二個任務(wù)就是把用戶提交的數(shù)據(jù)傳回到一個對象的屬性之中。要做到這一點,用戶提交的數(shù)據(jù)必須寫入表單對象才行。向控制器(Controller)中添加以下功能:

// ...use Symfony\Component\HttpFoundation\Request; public function newAction(Request $request){
    // just setup a fresh $task object (remove the dummy data)
    // 直接設(shè)置一個全新$task對象(刪除了假數(shù)據(jù))
    $task = new Task();     $form = $this->createFormBuilder($task)
        ->add('task', TextType::class)
        ->add('dueDate', DateType::class)
        ->add('save', SubmitType::class, array('label' => 'Create Task'))
        ->getForm();     $form->handleRequest($request);     if ($form->isSubmitted() && $form->isValid()) {         // $form->getData() holds the submitted values
        // but, the original `$task` variable has also been updated
        //  $form->getData() 持有提交過來的值
        // 但是,原始的 `$task` 變量也已被更新了
        $task = $form->getData();         // ... perform some action, such as saving the task to the database
        // for example, if Task is a Doctrine entity, save it!
        // 一些操作,比如把任務(wù)存到數(shù)據(jù)庫中
        // 例如,如果Tast對象是一個Doctrine entity,存下它!
        // $em = $this->getDoctrine()->getManager();
        // $em->persist($task);
        // $em->flush();         return $this->redirectToRoute('task_success');
    }     return $this->render('default/new.html.twig', array(
        'form' => $form->createView(),
    ));}

注意 createView() 方法應(yīng)該在 handleRequest 被調(diào)用 之后 再調(diào)用。否則,針對 *_SUBMIT 表單事件的修改,將不會應(yīng)用到視圖層(比如驗證時的錯誤信息)。


控制器(controller)在處理表單時遵循的是一個通用模式(common pattern),它有三個可能的途徑:

  1. 當(dāng)瀏覽器初始加載一個頁面時,表單被創(chuàng)建和渲染。handleRequest() 意識到表單沒有被提交進(jìn)而什么都不做。如果表單未被提交,isSubmitted() 返回false;

  2. 當(dāng)用戶提交表單時,handleRequest() 會識別這個動作并立即將提交的數(shù)據(jù)寫入到 $task 對象的 task and dueDate 屬性。然后該對象被驗證。如果它是無效的(驗證在下一章),isValid() 會返回 false,進(jìn)而表單被再次渲染,只是這次有驗證錯誤;

  3. 當(dāng)用戶以合法數(shù)據(jù)提交表單的時,提交的數(shù)據(jù)會被再次寫入到表單,但這一次 isValid() 返回 true。在把用戶重定向到其他一些頁面之前(如一個“謝謝”或“成功”的頁面),你有機(jī)會用 $task 對象來進(jìn)行某些操作(比如把它持久化到數(shù)據(jù)庫)。

    表單成功提交之后的重定向用戶,是為了防止用戶通過瀏覽器“刷新”按鈕重復(fù)提交數(shù)據(jù)。

如果你需要精確地控制何時表單被提交,或哪些數(shù)據(jù)被傳給表單,你可以使用 submit()。更多信息請參考 手動調(diào)用Form::submit()。

表單驗證 ?

在上一節(jié)中,你了解了附帶了有效或無效數(shù)據(jù)的表單是如何被提交的。在Symfony中,驗證環(huán)節(jié)是在底層對象中進(jìn)行的(例如 Task)。換句話說,問題不在于“表單”是否有效,而是 $task 對象在“提交的數(shù)據(jù)應(yīng)用到表單”之后是否合法。調(diào)用 $form->isvalid()  是一個快捷方式,詢問底層  $task 對象是否獲得了合法數(shù)據(jù)。

驗證(validation)是通過把一組規(guī)則(稱之為“constraints/約束”)添加到一個類中來完成的。我們給 Task 類添加規(guī)則和約束,使task屬性不能為空, duDate 字段不空且必須是一個有效的DateTime對象。

Annotations:// src/AppBundle/Entity/Task.phpnamespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; class Task{
    /**
     * @Assert\NotBlank()
     */
    public $task;     /**
     * @Assert\NotBlank()
     * @Assert\Type("\DateTime")
     */
    protected $dueDate;}
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Task:
    properties:
        task:
            - NotBlank: ~
        dueDate:
            - NotBlank: ~
            - Type: \DateTime
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\Task">
        <property name="task">
            <constraint name="NotBlank" />
        </property>
        <property name="dueDate">
            <constraint name="NotBlank" />
            <constraint name="Type">\DateTime</constraint>
        </property>
    </class></constraint-mapping>
PHP:// src/AppBundle/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')
        );
    }}

就是這樣!如果你現(xiàn)在重新以非法數(shù)據(jù)提交表單,你將會看到相應(yīng)的錯誤被輸出到表單。

驗證是Symfony一個非常強(qiáng)大的功能,它擁有自己的專屬章節(jié)

html5驗證

HTML5以來,許多瀏覽器都原生支持了客戶端的驗證約束。最常用的驗證之激活方式,是在一個必填字段上渲染一個 required 屬性(譯注:文檔中的“渲染”二字,對應(yīng)英文rendering,可以理解為“輸出”。在Symfony中,把從程序底層或控制中向視圖層顯示內(nèi)容的過程,稱為render)。對于支持HTML5的瀏覽器來說,如果用戶嘗試提交一個空字段到表單時,會有一條瀏覽器原生信息顯示出來。

生成出來的表單充分利用了這個新功能,通過添加一些有意義的HTML屬性來觸發(fā)驗證??蛻舳蓑炞C,也可通過把 novalidate 屬性添加到 form 標(biāo)簽,或是把 formnovalidate 添加到submit標(biāo)簽來關(guān)閉之。這在你想要測試服務(wù)器端的驗證規(guī)則(validation constraints)卻被瀏覽器端阻止,例如,在提交空白字段時,就非常有用。

PHP:<!-- app/Resources/views/default/new.html.php --><?php echo $view['form']->form($form, array(
    'attr' => array('novalidate' => 'novalidate'),)) ?>


Twig:{# app/Resources/views/default/new.html.twig #}
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}

內(nèi)置字段類型 ?

Symfony標(biāo)準(zhǔn)版內(nèi)含巨量字段類型,涵蓋了你所能遇到的全部常規(guī)表單字段和數(shù)據(jù)類型。

文本型字段 ?

選擇型字段 ?

日期和時間字段 ?

其他類型字段 ?

字段群 ?

隱藏字段 ?

按鈕 ?

表單字段基類 ?

你也可以定義自己的字段類型。參考 如何創(chuàng)建一個自定義的表單字段類型。

字段類型選項 ?

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

1
->add('dueDate', DateType::class, array('widget' => 'single_text'))

-simple2.png

每一種字段類型都有一系列不同的選項用于傳入此類型。關(guān)于字段類型的細(xì)節(jié)都可以在每種類型的文檔中找到。

required選項

最常用的是 required 選項,它可以應(yīng)用于任何字段。默認(rèn)情況下它被設(shè)置為 true 。這就意味著支持HTML5的瀏覽器會使用客戶端驗證來判斷字段是否為空。如果你不想需要這種行為,要么 關(guān)閉HTML5驗證,要么把字段的 required 選項設(shè)置為 false。

->add('dueDate', DateType::class, array(
    'widget' => 'single_text',
    'required' => false))

要注意設(shè)置 requiredtrue 意味著服務(wù)器端驗證會被使用。換句話說,如果用戶提交一個空值(blank)到該字段(比如在老舊瀏覽器中,或是使用web service時),這個空值當(dāng)被作為有效值予以采納,除非你使用了Symfony的 NotBlank 或者 NotNull 驗證約束。

也就是說, required 選項是很 "nice",但是服務(wù)端驗證卻應(yīng)該 始終 使用。

label選項

表單字段可以使用label選項來設(shè)置表單字段的label,它適用于任何字段:

->add('dueDate', DateType::class, array(
    'widget' => 'single_text',
    'label'  => 'Due Date',))

字段的label也可以在模版渲染表單時進(jìn)行設(shè)置,見下文。如果你不需要把label關(guān)聯(lián)到你的input(標(biāo)簽),你可以設(shè)置選項值為 false 。

字段類型猜測 ?

現(xiàn)在你已經(jīng)添加了驗證元數(shù)據(jù)(譯注:即annotation)到 Task 類,Symfony對于你的字段已有所了解。如果你允許,Symfony可以“猜到”你的字段類型并幫你設(shè)置好。在下面的例子中,Symfony可以根據(jù)驗證規(guī)則猜測到 task 字段是一個標(biāo)準(zhǔn)的 TextType 字段, dueDateDateType 字段。

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

當(dāng)你省略了 add() 方法的第二個參數(shù)(或者你輸入 null )時,“猜測”會被激活。如果你輸入一個選項數(shù)組作為第三個參數(shù)(比如上面的 dueDate),這些選項將應(yīng)用于被猜測的字段。

如果你的表單使用了一個特定的驗證組(validation group),猜測字段類型時仍將考慮 所有 驗證約束(包括不屬于這個“正在使用中”的驗證組的約束)。

對字段類型的選項進(jìn)行猜測 ?

除了猜測字段類型,Symfony還可嘗試猜出字段選項的正確值。

當(dāng)這些選項被設(shè)置時,字段將以特殊的HTML屬性進(jìn)行渲染,以用于HTML5的客戶端驗證。然而,它們不會在服務(wù)端生成相應(yīng)的驗證規(guī)則(如 Assert\Length )。盡管你需要手動地添加這些服務(wù)器端的規(guī)則,這些字段類型的選項接下來可以根據(jù)這些規(guī)則被猜出來。

  • required
  • required 選項可以基于驗證規(guī)則 (如,該字段是否為 NotBlankNotNull) 或者是Doctrine的metadata元數(shù)據(jù) (如,該字段是否為 nullable) 而被猜出來。這非常有用,因為你的客戶端驗證將自動匹配到你的驗證規(guī)則。
  • max_length
  • 如果字段是某些列文本型字段,那么 max_length 選項可以基于驗證約束 (字段是否應(yīng)用了 LengthRange) 或者是Doctrine元數(shù)據(jù) (通過該字段的長度) 而被猜出來。

這些字段選項 在你使用Symfony進(jìn)行類型猜測時(即,忽略參數(shù),或傳入null 作為 add() 方法的第二個參數(shù))才會被猜測。

如果你希望改變某個被猜出來的(選項)值,可以在字段類型的選項數(shù)組中傳入此項進(jìn)行覆寫。

1
->add('task', null, array('attr' => array('maxlength' => 4)))

創(chuàng)建表單類 ?

正如你看到的,表單可以直接在控制器中被創(chuàng)建和使用。然而,一個更好的做法,是在一個單獨的PHP類中創(chuàng)建表單。它能在你程序中的任何地方復(fù)用。創(chuàng)建一個持有“構(gòu)建task表單”所需邏輯的新類:

// src/AppBundle/Form/Type/TaskType.phpnamespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\Form\Extension\Core\Type\SubmitType; class TaskType extends AbstractType{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('task')
            ->add('dueDate', null, array('widget' => 'single_text'))
            ->add('save', SubmitType::class)
        ;
    }}

這個新類包含了創(chuàng)建task表單所需要的方方面面。它可用于在控制器中快速創(chuàng)建表單。

// src/AppBundle/Controller/DefaultController.phpuse AppBundle\Form\Type\TaskType; public function newAction(){
    $task = ...;
    $form = $this->createForm(TaskType::class, $task);     // ...}

把表單邏輯置于它自己的類中,可以讓表單很容易地在你的項目任何地方復(fù)用。這是創(chuàng)建表單最好的方式,但是決定權(quán)在你。

設(shè)置data_class

每個表單都需要知道“持有底層數(shù)據(jù)的類”的名稱(如 AppBundle\Entity\Task )。通常情況下,這是根據(jù)傳入 createForm 方法的第二個參數(shù)來猜測的(例如 $task )。以后,當(dāng)你開始嵌入表單時,這便不再夠用。因此,雖然不是絕對必須,但通過添加下面代碼到你的表單類型類中,以顯式地指定 data_class 選項是一個好辦法。

use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver){
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Task',
    ));}

當(dāng)把表單映射成對象時,所有的字段都將被映射。表單中的任何字段如果在映射對象上“不存在”,都會拋出異常。

當(dāng)你需要在表單中使用附加字段(如,一個 “你是否同意這些聲明?”的復(fù)選框)而這個字段將不被映射到底層對象時,你需要設(shè)置 mapped 選項為 false

use Symfony\Component\Form\FormBuilderInterface; public function buildForm(FormBuilderInterface $builder, array $options){
    $builder
        ->add('task')
        ->add('dueDate', null, array('mapped' => false))
        ->add('save', SubmitType::class)
    ;}

另外,若表單的任何字段未包含在提交過來的數(shù)據(jù)中,那么這些字段將被顯式設(shè)置為 null

在控制器中我們可以訪問字段的data(字段取值):

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

此外,未被映射的字段之?dāng)?shù)據(jù),也可直接修改:

1
$form->get('dueDate')->setData(new \DateTime());

最后的思考 ?

構(gòu)建表單時,牢記首要目標(biāo)是把一個對象(task)的數(shù)據(jù)轉(zhuǎn)換成一個HTML表單,以便用戶能夠修改(表單)取值。第二個目標(biāo)就是要取到用戶提交的數(shù)據(jù),并重新作用于該對象。

還有很多內(nèi)容需要掌握,F(xiàn)orm系統(tǒng)有大量 威力強(qiáng)大 的高級技巧。