表单

注册、签到、加入购物车、结账,这些等等都是在网店应用中利用HTML表单等进行的操作。构建表单是开发人员最常见的任务之一。一个经常需要花时间才能做好的工作。

Symfony 有一个表单组件,通过它我们可以用OO的方式来构建HTML表单。该组件本身也是一个独立的库,可以独立于 Symfony 使用。

我们来看看src/AppBundle/Entity/Customer.php文件的内容,我们的 Customer实体类是我们通过控制台定义时自动生成的。

  1. class Customer {
  2. private $id;
  3. private $firstname;
  4. private $lastname;
  5. private $email;
  6. public function getId() {
  7. return $this->id;
  8. }
  9. public function setFirstname($firstname) {
  10. $this->firstname = $firstname;
  11. return $this;
  12. }
  13. public function getFirstname() {
  14. return $this->firstname;
  15. }
  16. public function setLastname($lastname) {
  17. $this->lastname = $lastname;
  18. return $this;
  19. }
  20. public function getLastname() {
  21. return $this->lastname;
  22. }
  23. public function setEmail($email) {
  24. $this->email = $email;
  25. return $this;
  26. }
  27. public function getEmail() {
  28. return $this->email;
  29. }
  30. }

这里我们有一个普通的PHP类,它没有扩展任何东西,也没有以任何其他方式与 Symfony 链接。它代表了一个单一的客户实体,并为其设置和获取数据。有了这个实体类,我们想渲染一个表单,以获取我们的类所使用的所有相关数据。这就是表单组件的作用。

当我们之前通过控制台使用CRUD生成器时,它在src/AppBundle/Form/CustomerType.php文件中为我们的Customer实体创建了Form类,内容如下:

  1. namespace AppBundle\Form;
  2. use Symfony\Component\Form\AbstractType;
  3. use Symfony\Component\Form\FormBuilderInterface;
  4. use Symfony\Component\OptionsResolver\OptionsResolver;
  5. class CustomerType extends AbstractType
  6. {
  7. public function buildForm(FormBuilderInterface $builder, array $options) {
  8. $builder
  9. ->add('firstname')
  10. ->add('lastname')
  11. ->add('email')
  12. ;
  13. }
  14. public function configureOptions(OptionsResolver $resolver) {
  15. $resolver->setDefaults(array(
  16. 'data_class' =>'AppBundle\Entity\Customer'
  17. ));
  18. }
  19. }

我们可以看到表单组件背后的简单性归结为以下几点:

  • 扩展表格类型。我们从Symfony\Component\Form/AbstractType类中扩展出来。
  • 实现 buildForm 方法。这就是我们添加实际字段的地方,我们希望在表格上显示的字段。
  • 实现 configureOptions。这至少指定了指向我们客户实体的data_class配置。

表单生成器对象在这里做的是繁重的工作。它不需要太多的时间就可以创建一个表单。有了表单类之后,让我们来看看负责给模板输入表单的控制器动作。在本例中,我们将关注src/AppBundle/Controller/CustomerController.php文件中的newAction,内容如下所示:

  1. $customer = new Customer();
  2. $form = $this->createForm('AppBundle\Form\CustomerType', $customer);
  3. $form->handleRequest($request);
  4. if ($form->isSubmitted() && $form->isValid()) {
  5. $em = $this->getDoctrine()->getManager();
  6. $em->persist($customer);
  7. $em->flush();
  8. return $this->redirectToRoute('customer_show', array('id' =>$customer->getId()));
  9. }
  10. return $this->render('customer/new.html.twig', array(
  11. 'customer' => $customer,
  12. 'form' => $form->createView(),
  13. ));

前面的代码首先实例化了Customer实体类。$this->createForm(...)实际上是调用$this->container->get('form.factory')->create(...),传递给它我们的表单类名称和customer对象的实例,然后我们有isSubmittedisValid检查,看看这是否是GET或有效的POST请求。根据这个检查,代码要么返回客户列表,要么将表单和客户实例设置为使用模板customer/new.html.twig。关于实际的验证,我们会在后面再讲。

最后,让我们来看看app/Resources/views/customer/new.html.twig文件中的实际模板:

  1. {% extends 'base.html.twig' %}
  2. {% block body %}
  3. <h1>Customer creation</h1>
  4. {{ form_start(form) }}
  5. {{ form_widget(form) }}
  6. <input type="submit" value="Create" />
  7. {{ form_end(form) }}
  8. <ul>
  9. <li>
  10. <a href="{{ path('customer_index') }}">Back to the list</a>
  11. </li>
  12. </ul>
  13. {% endblock %}

在这里我们看到了extendsblock标签,以及一些相关的表单功能。Symfony 在Twig中增加了以下几个表单渲染功能:

  • form(view, variables)
  • form_start(view, variables)
  • form_end(view, variables)
  • form_label(view, label, variables)
  • form_errors(view)
  • form_widget(view, variables)
  • form_row(view, variables)
  • form_rest(view, variables)

我们的大多数申请表格都会像这样自动生成,所以我们能够得到一个功能完备的CRUD,而不需要太深入地了解表格的其他功能。