Symfony2 form type customization - forms

I need help with this since I just can't figure it out by myself even after reading and examining all resources I found on the internet.
I have an Image entity. It has 3 mapped properties.
id
location
thumb_location
And I have custom ImageSelectType form type which extends AbstractType:
buildForm function looks like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$em = $options['em'];
$qb = $em->createQueryBuilder();
$result = $qb->select('i')
->from('BloggerBlogBundle:Image', 'i')
->leftJoin('i.articles', 'a')
->where('a is NULL')
;
$builder
->add('images', 'entity', array(
'class' => 'BloggerBlogBundle:Image',
'query_builder' => $result,
'required' => true,
'multiple' => false,
'expanded' => true,
)
)
->setAttribute('widget', 'imageSelect')
;
}
So it takes all images that aren't used by any article and populates form with them. With these options, I get radiobuttons, like this:
<div id="image_images">
<input type="radio" id="image_images_67" name="image[images]" required="required" value="67"><label for="image_images_67" class="required">c5252b4ffc9c50540218e25be1353b33aaa4ee05.png</label>
<input type="radio" id="image_images_68" name="image[images]" required="required" value="68"><label for="image_images_68" class="required">fcfc7d7d05d63b1f55dff8cbff0bedeb3c917dfc.jpeg</label>
</div>
What I want now is all radiobuttons to have custom html5 data attribute data-thumb="thumbnail/location.png" which would be the thumb_location property value of image object represented by that radiobutton.
I hope I was clear enough. If any more info is needed I will provide it.
I've read so much about this but I think I'm imagining things more complex then they actually are. At one point I just wanted to say 'Oh, forget it, I'll just render this manually' and use:
{% for choice in form.vars.choices %}
<input type="radio" data-thumb="{{choice.thumb_location}}" />
{% endfor %}
But I really want to use good practices that this amazing framework provides....Sometimes just don't seem as obvious to me as they should.

