Symfony 4 Form Validation BotDetect CAPTCHA Example

First Time Here?

Check the BotDetect Symfony 4 Captcha Quickstart for key integration steps.

Symfony Form Validation BotDetect CAPTCHA Example shows how to integrate CaptchaBundle in a form. We have also demonstrated how to create the ValidCaptcha constraint in order to validate captcha code user submits.

The brief example is based around a contact form which sends email if user input is considered valid -– a likely real world scenario for Captcha protection integration.

Files for this example are:

Symfony 4 Contact BotDetect Captcha validation screenshot

Config - /config/packages/captcha.php

<?php if (!class_exists('CaptchaConfiguration')) { return; }

// BotDetect PHP Captcha configuration options

return [
  // Captcha configuration for contact page
  'ContactCaptcha' => [
    'UserInputID' => 'captchaCode',
    'CodeLength' => CaptchaRandomization::GetRandomCodeLength(4, 6),
    'ImageStyle' => ImageStyle::AncientMosaic,
  ],

];

In order to use the CaptchaBundle, we have defined Captcha configuration which will be used as a CaptchaType in ContactType form. Detailed description of this approach is available in a BotDetect Symfony 4 integration guide.

Type - /src/Form/Type/ContactType.php

<?php

namespace App\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// Importing the CaptchaType class
use Captcha\Bundle\CaptchaBundle\Form\Type\CaptchaType;

class ContactType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      ->add('name', TextType::class)
      ->add('email', EmailType::class)
      ->add('subject', TextType::class)
      ->add('message', TextareaType::class)
      ->add('captchaCode', CaptchaType::class, array(
        'captchaConfig' => 'ContactCaptcha',
        'label' => 'Retype the characters from the picture'
      ))
      ->add('submit', SubmitType::class)
    ;
  }
}

The above code uses the CaptchaType to add Captcha to contact form. It is required to declare captchaConfig option and assign it a captcha configuration variable defined in config/packages/captcha.php file.

Entity - /src/Entity/Contact.php

<?php namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Captcha\Bundle\CaptchaBundle\Validator\Constraints as CaptchaAssert;

class Contact
{
  /**
   * @Assert\Length(
   *      min = 2,
   *      max = 10,
   *      minMessage = "Your name must be at least {{ limit }} characters long",
   *      maxMessage = "Your name cannot be longer than {{ limit }} characters"
   * )
   */
  protected $name;

  /**
   * @Assert\Email(
   *      message = "The email '{{ value }}' is not a valid email.",
   * )
   */
  protected $email;

  /**
   * @Assert\Length(
   *      min = 1,
   *      max = 50,
   *      minMessage = "Your subject must be at least {{ limit }} characters long",
   *      maxMessage = "Your subject cannot be longer than {{ limit }} characters"
   * )
   */
  protected $subject;

  /**
   * @Assert\Length(
   *      min = 2,
   *      max = 255,
   *      minMessage = "Your message must be at least {{ limit }} characters long",
   *      maxMessage = "Your message cannot be longer than {{ limit }} characters"
   * )
   */
  protected $message;

  /**
   * @CaptchaAssert\ValidCaptcha(
   *      message = "CAPTCHA validation failed, try again."
   * )
   */
  protected $captchaCode;

  public function getCaptchaCode()
  {
    return $this->captchaCode;
  }

  public function setCaptchaCode($captchaCode)
  {
    $this->captchaCode = $captchaCode;
  }

  public function getName()
  {
    return $this->name;
  }

  public function setName($name)
  {
    $this->name = $name;
  }

  public function getEmail()
  {
    return $this->email;
  }

  public function setEmail($email)
  {
    $this->email = $email;
  }

  public function getSubject()
  {
    return $this->subject;
  }

  public function setSubject($subject)
  {
    $this->subject = $subject;
  }

  public function getMessage()
  {
    return $this->message;
  }

  public function setMessage($message)
  {
    $this->message = $message;
  }
}

To validate the captchaCode field in contact form, we have added the ValidCaptcha constraint to Contact Entity.

Controller - /src/Controllers/ContactController.php

<?php

namespace App\Controller;

use App\Entity\Contact;
use App\Form\Type\ContactType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class ContactController extends AbstractController
{
  /**
   * @Route("/contact")
   */
  public function getForm(Request $request)
  {
    // create contact form
    $form = $this->createForm(ContactType::class, new Contact());

    // initially, the message shown to the visitor is empty
    $message = '';

    $form ->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
      // Captcha validation passed
      // TODO: send email
      $message = 'Your message was sent successfully.';
    }

    return $this->render('contact/index.html.twig', array(
      'form' => $form->createView(),
      'message' => $message
    ));
  }
}

When user submits a valid captcha code, isValid() returns true. Before redirecting user, it is possible to perform some actions using the $contactForm object. Code above assigns a success message to the $message variable which will be displayed on a contact page.

View - /templates/contact/index.html.twig

<!DOCTYPE html>
<html>
<head>
  <title>Symfony 4 Form Validation BotDetect CAPTCHA Example</title>
</head>

<body>
  <h2>Symfony 4 Form Validation BotDetect CAPTCHA Example</h2>
  
  {# show message #}
  {% if message is not empty %}
    <p>{{ message }}</p>
  {% endif %}
  
  {{ form_start(form) }}
  {{ form_widget(form) }}
  {{ form_end(form) }}
</body>
</html>

The form has already been created, the next step is render it by using a set of forms helper functions, and to prints out a message (set in Controller) about Captcha validation success.