Creating a simple contact form with FormType in Symfony 3

Dealing with HTML forms is one of the most common and challenging tasks for a web developer. Symfony integrates a Form component that makes dealing with forms easy.

In this article, you'll learn how to create a basic contact form in Symfony 3 using the FormBuilder and SwiftMailer to send the Email.

Requirements

  • Symfony 3.
  • SwiftMailer Bundle (which comes usually pre-installed and all the symfony distributions).

Implementation

The use of a FormType is encouraged as it is the correct workflow when using Symfony. It will help you to deal easily with the customization of errors, properties easily.

Creating a FormType for the contact form

The following class contains the ContactType class which will be used to create the form in the controller later.

<?php
// your-path-to-types/ContactType.php

namespace myapplication\myBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;

class ContactType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class, array('attr' => array('placeholder' => 'Your name'),
                'constraints' => array(
                    new NotBlank(array("message" => "Please provide your name")),
                )
            ))
            ->add('subject', TextType::class, array('attr' => array('placeholder' => 'Subject'),
                'constraints' => array(
                    new NotBlank(array("message" => "Please give a Subject")),
                )
            ))
            ->add('email', EmailType::class, array('attr' => array('placeholder' => 'Your email address'),
                'constraints' => array(
                    new NotBlank(array("message" => "Please provide a valid email")),
                    new Email(array("message" => "Your email doesn't seems to be valid")),
                )
            ))
            ->add('message', TextareaType::class, array('attr' => array('placeholder' => 'Your message here'),
                'constraints' => array(
                    new NotBlank(array("message" => "Please provide a message here")),
                )
            ))
        ;
    }

    public function setDefaultOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'error_bubbling' => true
        ));
    }

    public function getName()
    {
        return 'contact_form';
    }
}

Note: change the namespace according to the location of the FormType in your bundle and save it to use it in the next step.

Creating the view in Twig

Now, the view (in this case that will be rendered via twig), should be basic to test :

{# contact.html.twig #}

{{ form_start(form) }}

    <div>
        {{ form_widget(form.subject) }}
        {{ form_errors(form.subject) }}
    </div>
    <div>
        {{ form_widget(form.name) }}
        {{ form_errors(form.name) }}
    </div>
    <div>
        {{ form_widget(form.email) }}
        {{ form_errors(form.email) }}
    </div>
    <div>
        {{ form_widget(form.message) }}
        {{ form_errors(form.message) }}
    </div>

    {# Render CSRF token etc .#}
    <div style="display:none">
        {{ form_rest(form) }}
    </div>
    
    <input type="submit" value="Submit">
    
{{ form_end(form) }}

Creating the controller

Now, comes the most important point in the tutorial, the controller that will handle our form.

As usual, your action in the controller should already have a path in the routing.yml file and it targets to it :

myapplication_contact:
    path:     /contact
    defaults: { _controller: myBundle:Default:contact }

Finally, our controller (with the contact action) should look like :

<?php

namespace myapplication\myBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    public function contactAction(Request $request)
    {
        // Create the form according to the FormType created previously.
        // And give the proper parameters
        $form = $this->createForm('myapplication\myBundle\Form\ContactType',null,array(
            // To set the action use $this->generateUrl('route_identifier')
            'action' => $this->generateUrl('myapplication_contact'),
            'method' => 'POST'
        ));

        if ($request->isMethod('POST')) {
            // Refill the fields in case the form is not valid.
            $form->handleRequest($request);

            if($form->isValid()){
                // Send mail
                if($this->sendEmail($form->getData())){

                    // Everything OK, redirect to wherever you want ! :
                    
                    return $this->redirectToRoute('redirect_to_somewhere_now');
                }else{
                    // An error ocurred, handle
                    var_dump("Errooooor :(");
                }
            }
        }

        return $this->render('myBundle:Default:contact.html.twig', array(
            'form' => $form->createView()
        ));
    }

    private function sendEmail($data){
        $myappContactMail = 'mycontactmail@mymail.com';
        $myappContactPassword = 'yourmailpassword';
        
        // In this case we'll use the ZOHO mail services.
        // If your service is another, then read the following article to know which smpt code to use and which port
        // http://ourcodeworld.com/articles/read/14/swiftmailer-send-mails-from-php-easily-and-effortlessly
        $transport = \Swift_SmtpTransport::newInstance('smtp.zoho.com', 465,'ssl')
            ->setUsername($myappContactMail)
            ->setPassword($myappContactPassword);

        $mailer = \Swift_Mailer::newInstance($transport);
        
        $message = \Swift_Message::newInstance("Our Code World Contact Form ". $data["subject"])
        ->setFrom(array($myappContactMail => "Message by ".$data["name"]))
        ->setTo(array(
            $myappContactMail => $myappContactMail
        ))
        ->setBody($data["message"]."<br>ContactMail :".$data["email"]);
        
        return $mailer->send($message);
    }
}

The contactAction is a normal and typical symfony 3 Form. It validates if the form has been submitted with the POST method, then it checks if the given constraints given in the ContactType are correct. If they're valid, proceed to send the email using the sendEmail function.

FormType symfony 3

Conclusion

  • Note that the sendEmail function is a straight-forward implementation use of SwiftMailer. We've just used the implementation with Zoho Mail, if you use another email provider you need to change the SMTP address and check if your service requires SSL. You can read how to send mails from SwiftMailer to different emails providers in this article. In the function, you just need to change the mail variable and the password and you'll receive an email sent from the same direction that you receive (mymail@mail.com sends to mymail@mail.com).
  • You can add more fields easily to your form in the FormType, to retrieve them in the sendEmail function (which becomes an array from $form->getData) use it's key value.
  • You can use Google Recaptcha in your forms too if you want to prevent robots etc. Read the following article to learn how to implement recaptcha in a Symfony 3 form.
  • The use of the Symfony Form component is more reliable than implement a form by yourself without CSRF protection and it provides an easy way to handle errors.
Become a more social person