Ah, I thought this was relating to this object as the FormType is. So in this case I see the possibility to set a variable withe the buildView method.
/**
* {#inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['thumb'] = $options['thumb'];
}
You can set this option whether in the ->add(x, x, array('thumb_location' => 'location'))
(or in the setDefaultOptions method).
But now I am not sure to be honest. Because you would need to set in the view like this:
<input data-thumb="{{ thumb }}" type="radio" id="blog_article_image_17" name="blog_article[image]" required="required" value="17">
And this means you make <input data... by hand again.
Not sure if this is possible in your case but I think it would be nice to define a second FormType for the radio input field:
public function buildForm(FormBuilderInterface $builder, array $options)
{
...this is your current form builder....
->add('radio', MyRadioType());
}

Not sure if I understand the question, you want to set the data-thumb directly within the form builder, right?
So the form builder contains mains features (http://api.symfony.com/2.0/Symfony/Component/Form/FormBuilder.html) including the setAttribute(string $name, string $value) method.
You could do something like:
setAttribute('data-thumb', $yourvalue);
To apply these attributes in your input field, you need to set attributes in your twig template:
<input type="radio" {{ block('widget_attributes') }} />

Related

Form collection of imbricated form without entity never validates

I am currently working on a "Filters" form to add the possibility for the users to apply filters on item lists. The issue I am facing is that once the form is submitted, the controller considers that the form is empty and invalid.
Dumping what's returned by $form->getData() shows the following:
array(1) { ["filters"]=> array(0) { } }
There is neither errors nor warnings in the logs. The GUI returns an error on the field of the filter:
This value is not valid.
However if I modify the Twig widget to change the select's id to anything else, I no longer get the invalid value but the form's data still is an empty array.
Here's the layout of this project:
a FormType containing one select input, and one text input,
another FormType that implements the former in a collection,
The controller, which instantiates and use the form,
a Twig view of the 2nd FormType,
and the final Twig page
FilterSearchType.php
namespace NetDev\CoreBundle\Form\Helpers;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use NetDev\CoreBundle\Form\Helpers\FilterType;
class FilterSearchType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('filters', 'collection', array('type' => new FilterType($options['entity']), 'allow_add' => true,
'allow_delete' => true, 'by_reference' => false))
->add('search', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('filters' => [],
'entity' => null));
}
public function getName() {
return 'search_filter';
}
}
FilterType.php
<?php
namespace NetDev\CoreBundle\Form\Helpers;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
class FilterType extends AbstractType {
public function __construct($entity) {
$this->model = $entity;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
/*getFilters returns something like that:
* ['Column A' => 'column_a', 'Column B' => 'column_b', ...]
*/
->add('column_name', 'choice', array('choices' => $this->model->getFilters(true)))
->add('search_value', 'text')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(['column_name' => '',
'search_value' => '']);
}
public function getName() {
return 'netdev_filter';
}
}
Here is how the form is given to Twig from the controller:
RoutesController.php
class RoutesController extends Controller {
public function indexAction($page = 1, Request $request) {
$em = $this->getDoctrine()->getManager();
$orderBy = $request->query->get('orderBy');
$orderType = $request->query->get('orderType');
$form = $this->createForm(new RouteManagerType($em), null, array('orderBy' => $orderBy,
'orderType' => $orderType));
$filterForm = $this->createForm(new FilterSearchType(), null, array('entity' => new Route()));
if ($request->isMethod('POST') && $filterForm->handleRequest($request)->isValid()) {
// Never reached, $filterForm is always marked as invalid
$formData = $filterForm->getData();
var_dump($formData);
exit();
if (!empty($formData['filters']) && count($formData['filters'])) {
if (empty($formData['action'])) $formData['action'] = 'filter';
$form = $this->createForm(new RouteManagerType($em), null,
array('orderBy' => $orderBy,
'orderType' => $orderType,
'filters' => $formData['filters']));
}
}
Twig widget: filter_search.html.twig
{% block netdev_filter_widget %}
{% spaceless %}
{{ form_errors(form) }}
<div class="form-group">
<div class="input-group">
<select {{ block('widget_attributes') }} class="form-control" id="search_column">
{% for group_label, choice in form.column_name.vars.choices %}
<option value="{{ choice.value }}" {{ block('attributes') }}>{{ choice.label }}</option>
{% endfor %}
</select>
<div class="input-group-addon">contains</div>
<input type="text" class="form-control" id="search_criteria" placeholder="search"/>
</div>
</div>
{% endspaceless %}
{% endblock %}
I've dumped pretty much everything I could and nothing was really interesting. I am not even sure that the Kernel understands / correctly "links" the submit the user has performed and the form the controller created.
Any help on that would be greatly appreciated.
OK, so the issue is that if you do the rendering yourself, you should very well be aware of the fact that the names as rendered in HTML are exactly what the backend expects, otherwise you'll get issues like these.
The best way to tackle that is take the default form rendering as a starting point and don't do any custom HTML until you're absolutely sure you need custom templating. When you do, inspect the default templates to see how the names of the elements are built, and follow the exact same naming, or even better, reuse base templates wherever possible (either by extending blocks and calling parent() and/or using the block function).

Change symfony2 form field but keep relation

So I have a form that is based on an entity that contains a one to many relationship.
The problem is that this field is rendered as a select (or choice). I really don't want to load all the possible ids (there are many) but just want to load the one that is set in the entity (which is the id that appears selected in the select).
Is there any way of doing this and still keep the relationship? If I really have to change the field how can I access, in the form class, the selected entity given to the entity so that I can retrieve the id?
UPDATE
To make this a bit clearer here is my form code:
$this->createFormBuilder()
->add('items', 'collection', array(
'type' => new \MyBundle\Form\ItemsType(),
'allow_add' => true,
'data' => $itemsEntities
)
)
->add('submit', 'submit')
In the $itemsEntities I have 5 entities all of which generate the select with loads of ids. Hakins answer would work I think if this would be just one field but since there are many I don't really know how to handle this.
I have tried to put an eventListner on the \MyBundle\Form\ItemsType for but I can never access any data.
Maybe you could use the 'query_builder' option of the field (see: http://symfony.com/doc/current/reference/forms/types/entity.html) and create a query that fetches the only result you want, based on it's id. You could pass the id to the constructor of form if necessary.
You can pass the id of the related entity to the form builder parameters, and change your field type to hidden instead of choice (or entity):
In your controller:
$id = $entity->getRelatedEntity()->getId();
$options['id'] = $id;
$form = $this->createForm(new EntityType($options), $entity);
In your EntityType:
public function buildForm(FormBuilderInterface $builder, array $options) {
$options = $this->options;
$builder
->add('relatedEntity', 'hidden', array(
'data' => $options['id'],
'required' => TRUE
));
Update
To avoid rendering a collection without changing the relationship, you can change only your Twig form by rendering the selected item(s) id(s) as hidden field(s), then set rendered the form.items. (If you don't set them rendered, they will be present in the form_rest(form))
With your existing code for the formBuilder, change your twig like this:
{% block body %}
...
{% for item in form.items %}
{% if item.vars.data %}
<input type="hidden" name="{{ item.vars.full_name}}" id="{{ item.vars.id }}" value="{{ item.vars.value }}"
{% endif %}
{% endfor %}
{% do form.items.setRendered %}
...
{% endblock %}

Symfony embedded formType in another formType for adding datas at the same time in two entities that have a ManyToOne relation

I have a relation ManyToOne between two entities Articles.php and Corrections.php.
Look at this code, Corrections.php where my FK constraint is:
class Corrections
{
/**
* #var \Articles
*
* #ORM\ManyToOne(targetEntity="Articles")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="article_id", referencedColumnName="id")
* })
*/
private $article;
An article could have many correction, but a correction belong to only one article.
So, in my Corrections.php, I have the foreign key named $article refers to article_id in MySQL database.
Now I need to add a correction to an article in a form Type, but not only that.
This is the controller code for:
public function addCorrectionAction() {
$em=$this->getDoctrine()->getManager();
$correction= new Corrections;
$form = $this->createForm(new CorrectionsType(), $correction);
$request = $this->getRequest();
if ($request->isMethod('POST') | ($form->isValid()) ) {
$form->bind($request);
$correction= $form->getData();
$em->persist($correction);
$em->flush();
return $this->redirect($this->generateUrl('indexArticles'));
}
else {
return $this->render('Bundle:Folder:addCorrections.html.twig', array('form' => $form->createView() ));
}
}
No problems here for now. This my buildForm, CorrectionsType.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('date')
->add('description')
->add('text')
->add('articles', 'collection', array(
'type' => new ArticlesType(),
'allow_add' => true))
;
}
And the twig view:
<form action="{{ path('addCorrection_process') }}" method="POST">
{{ form_widget(form) }}
<div class="row col-md-3">
<input type="submit" value="Add correction and article" class="btn"/>
</div>
</form>
In fact, I need to create a form which allows a user to add an article and his correction at the same time. Or here, when I display the addCorrection method with the form, the field articles is a select which allow user to add a correction to an existing article.
How can I proceed to have a form which allow user correctly ADD a NEW correction and a NEW article at the time in the same form? I need this form must be consistent with my FK constraint too, i-e when I ADD a NEW correction with a NEW article at the same time, the FK $article (article_id) have the correct value refers to the correction_id.
Check Collection Field Type official documentation page. So basically you create ArticleType and CorrectionType forms and connect the latter by collection field type.

Form Type Default Value for empty value in rendered form

I found some weird behavior with a rendered controller with displays a edit form for my entity.
But first things first:
I'm rendering a template with displays a entity. If the logged in user is the same user as the owner of that entity i also render another controller hidden with contains the edit form for this entity. The User can access this via a button which fires a jQuery toggle.
The entity has 2 textfields which can be empty, description and situation.
So if one of the two or both are empty the edit form will display in the textfield (null) by default. I do not want that! How can i fix this so that the textfields are empty like the value of the field (so that my placeholder will be shown).
Here's an image to visualize this:
But further: This entity (Poi) belongs to another Entity (Turn), so 1 Turn -> many Pois. You can navigate through the pois in my website.
But if the owner navigtes through them (keep in mind, the edit form will be rendered, but not displayed until the button was klicked) all description and situation fields now display (null), even he did not saved the edit. It just happen by itself.
Here an image which shows it
Why does this happen? What can i do against it? Is there maybe something like an empty value option in the form type?
I searched for a solution, but i couldn't find anything that is nearly simliar with my situation.
The form build from my Form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array(
'required' => false
))
->add('situation', 'textarea', array(
'required' => false
))
->add('description', 'textarea', array(
'required' => false
))
->add('isPrivateText', 'checkbox', array(
'required' => false
))
->add('isPrivateImage', 'checkbox', array(
'required' => false
))
;
}
The relevant part of my edit.html.twig
<p class="edit_form"><span class="edit_left">{{ form_label(edit_form.situation, 'Situation') }} </span>
<span class="edit_right">{{ form_widget(edit_form.situation, { attr: {'placeholder': 'Törn Situation'} }) }}</span></p>
<p class="edit_form"><span class="edit_left">{{ form_label(edit_form.description, 'Beschreibung') }} </span>
<span class="edit_right">{{ form_widget(edit_form.description, { attr: {'placeholder': 'Törn Beschreibung'} }) }}</span></p>
Where my showPoi.html.twig renderes the form controller:
<div class="col-md-6 col-sm-6 toggle_edit" style="display: none;">
<div>
{% render controller('MysailinglogMysailinglogBundle:Poi:edit', { id: poi[0].id , poi: poi}) %}
<!-- Don't worry about the 2 divs, i just shortened up the code -->
</div>
</div>
After lots of more research i found a solution that is working fine
I'm adding a listener to my formType which leads to the following function:
function onPreSetData(FormEvent $event) {
$data = $event->getData();
if($data->getDescription() == "(null)"){
$data->setDescription('');
}
if($data->getSituation() == "(null)"){
$data->setSituation('');
}
return $event->setData($data);
}
It just takes the data from the event which will build the form and is nothing more then the Poi Entity. There i simply check if the value is (null) and if it is i set it to a empty string.
Registering the listener is also easy, it`s done with this simple line of code:
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
This must be done with a instance of the FormBuilder, the "onPreSetData" must be the same name as the function above which will be triggered by the event.
It's important to mention that the Event must be the PRE_SET_DATA event in this situation because i wanted to manipulate the data before they're written into the form!
You can set up an empty data attribute in the Form type:
Symfony documentation
$builder->add('description', 'textarea', array(
'required' => false,
'empty_value' => 'Choose your gender',
'empty_data' => null
));

How can I show a form with the old values so that you can edit?

in controller I find the id
$oggetto = $this->getDoctrine()
->getRepository('AcmeTryBundle:Try')
->find($id);
after I passed this $values into form(just?)
$form = $this->createForm(new TryType(), $oggetto);
and in FormType? what I put?
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name','text') ?
Your approach is good.
1) Get your $oggetto object in DB
2) Pass it to your FormType $form = $this->createForm(new TryType(), $oggetto);
3) Add the fields you want in your form type
4) Send your form to your view 'form' => $form->createView()
5) In your view, call your form
<form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" />
</form>
Your fields (you defined in 3) ) will be automaticaly populated by your object data. You can then change them and edit them.
See the doc for more info: http://symfony.com/doc/current/book/forms.html
Your Form seems fine
class TryFormType extends AbstractType {
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name','text') ;
}
public function getName() {
return 'tryform';
}
}
The function getName gives the form a name, which in this case is tryform.
In the controller you can add a return statement like this.
return $this->render('AcmeTryBundle:Default:TryForm.html.twig', array(
'TryForm' => $form->createView()
));
And in twig file access it as follows.
{{ form_widget(TryForm.name) }}
The value will automatically be passed there. You can then edit it
For example in Symfony 4.2.3, you filled and submitted a form and some form values are invalid.
<input type="text" name="name" value="{{ form.vars.value.name }}">
In this way, you will be set old form input as a default value if the value is valid.