watermark
A coffee on a wooden table next to a laptop

Can I get a form builder? Yes you can!

I had a quick look at the different ways in which I could get my hands on the form builder in a Symfony controller.

Disclaimer: 1) other services are available and 2) this is not an exhaustive list on how to inject and/or access services in a controller.

So, as a starting point, let's assume we've installed Symfony 5.x (either with the Symfony CLI binary or with composer create project) and we have a lovely fresh installation. Let's open up a few files and see what we have... Here's what we'd expect to see in /config/services.yaml

// config/services.yaml
services:
    _defaults:
        autowire: true

// and a bit further down...
    App\Controller\:
        resource: '../src/Controller/'
        tags: ['controller.service_arguments']

Now, if we uncomment the lines in /config/routes.yaml so we have...

// config/routes.yaml
index:
    path: /
    controller: App\Controller\DefaultController::index

and add a controller...

// src/Controller/DefaultController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends AbstractController
{
    public function index()
    {
        $formBuilder = $this->createFormBuilder();

        return new Response('Yo!');
    }
}

Bingo! We can start using the form builder.

Now if you're inheritance-adverse, you might not fancy extending the AbstractController class. Fear not! All we need to do is constructor-inject our dependency using type-hinting as follows:

// src/Controller/DefaultController.php
<?php

namespace App\Controller;

use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    protected $formFactory;

    public function __construct(FormFactoryInterface $formFactory)
    {
        $this->formFactory = $formFactory;
    }

    public function index()
    {
        $formBuilder = $this->formFactory->createBuilder();

        return new Response('Yo!');
    }
}

And this totally works because of the lovely autowiring provided by Symfony. Thanks, Symfony!

Lastly, let's take a look at how you'd get access to the form builder if you weren't, for whatever reason, using autowiring.

Let's disable autowiring in /config/services.yaml, tag our controller class with controller.service_arguments and bind the form factory service...

// config/services.yaml
services:
    _defaults:
        autowire: false

    App\Controller\DefaultController:
        tags: ['controller.service_arguments']
        bind:
            $formFactory: '@form.factory'

And finally, modify our controller...

// src/Controller/DefaultController.php
<?php

namespace App\Controller;

use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Response;

class DefaultController
{
    public function index(FormFactoryInterface $formFactory)
    {
        $formBuilder = $formFactory->createBuilder();

        return new Response('Yo!');
    }
}

And there we have it, I hope you enjoy playing around with these approaches and I also hope you enjoy your coffee... or preferred beverage of choice!

A photo of Code Enigma developer, Chris.

Written by

Chris Maiden

Web Developer