If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
The only way to make an option valid for a field is to, well, hack the core class
and add it! For example, since funFact
is a TextType
, I could - if I were feeling
crazy - open TextType
, scroll down, and hack that help
option into configureOptions()
:
... lines 1 - 16 | |
use Symfony\Component\OptionsResolver\OptionsResolver; | |
class TextType extends AbstractType implements DataTransformerInterface | |
{ | |
... lines 21 - 35 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
$resolver->setDefaults(array( | |
'compound' => false, | |
)); | |
} | |
... lines 42 - 67 | |
} |
With a few other hacks, we could have this feature working!
Obviously, this is not the right path to take, and that's ok, but there is another way to add an option to a field. It's called a form type extension, and it's basic a plugin to the Symfony form field system. By leveraging a form type extension, you can modify any field in the system. You could, I don't know, add a new attribute to literally every single field on your entire site.
Let's find out how.
In your Form
directory, create a new directory called TypeExtension
and then
a new class called HelpFormExtension
:
... lines 1 - 2 | |
namespace AppBundle\Form\TypeExtension; | |
... lines 4 - 6 | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
... lines 9 - 12 | |
} |
The goal of this class will be to to allow for a help
option to be passed to any
field, and to turn that help
option into a help
variable.
First, all form type extensions should extend AbstractTypeExtension
:
... lines 1 - 2 | |
namespace AppBundle\Form\TypeExtension; | |
use Symfony\Component\Form\AbstractTypeExtension; | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
... lines 9 - 12 | |
} |
Next, use the "Code"->"Generate" menu, or Command
+N
on a Mac, and click "Implement Methods".
This abstract class requires us to have one method: getExtendedType()
:
... lines 1 - 2 | |
namespace AppBundle\Form\TypeExtension; | |
use Symfony\Component\Form\AbstractTypeExtension; | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
public function getExtendedType() | |
{ | |
// TODO: Implement getExtendedType() method. | |
} | |
} |
You see, when you create a form type extension, you could make it modify every
field in your entire system, or just one type, like the FileType
. To modify
every field, return FormType::class
:
... lines 1 - 5 | |
use Symfony\Component\Form\Extension\Core\Type\FormType; | |
... lines 7 - 9 | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
... lines 12 - 16 | |
public function getExtendedType() | |
{ | |
return FormType::class; | |
} | |
} |
Because remember, FormType
is the parent for all fields.
Tip
Technically, FormType
is the parent type for all fields, except for buttons.
But I don't like adding buttons to my form anyways!
Here's the cool thing about these classes: they have all the same functions as a
normal form class, like buildForm()
or configureOptions()
. The difference is
that whatever modifications we make to this class will literally be applied to every
field in the system.
For example, go back to the "Code"->"Generate" menu, click "Override Methods", then
select buildView()
:
... lines 1 - 6 | |
use Symfony\Component\Form\FormInterface; | |
use Symfony\Component\Form\FormView; | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
public function buildView(FormView $view, FormInterface $form, array $options) | |
{ | |
... line 14 | |
} | |
... lines 16 - 20 | |
} |
When we're done setting things up, whenever any field is transformed into a FormView
object, this method will be called and we will be able to add variables to anything!
Try it: add $view
- which represents whatever one field is being setup -
$view->vars['help']
set to TURTLES
!
... lines 1 - 9 | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
public function buildView(FormView $view, FormInterface $form, array $options) | |
{ | |
$view->vars['help'] = 'TURTLES!'; | |
} | |
... lines 16 - 20 | |
} |
That's a ridiculous, and yet, fully-functional type extension.
To tell Symfony about this, you guys can probably guess, we need to register this
as a service. In app/config/services.yml
, add a new service: call it
app.form.help_form_extension
. Set its class to HelpFormExtension
and then I'll
set autowire
to true... even though the class doesn't have any constructor arguments,
at least not yet:
... lines 1 - 5 | |
services: | |
... lines 7 - 27 | |
app.form.help_form_extenion: | |
class: AppBundle\Form\TypeExtension\HelpFormExtension | |
autowire: true | |
... lines 31 - 33 |
Then, to actually tell Symfony: "Hey! This is a form type extension!", add a tag,
set to form.type_extension
. Also give this an extended_type
option. This needs
to match whatever you're returning from getExtendedType()
. FormType::class
returns the long string in the use
statement, so copy that, and paste it into your
service:
... lines 1 - 5 | |
services: | |
... lines 7 - 27 | |
app.form.help_form_extenion: | |
class: AppBundle\Form\TypeExtension\HelpFormExtension | |
autowire: true | |
tags: | |
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType } |
That's it team! Temporarily remove the help
option from GenusFormType
, ya know,
so the page doesn't explode. Then, refresh! OMG, everyone is screaming about
TURTLES! Well, everyone except for the isPublished
field, because we're overriding
that help
variable at the last possible second: from inside the template.
Finally, uncomment the help
option. So, how can we make this a valid option? Go
back to HelpFormExtension
, use the "Code"->"Generate" menu one last time, click
"Override Methods", and select configureOptions()
:
... lines 1 - 8 | |
use Symfony\Component\OptionsResolver\OptionsResolver; | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
... lines 13 - 19 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
... line 22 | |
} | |
... lines 24 - 28 | |
} |
Our job here is so simple: $resolver->setDefault('help', null)
:
... lines 1 - 10 | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
... lines 13 - 19 | |
public function configureOptions(OptionsResolver $resolver) | |
{ | |
$resolver->setDefault('help', null); | |
} | |
... lines 24 - 28 | |
} |
Just by doing that, you are now allowed to have a help
option on any field. It
also means that when buildView()
is called, the $options
array will have a key
called help
. All we need to say is if $options['help']
, then set the help
variable
to $options['help']
:
... lines 1 - 10 | |
class HelpFormExtension extends AbstractTypeExtension | |
{ | |
public function buildView(FormView $view, FormInterface $form, array $options) | |
{ | |
if ($options['help']) { | |
$view->vars['help'] = $options['help']; | |
} | |
} | |
... lines 19 - 28 | |
} |
And that takes care of it. Try this puppy out.
And consider yourself very, very dangerous.
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1", // 1.1.1
"stof/doctrine-extensions-bundle": "^1.2" // v1.2.2
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}