I want to override how parts of my form is rendered, but I always forget to which twig block names symfony forms renders. If the block is not found, I get no feedback why not was not found, and how it should have been called.
Example:
class SetPasswordType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('plainPassword', PasswordType::class);
$builder->add('repeatPlainPassword', PasswordType::class);
}
}
Three essential questions:
SetPasswordType
?For this specific type, the twig widget
block name is set_password_widget
.
Override it in your form theme like this:
{% block set_password_widget %}
<div class="row">
<div class="col">
{{ form_row(form.plainPassword) }}
</div>
<div class="col">
{{ form_row(form.repeatPlainPassword) }}
</div>
</div>
{% endblock %}
SetPasswordType
, it is set_password
row
block. If you want to change the input, likely you will override the widget
block. Refer to the documentation: https://symfony.com/doc/current/form/form_themes.html#form-fragment-namingwidget
block of the SetPasswordType
, you want to override set_password_widget
.twig.form_themes
or otherwise as a form theme included.$blockPrefixes
in Symfony\Component\Form\Extension\Core\Type\BaseType::buildView
to see which block prefixes were determined. The last entry in the array will be looked for first.$blockNameHierarchy
in Symfony\Component\Form\FormRenderer::searchAndRenderBlock
to see which specific blocks are looked for. The last entry in the array will be looked for first.The names of the blocks are build up in Symfony\Component\Form\Extension\Core\Type\BaseType::buildView
, within the $blockPrefixes
variable. $blockPrefixes
defines the following priority (first mentioned = highest priority. It is stored in reverse!):
AbstractType
, the name is generated in AbstractType::getBlockPrefix()
)FormTypeInterface::getParent()
method).For example, it could look like this:
$blockPrefixes = [
0 => "form", # because `AbstractType::getParent()` returns `FormType`
1 => "set_password", # our type, name resolved automatically depending on class name
## at this position, a block prefix would be included if one is configured
2 => "_set_password" # the hierarchical unique name, prefixed with `_`. for the plainPassword field, this value would be "searchAndRenderBlock".
];
When the form is rendered by twig, the Symfony\Component\Form\FormRenderer::searchAndRenderBlock
is called with a respective $blockNameSuffix
(for example widget
or row
). The $blockNameHierarchy
is created by suffixing each potential prefix with the $blockNameSuffix
.
For $blockNameSuffix == "row"
it would then look like this:
$blockNameHierarchy = [
0 => "form_widget",
1 => "set_password_widget",
2 => "_set_password_widget"];
These blocks are looked for, and the one with the highest priority which can be found is chosen. The later in the array, the higher the priority.
You can customize the prefixes like this:
class SetPasswordType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('plainPassword', PasswordType::class, ['mapped' => false]);
$builder->add('repeatPlainPassword', PasswordType::class, ['mapped' => false]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('block_prefix', 'my_custom_prefix');
$resolver->setDefault('block_name', 'my_custom_name');
}
}
This will result in
$blockPrefixes = [
0 => "form",
1 => "set_password", # this will stay the same as its resolved depending on type name
2 => "my_custom_prefix", # as per our configuration, we now have a custom prefix
3 => "_my_custom_name" # the custom name will be used to determine the hierarchical name
];