Symfony 2, Custom Form Widgets For Single inputs? - forms

UPDATE/EDIT
This is what I have done, but for some reason its not working. So the services is as follows,
clearfix.extended_type:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
Now I am sure this is wrong, I have not used services much and still trying to get my head around them.
I ave also built the class, see answer below for anyone else reading this, with the right namespace.
I have also done the following to the block,
{%- block form_row -%}
<div class="other classes here {% if clearfix %}clearfix"{% else %}"{% endif %}>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
But all I get from my form when I try to render it is, "Variable "clearfix" does not exist in....."
So what have I done wrong? I guessing it has something to do with the service setup?
Thanks
ok, this is doing my head in a little, has I have gone over and over the docs but I am either doing it completely wrong or its what I want to do is just not do able?
So I am using Symfony 2.7, and have it setup to point to a forms twig file, so that I can override some of the defaults. This is working fine without any problems. However I now want to be able to render some of the rows with an added clearfix class. This is what I am rending right now,
{%- block form_row -%}
<div>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}
That renders all my rows with the right classes, but I want to be able to use the following on some selected rows
{%- block _clerfix -%}
<div class="clearfix">
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock _clerfix -%}
And then I call this with the block_name within the Abstract Type of the form that I want to build with the added clearfix class, like so,
->add('password', 'password', ['label' => 'Password',
'required' => false,
'block_name' => 'clearfix',
'attr' => ['autocomplete' => 'off','class' => 'Passwordd', 'aria-required' => 'true' ]
])
Is what I want doable? I don't really want to add that clearfix into the user form I am building now, as that will not keep my code very DRY, as there will be (one would think so) places where I will need to re-use that block to render other fields?
All help most welcome.
Thanks.

