Sign up, sign in, add to cart, checkout, all of these and more are actions that make use of HTML forms in web shop applications and beyond. Building forms is one of the most common tasks for developers. One that often takes time to do it right.
Symfony has a form component through which we can build HTML forms in an OO way. The component itself is also a standalone library that can be used independently of Symfony.
Let's take a look at the content of the src/AppBundle/Entity/Customer.php file, our Customer entity class that was auto-generated for us when we defined it via console:
class Customer {
private $id;
private $firstname;
private $lastname;
private $email;
public function getId() {
return $this->id;
}
public function setFirstname($firstname) {
$this->firstname = $firstname;
return $this;
}
public function getFirstname() {
return $this->firstname;
}
public function setLastname($lastname) {
$this->lastname = $lastname;
return $this;
}
public function getLastname() {
return $this->lastname;
}
public function setEmail($email) {
$this->email = $email;
return $this;
}
public function getEmail() {
return $this->email;
}
}Here we have a plain PHP class, which does not extend anything nor is in any other way linked to Symfony. It represents a single customer entity, for which it sets and gets the data. With the entity class in place, we would like to render a form that will pick up all of the relevant data used by our class. This is where the Form component comes in place.
When we used the CRUD generator via console earlier, it created the Form class for our Customer entity within the src/AppBundle/Form/CustomerType.php file with content as follows:
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstname')
->add('lastname')
->add('email')
;
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' =>'AppBundle\Entity\Customer'
));
}
}We can see the simplicity behind the form component comes down to the following:
Symfony\Component\Form\AbstractType classdata_class configuration which points to our Customer entity.The form builder object is the one doing the heavy lifting here. It does not take much for it to create a form. With the form class in place, let's take a look at the controller action in charge of feeding the template with the form. In this case, we will focus on newAction within the src/AppBundle/Controller/CustomerController.php file, with content shown as follows:
$customer = new Customer();
$form = $this->createForm('AppBundle\Form\CustomerType', $customer);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($customer);
$em->flush();
return $this->redirectToRoute('customer_show', array('id' =>$customer->getId()));
}
return $this->render('customer/new.html.twig', array(
'customer' => $customer,
'form' => $form->createView(),
));The preceding code first instantiates the Customer entity class. The $this->createForm(…) is actually calling $this->container->get('form.factory')->create(…), passing it our form class name and instance of customer object. We then have the isSubmitted and isValid check, to see if this is a GET or valid POST request. Based on that check, the code either returns to customer listing or sets the form and customer instance to be used with the template customer/new.html.twig. We will speak more about the actual validation later on.
Finally, lets take a look at the actual template found in the app/Resources/views/customer/new.html.twig file:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Customer creation</h1>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
<a href="{{ path('customer_index') }}">Back to the list</a>
</li>
</ul>
{% endblock %}Here we see extends and block tags, alongside some form of related functions. Symfony adds several form rendering function to Twig as follows:
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)Most of our application forms will be auto-generated like this one, so we are able to get a fully functional CRUD without going too deep into the rest of form functionality.