Skip to content

Forms, validation, CSRF ​

Symfony Forms look heavy the first hour - then you notice CSRF, rendering, and validation constraints live in one place. You already know the pieces from the PHP tutorial.

Form type class ​

php
<?php

namespace App\Form;

use App\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PostType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('title', TextType::class)
            ->add('body', TextareaType::class);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Post::class,
        ]);
    }
}

Controller handle ​

php
<?php

#[Route('/posts/new', name: 'app_posts_new')]
public function new(Request $request, EntityManagerInterface $em): Response
{
    $post = new Post();
    $form = $this->createForm(PostType::class, $post);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $em->persist($post);
        $em->flush();

        return $this->redirectToRoute('app_posts_index');
    }

    return $this->render('posts/new.html.twig', [
        'form' => $form,
    ]);
}

Comments for the code above:

  • handleRequest binds POST data onto the form / entity
  • CSRF is enabled by default on forms - Twig form_start outputs the token
  • isValid() runs constraints (on the form and/or the entity)

Validation constraints ​

On the entity (common):

php
<?php

use Symfony\Component\Validator\Constraints as Assert;

class Post
{
    #[Assert\NotBlank]
    #[Assert\Length(max: 255)]
    private ?string $title = null;
}

Or on the form field via constraints option. Same Validator component either way - think Zod / class-validator energy.

Twig ​

twig
{{ form_start(form) }}
  {{ form_row(form.title) }}
  {{ form_row(form.body) }}
  <button class="btn">Save</button>
{{ form_end(form) }}

form_row = label + widget + errors. Customize later with themes; defaults are fine for learning.

Manual CSRF (APIs / custom forms) ​

For non-Form POSTs you can inject CsrfTokenManagerInterface - same idea as your hand-rolled token in the PHP tutorial. Prefer the Form component when it's an HTML form.

Next: events and subscribers - Symfony's middleware cousin.

Best,

-- ł.

Continues after /lab/php-tutorial-for-fe-devs/