Create a form with a for in Symfony - forms

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

Related

easyadmin action buttons in custom template

I'm creating my own template for the edit page the thing is the actions buttons don't show so I tried to add them in configureActions function:
public function configureActions(Actions $actions): Actions
{
return parent::configureActions($actions)->add(Crud::PAGE_EDIT, Action::SAVE_AND_CONTINUE);
}
but i get this error
The "saveAndContinue" action already exists in the "edit" page, so you can't add it again. Instead, you can use the "updateAction()" method to update any options of an existing action+
This is my edit twig:
{% extends '#!EasyAdmin/layout.html.twig' %}<div class="tab-pane fade show active" id="client">
{% block edit_form %}
<div class="form-group">
{{ form_label(edit_form.name) }}
{{ form_widget(edit_form.name, {'attr': {'class': 'form-control' }}) }}
</div>
<div class="form-group">
{{ form_label(edit_form.surname) }}
{{ form_widget(edit_form.surname, {'attr': {'class': 'form-control' }}) }}
</div>{% endblock %}

Symfony 6 - Access CollectionType fields in Twig Template

I have a Symfony 6 project, where I us a CollectionType in a Form.
This CollectionType is called "variants" and has three fields. Lets call them:
Field_1
Field_2
Field_3
If I render this CollectionType in my Twig template with {{form_row(form.variants)}} all three fields of the CollectionType are rendered underneith.
However, I would like to to separate each of the fields of the CollectionType in a own column next to each other. But how dow I access the fields?
I have tried to access the fields via {{form_row(form.variants.Field_1)}} but obviously that did not work.
I am happy about any ideas :)
you were close.
CollectionType is an array - as it can hold multiple sets of the type.
If you only have one set try
{{ form_row(form.variants[0].Field_1) }}
{{ form_row(form.variants[0].Field_2) }}
{{ form_row(form.variants[0].Field_3) }}
The for method:
{% for variant in form.variants %}
{{ form_row(variant.Field_1) }}
{{ form_row(variant.Field_2) }}
{{ form_row(variant.Field_3) }}
{% endfor %}
Thanks to Rufinus again.
I used that information and the remark from vinceAmstoutz and did the creation in a foor loop:
{% for variant in form.variants %}
<div class="row">
<div class="col">
{{ form_row(variant.Field_1) }}
</div>
<div class="col">
{{ form_row(variant.Field_2) }}
</div>
<div class="col">
{{ form_row(variant.Field_3) }}
</div>
</div>
{% endfor %}

Why does Symfony 3.4 form displays EntityType choices with label although 'label' => false was used?

