form
For a web developer, processing HTML forms is one of the most common and challenging tasks. Symfony integrates a Form component to make processing forms easy. In this chapter, you will create a complex form from scratch and learn the important features of the form library.
Symfony's Form component is an independent class library that you can use outside of your Symfony project. Refer to the Form component documentation to learn more.
Create a simple form ¶
Suppose you are building a simple to-do list to display some "tasks". You need to create a form to let your users edit and create tasks. Before that, let's take a look at the Task
class, which can present and store data for a single task.
// 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; }}
This is a native PHP object class, because it does not interact with Symfony or reference other libraries. It is a very simple PHP object class that directly solves the data problem of task (task) in your
program. Of course, by the end of this chapter, you will be able to submit data to a Task
instance via an HTML form, validate its value, and persist it to the database.
Building the form ¶
Now that you have created a Task
class, the next step is to create and render a real html form . In Symfony, this is done by building a form object and rendering it to the template. Now, you can do all this in the controller:
// 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 // 创建一个task对象,赋一些例程中的假数据给它 $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(), )); }}
This example shows how to build your form directly in the controller. In the following Create form class, you will use an independent class to build the form. This method is recommended because the form can be reused.
Creating a form does not require a lot of code because Symfony's form objects are created through a "form builder". The purpose of the form builder is to let you write simple form creation "instructions", and all "overloading" tasks when actually creating the form are completed by the builder.
In this example, you have added two fields to the form, task
and dueDate
. Corresponding to the task
and dueDate
attributes in the Task
class. You have specified the "type" of FQCN (Full Quilified Class Name/full path class name) (such as TextType
, DateType
) for them respectively. The type determines which one is generated for the field. HTML form tags (tag groups).
Finally, you add a submit button with a custom label to submit the form to the server.
Symfony comes with a number of built-in types, which will be briefly introduced (see Built-in form types below).
Rendering the form ¶
After the form is created, the next step is to render it. This is done by passing a specific form "view" object (note the $form->createView()
method in the example controller above) to your template, and through a series of form helper functions ( helper function).
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) ?>
This example assumes that you submit the form with a "POST" request and submit it to the same URL as the "Form Display (Page)". Later you'll learn how to change the request method and the target URL after form submission.
That's it! Only three lines are needed to render the complete form:
form_start(form)
- Render the start tag of the form, including when using file upload The correct enctype attribute.
form_widget(form)
- Renders all fields, including the field element itself, field label, and any error information for field validation.
form_end(form)
- When you manually generate each field, it can render the form end tag as well as all the fields in the form that have not yet been rendered. This is useful when rendering hidden fields and taking advantage of automatic CSRF Protection Very useful when using the protection mechanism.
It’s that simple, but not very flexible (for now). Typically, you want to render each field in a form individually to control the style of the form. You will master this method in the later How to control form rendering article.
Before continuing, please note why the rendered task
input box has a property value from the $task
object (i.e. "Write a blog post"). This is the first task of the form: getting data from an object and converting it into an appropriate format so that it can be rendered in an HTML form.
The form system is smart enough that they access the Task
class through the getTask()
and setTask()
methods Protected task
attribute. Unless it is a public property, must have a "getter" and "setter" method defined so that the form component can get and write data from these properties. For boolean properties, you can use an "isser" and "hasser" method (such as isPublished()
and hasReminder()
) instead of a getter method (getPublished( )
and getReminder()
).
Processing form submission ¶
By default, the form will submit the POST request back to the "same controller that rendered it".
Here, the second task of the form is to transfer the data submitted by the user back to the properties of an object. To do this, the data submitted by the user must be written to the form object. Add the following functionality to the Controller:
// ...use Symfony\Component\HttpFoundation\Request; public function newAction(Request $request){ // just setup a fresh $task object (remove the dummy data) // 直接设置一个全新$task对象(删除了假数据) $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! // 一些操作,比如把任务存到数据库中 // 例如,如果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(), ));}
Note that the createView()
method should be called in handleRequest
After call again. Otherwise, modifications to *_SUBMIT
form events will not be applied to the view layer (such as error messages during validation).
The controller follows a common pattern when processing the form. It has three possible ways:
When the browser initially loads a page, the form is created and rendered.
handleRequest()
Realizes that the form has not been submitted and does nothing. If the form is not submitted,isSubmitted()
returns false;When the user submits the form,
handleRequest( )
will recognize this action and immediately write the submitted data to thetask
anddueDate
properties of the$task
object. The object is then validated. If it is invalid (validation is in the next chapter),isValid()
will returnfalse
, and the form will be rendered again, only this time with validation errors;When the user submits the form with legal data, the submitted data will be written to the form again, but this time
isValid()
Returntrue
. Before redirecting the user to some other page (such as a "Thank you" or "Success" page), you have the opportunity to perform some operations with the$task
object (such as persisting it to a database ).Redirecting the user after the form is successfully submitted is to prevent the user from repeatedly submitting data through the browser "refresh" button.
If you need to precisely control when a form is submitted, or what data is passed to the form, you can use submit(). For more information, please refer to Manually calling Form::submit().
Form Validation ¶
In the previous section, you learned how a form with valid or invalid data is submitted. In Symfony, the verification process is performed in the underlying object (such as Task
). In other words, the question is not whether the "form" is valid, but whether the $task
object is valid after "the submitted data is applied to the form". Calling $form->isvalid()
is a shortcut to ask the underlying $task
object whether it has obtained valid data.
Validation is accomplished by adding a set of rules (called "constraints/constraints") to a class. We add rules and constraints to the Task
class so that the task attribute cannot be empty and the duDate
field is not empty and must be a valid DateTime object.
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') ); }}
That’s it! If you now resubmit the form with invalid data, you will see the appropriate errors printed to the form.
Verification is a very powerful feature of Symfony, it has its own exclusive chapter.
Built-in field types ¶
The standard version of Symfony contains a huge number of field types, covering all conventional form fields and data types you can encounter.
Text type field ¶
- TextType
- TextareaType
- EmailType
- ##IntegerType
- MoneyType
- NumberType
- ##PasswordType
- PercentType
- SearchType ##UrlType
- RangeType
- Selective field ¶
- ChoiceType
- EntityType
- ##CountryType
- LanguageType
- ##LocaleType
- TimezoneType
- CurrencyType
- Date and time fields ¶
DateType
CheckboxType
##CollectionType
Hidden Field ¶
##Button¶
##FormType
- You can also define your own field types. Refer to How to create a custom form field type .
Field type options ¶
Each field type has a certain number of options for configuration. For example, the dueDate field is currently rendered as 3 select boxes. The
DateType date field can be configured to render as a single text box (the user can enter a string as a date).
1
->add('dueDate', DateType::class, array('widget' => 'single_text')) | #Each field type has a different set of options for passing in the type. Details about field types can be found in the documentation for each type. Field type guessing ¶Now you have added validation metadata (annotation: annotation) to public function newAction(){ $task = new Task(); $form = $this->createFormBuilder($task) ->add('task') ->add('dueDate', null, array('widget' => 'single_text')) ->add('save', SubmitType::class) ->getForm();} "Guess" is activated when you omit the second parameter of the If your form uses a specific validation group, guessing the field type will still consider all validation constraints (including those that do not constraints belonging to this "in use" validation group). Guess options for field types ¶In addition to guessing field types, Symfony can also try to guess the correct values for field options. When these options are set, fields will be rendered with special HTML attributes for use with HTML5 client-side validation. However, they do not generate corresponding validation rules (such as
These field options are only used when you are using Symfony for type guessing (i.e., omit the argument, or pass in If you want to change a guessed (option) value, you can pass this item in the options array of the field type to override it.
|