You're can create form extension for defining option to apply clearfix
Symfony 2.8
class ClearfixExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['clearfix'] = $options['clearfix'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('clearfix', false);
}
public function getExtendedType()
{
return FormType::class;
}
}
services.yml
clearfix_extension:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
Symfony 2.7
class ClearfixExtension extends AbstractTypeExtension
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['clearfix'] = $options['clearfix'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('clearfix', false);
}
public function getExtendedType()
{
return 'form';
}
}
services.yml
clearfix_extension:
class: Bundle\Form\ClearfixExtension
tags:
- { name: form.type_extension, alias: form }
and check this options in twig
{%- block form_row -%}
<div {% if clearfix %}class="clearfix"{% endif %}>
{{- form_widget(form) -}}
{{- form_label(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row -%}

Related

Modify label_attr field in Symfony 5 form builder

When building a form in symfony form builder changing the choice attribute can be done.
However, for the label attribute this doesn't seem possible.
Here is how i modify the choice:
$builder->add('type', EntityType::class, [
'class' => Resourcetype::class,
'multiple' => true,
'expanded' => true,
'choice_attr' => function (?Resourcetype $type) {
return ['class' => $type->getSafeName() . '-parent parent' : $type->getSafeName()
];
});
Is this possible for the label_attr field?
EntityType doesn't provide option for modifying choice label attributes.
You should do it yourself.
1. Simple solution
Iterate over choices in templating engine, one by one and render it yourselves. Obtain entity from choice and set label attribute.
{{ form_start(form) }}
{%- for choice in form.choices %}
<div>
{% set entity = form.choices.vars.choices[choice.vars.value].data %}
{{ form_widget(choice) }}
{{ form_label(choice, null, {
label_attr: {class: 'test-' ~ entity.number}
}) }}
</div>
{% endfor -%}
{{ form_end(form) }}
2. Clean solution
Create custom type extending EntityType:
https://symfony.com/doc/current/form/create_custom_field_type.html
Create new option in type definition allowing closures e.g. "choice_label_attr" and pass closure to view:
// src/Form/Type/CustomEntityType.php
namespace App\Form\Type;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class CustomEntityType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setRequired('choice_label_attr');
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['choice_label_attr'] = $options['choice_label_attr']
}
public function getParent(): string
{
return EntityType::class;
}
}
Extend template for choice type:
https://symfony.com/doc/current/form/form_themes.html#applying-themes-to-all-forms
Use "choice_label_attr" callback inside extended template:
{% use "bootstrap_4_layout.html.twig" %}
{% block custom_entity_widget_expanded -%}
<div {{ block('widget_container_attributes') }}>
{%- for child in form %}
{{- form_widget(child) -}}
{{- form_label(child, null, {class: choice_label_attr(form.choices.vars.choices[child.vars.value].data), translation_domain: choice_translation_domain}) -}}
{% endfor -%}
</div>
{%- endblock custom_entity_widget_expanded %}
More info: https://github.com/symfony/symfony/blob/5.x/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig
Usage example:
use App\Form\Type\CustomEntityType ;
$builder->add('type', CustomEntityType::class, [
'class' => Resourcetype::class,
'multiple' => true,
'expanded' => true,
'choice_attr' => function (?Resourcetype $type) {
return [
'class' => sprintf('%s-parent parent', $type->getSafeName()) : $type->getSafeName()
];
});
Solution 2. is written from head and can contain some bugs but I hope you get the idea.
Both solution are using Twig and Bootstrap 4 form layout, but it is not requirement.

How to use existing widget form template blocks

I am creating custom Twig form template blocks to do some special rendering.
I have been unable, so far, to use some of the built-in form blocks. Specifically, and of the {{ *_widget() }} blocks.
Example:
Declare a new Twig function.
/* src/SiteBundle/Library/TwigExtension.php - Bundle-specific Twig extension */
...
public function getFunctions
{
$ret = [
new \Twig_SimpleFunction( 'wwui_myBlock', null, ['node_class'=>'Symfony\Bridge\Twig\Node\RenderBlockNode, 'is_safe'=>['html']] )
];
return $ret;
}
The extension is registered in the service.yml file and is obviously recognized since there are no errors about the wwui_myBlock function being undefined.
Define the Twig block.
{# src/SiteBundle/Resources/views/Form/fields.html.twig - Custom form blocks #}
{% block wwui_myBlock %}
<div class="formRow form-group-xs clearfix">
<div class="col-xs-1"></div>
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-xs-2' )|trim}) -%}
{{ form_label( form ) }}
<div class="col-xs-9">
{{ choice_widget( form ) }}
</div>
</div>
{% endblock wwui_myBlock %}
In the page template.
{# Page-specific template #}
...
{{ form_start( form ) }}
...
{{ wwui_myBlock( form.options ) }} {# options is a choice with radio buttons. #}
...
{{ form_end( form ) }}
When this page is rendered an exception is thrown:
The function "choice_widget" does not exist in SiteBundle:Form:fields.html.twig at line 62
My fields.html.twig file "uses" bootstrap_3_horizontal_layout.html.twig. That file "uses" bootstrap_3_layout.html.twig which, in turn, "uses" Twig's default form_div_layout.html.twig.
Why can't my custom block see choice_widget which is defined in form_div_layout.html.twig?
Turns out that the proper way to access such blocks is to use the block() function. so, instead of
{{ choice_widget( form ) }}
it's
{{ block( 'choice_widget', form ) }}

Create a form with a for in Symfony

I created a form in Symfony like this:
$form = $this->createFormBuilder($template)
->add('product1', 'text')
->add('product2', 'text')
->add('save', 'submit')
->getForm();
Now this is my twig:
{{ form_start(form) }}
{% for i in 1..2 %}
<div class="col-md-3">
<div class="product">
<div class="name">
{{ form_label(form.product{{ i }} ) }}
{{ form_errors(form.product{{ i }} ) }}
{{ form_widget(form.product{{ i }} ) }}
</div>
</div>
</div>
{% endfor %}
{{ form_end(form) }
The main idea is iterate over the for and get a new form.product<X> each loop.
I can't make it works and I don't even know if it can be done in this way. Any idea?
I would recommend you to use Collection type for this purpose. But if you want do it your way you should do it this way:
{{ form_start(form) }}
{% for i in 1..2 %}
<div class="col-md-3">
<div class="product">
<div class="name">
{{ form_label( attribute(form, 'product' ~ i) ) }}
{{ form_errors( attribute(form, 'product' ~ i) ) }}
{{ form_widget( attribute(form, 'product' ~ i) ) }}
</div>
</div>
</div>
{% endfor %}
{{ form_end(form) }
You're right, it probably won't work. For information, concatenation symbol in Twig is "~".
In your case, if your entity is supposed to have 2 or more "products" you should use collections instead of creating manually each product.
In your entity you would have something like
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
And on the product entity, you would have
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
And then in your first entity __constructor or in your controller you iterate to create as many product as you want and you add them to the entity.
In your form, you would just have to add :
$builder->add('products', 'collection');
and you would be able to iterate on it in Twig.
Hopefully this will help you
You should start with a Entity with only a ManyToOne relation to the "product" entity. Lets say that we call this entity "ProductContainer".
Then you create an form for the ProductContainer with only one field with the type 'collection' which will make a list of products for you.
You can follow this tutorial: http://symfony.com/doc/current/cookbook/form/form_collections.html

How to get entity or pass variable to Symfony2 twig form widget?

In my edit.html.twig I have:
{% form_theme edit_form 'MyBundle:Entity:form.html.twig' %}
{% set img_src = asset('120x100.jpg') %}
{{ dump(img_src) }}
{{ dump(entity) }}
{{ form_widget(edit_form, {'form_type': 'horizontal', 'img_src': img_src }) }}
There I have img_src and entity dumped with no problem.
In form.html.twig I have:
{% extends 'MyBundle:Form:bootstrap.html.twig' %}
{% block _entity_field_widget %}
{{ dump(img_src) }}
{{ dump(entity) }}
{% set type = 'hidden' %}
{{ block('form_widget_simple') }}
{% endblock _channel_media_widget %}
bootstrap.html.twig is just a bootstraped *form_div_layout.html.twig*
And in that widget I have no img_src nor entity.
Any ideas how to get entity in widget? Should it be passed to form widget or is there another way? What am I doing wrong?
Each symfony form type extents AbstractType class.
AbstactType class has method:
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->set('img_src', '120x100.jpg');
$view->set('my_variable', $foo);
}
You can create this method on your form type and next in your twig:
{{ asset(img_src) }}

Symfony2: Form fragment themeing and TWIG

Can anyone explain why this code:
{% form_theme form _self %}
{% block avo_gallery_upload_widget %}
{% spaceless %}
<label for="name">Name:</label>
{{ form_widget(form.name) }}
<label for="description">Description:</label>
{{ form_widget(form.description) }}
{% endspaceless %}
{% endblock avo_gallery_upload_widget %}
Throws
Method "name" for object "Symfony\Component\Form\FormView" does not exist in MyBundle:Default:upload.html.twig at line 13
For reference: line 13 is {{ form_widget(form.name) }}
But when wrapped in IF clause:
{% form_theme form _self %}
{% block avo_gallery_upload_widget %}
{% spaceless %}
{% if form.name is defined %}
<label for="name">Name:</label>
{{ form_widget(form.name) }}
<label for="description">Description:</label>
{{ form_widget(form.description) }}
{% endif %}
{% endspaceless %}
{% endblock avo_gallery_upload_widget %}
Suddenly everything works fine!
For reference - this is how form looks like:
class GalleryUploadType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name', 'text')
->add('description', 'textarea')
;
}
public function getName()
{
return 'avo_gallery_upload';
}
public function getDefaultOptions(array $options){
return array('data_class' => 'Me\MyBundle\Entity\GalleryUpload');
}
}
fabpot closed github issue (2012-07-03) with comment:
Every month, I spend hours trying to reproduce the problem without luck. So, I'm giving up for now as there is probably something else going on in your application (as it works fine for almost everyone). If you have any new information that can be relevant, feel free to reopen a new ticket. Thanks.
Since it probably is something wrong in my application and there is nothing new I could add to the question I am closeing this question.
If you happen to encounter this error have a look at this workaround.
If you have any additional info on reproducing the problem, post it here.