I am working on an existing Symfony 3.4 based project and trying to add and render a new form. Although the 'label' => false option was used, the fields are rendered including a label. Why?
// Symfony
class SomeController extends Controller {
public function userListAction(Request $request) {
$users = $someService->getUsers();
$formBuilder = $this->createFormBuilder()
->add('users', EntityType::class, [
'label' => false, // also tested '' and 'someLabel'
'class' => 'AppBundle:User',
'choices' => $users,
'multiple' => true,
'expanded' => true,
]);
$variables = array(
'form' => $formBuilder->getForm()->createView(),
);
return $this->render('AppBundle:Pages:user_list.html.twig', $variables);
}
}
// Twig
{% extends 'AppBundle::layout.html.twig' %}
{% block page_content %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endblock %}
This shows a list of checkboxes for all users including the username as label.
Where does Symfony get the information to use the username as label? As far as I know no custom form widget was defined for the User class. Is there any way to check this for sure? Maybe there is something hidden in the vendor bundles like FOSUserBundle?
Why is the 'label' => false option ignored?
Edit:
Different ways of rendering the form does not solve the problem:
{{ form_start(form) }}
{{ form_row(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="form-group">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
</div>
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
{{ form_start(form) }}
{% for userFormView in form.users %}
{{ form_row(userFormView) }}
{% endfor %}
{{ form_end(form) }}
Result:
Basically the same as before with form_row
You need to use ‘choice_label’ => ‘YOUR PROPERTY PATH’ in the field options.
Pretty match is written in the docs: https://symfony.com/doc/current/reference/forms/types/entity.html#choice-label
If the entity class cast to string then is used if is not it will throw an exception. It looks like your entity User cast to the user name and that’s why it works.
You should try to use {{ form_row(form) }} which should render the whole field correctly.

Symfony2 - Collection type is displayed twice in my view

I have a form with a collection and I can see it properly when I use the following syntax:
form_row(form.items)
Then when I try to render it by myself, it is duplicated.
Here is my code in my twig file:
{{ form_errors(form) }}
{{ form_start(form) }}
... some fields here ...
<div id="{{ form.items.vars.id }}" class="collection items" data-prototype="{{ form_widget(form.items.vars.prototype)|e }}">
<h3>{{ form_label(form.items) }}</h3>
Add
{% for item in form.items %}
{{ form_row(item) }}
{% endfor %}
</div>
... some other fields here ...
{{ form_end(form) }}
Here is my code in my form object:
$builder->add('items', 'collection', array(
'type' => new ItemType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => true,
));
Add here is my output:
<!-- This is the one I need, I removed the prototype for the sake of clarity -->
<div id="bundle_data_items" class="collection items" data-prototype="...">
<h3>
<label class="required">Items</label>
</h3>
Add
</div>
<!-- This one is automatically added. The prototype is empty -->
<div>
<label class="required">Items</label>
<div id="bundle_data_items" data-prototype=""></div>
</div>
Do you have any ideas why this happens?
I followed that documentation. Did I miss something?
Edit:
This happens only when my collection is empty
I found the solution to that.
It turns out that when the collection is empty, the rendering of it never occurs. I could use sjagr solution but as there is some fields I want to render automatically, I cannot use it.
So my solution is the following:
{{ form_errors(form) }}
{{ form_start(form) }}
... some fields here ...
<div id="{{ form.items.vars.id }}" class="collection items" data-prototype="{{ form_widget(form.items.vars.prototype)|e }}">
<h3>{{ form_label(form.items) }}</h3>
Add
{% for item in form.items %}
{{ form_row(item) }}
{% else %}
{% do form.items.setRendered %}
{% endfor %}
</div>
... some other fields here ...
{{ form_end(form) }}
When your collection is empty, the for loop in {% for item in form.items %} never gets to execute, hence form_row(item) never happens. This means, to Twig/Symfony, you never actually outputted the field.
Then you do form_end at the end of the form. This is not usually a big deal, but from the docs:
This helper also outputs form_rest() unless you set render_rest to false
So, you must simply pass a false value for render_rest:
{{ form_end(form, {'render_rest': false}) }}
I'm not sure how form_widget did fix your problem, but I'm also not sure how you tried that alternative - perhaps you ran it outside of the for loop.
As an aside, you should consider making your own form.html.twig template file so you can reuse the markup format you've chosen for your collection. It will allow you to simply do form_widget(...) without having to break apart the pieces and your form_end will always consider the field to be outputted even if the collection is empty.
Unfortunately i cannot comment yet (no 50 rep), but perhaps this is caused the browser itself. A rendering issue due to markup someplace? See if the raw http response has the same thing. Since it works with form_row(form.items) but not your own, it "could be" that.
I would check if it's empty also before outputting it.
Similar thing happened to me, label of CollectionType was rendering twice when Collection is empty, but it was rendering once when there are data.
This is my configuration:
$builder->add('items', CollectionType::class, [
'entry_type' => NumberType::class,
'required' => true,
'label' => 'Fields',
'entry_options' => [
'label' => false,
],
'prototype' => true,
])
and in Twig I was displaying Collection label in this way:
{{ form_label(form.items) }} and rendering field widgets in somewhere else. In this case label was displayed twice, once where this code is placed, and the other at the end of form.
Solution is to change it to:
{{ form_row(form.items) }}
or if you need custom rendering for form widgets like in my case, following code will print only labels:
{% if 0 < (form.items|length) %}
{{ form_label(form.items) }}
{% else %}
{{ form_row(form.items) }}
{% endif %}

multiple form in symfony2

I have a form for adding comments, and i want to know how can i multiply this form on the same page.
I tried this code but it does not work :
{% for statut in statuts %}
<div>
{{ statut.sujet }}
<form action="" method="post" {{ form_enctype(formC) }}>
{{ form_errors(formC) }}
{{ form_errors(formC.commentaire) }}
{{ form_widget(formC.commentaire) }}
<div><input type="submit" class="btn btn-info"/><div>
{{ form_rest(formC) }}
</form>
</div>
{% endfor %}
Does somebody know how to do that ? or any suggestions
You need to do this in the controller like get all statuts then run a for loop for each statuts and get comment form for each statut field store them in array and return the array.
In your twig template then run a for loop for the same.