If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeThe 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.
maraloon We'll be releasing a whole new suite of Symfony 4 videos soon :). The service name change for Symfony 3.3 was a bit tough - we added a bunch of notes in our older tutorials to make people aware. But mostly, this will be improved with fresh tutorials!
Hi, I get this error at the end:
Type error: Argument 1 passed to Symfony\Component\OptionsResolver\OptionsResolver::setDefaults() must be of the type array, string given, called in C:\Users\John\Documents\Siti\aqua_note\src\AppBundle\Form\TypeExtension\HelpFormExtension.php on line 22
I think It shouldn't be an array because it's a value and its option, what am I doing wrong? Below my code:
namespace AppBundle\Form\TypeExtension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class HelpFormExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
if ($options['help']) {
$view->vars['help'] = $options['help'];
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults('help', null);
}
public function getExtendedType()
{
return FormType::class;
}
}
Thanks in advance
Hey Josh,
Ah, those methods do the same but in a different way, very easy to miss "s" at the end :)
I'm glad you found the problem so quick.
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"symfony/symfony": "3.4.*", // v3.4.49
"doctrine/orm": "^2.5", // 2.7.5
"doctrine/doctrine-bundle": "^1.6", // 1.12.13
"doctrine/doctrine-cache-bundle": "^1.2", // 1.4.0
"symfony/swiftmailer-bundle": "^2.3", // v2.6.7
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.23.0
"sensio/distribution-bundle": "^5.0", // v5.0.25
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
"incenteev/composer-parameter-handler": "^2.0", // v2.1.4
"composer/package-versions-deprecated": "^1.11", // 1.11.99.4
"knplabs/knp-markdown-bundle": "^1.4", // 1.9.0
"doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
"stof/doctrine-extensions-bundle": "^1.2" // v1.3.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.7
"symfony/phpunit-bridge": "^3.0", // v3.4.47
"nelmio/alice": "^2.1", // v2.3.6
"doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
}
}
https://knpuniversity.com/s...
I think it will be good to remind about Symfony3.3-like adding service:
AppBundle\Form\TypeExtension\HelpFormExtension:
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }