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 %}
Related
This Symfony form question has been asked 100 times (and I've read ALL of the responses), but none are working for me. I have a class (Employer), a form (Preview.html.twig), and a controller (DefaultController.php). No matter what I try, I still get null values for the form fields. The form displays properly and I'm not saving to a database (I just want to dump the variables, then I'll move on to db action). This has consumed weeks of my life and any assistance is sincerely appreciated.
The Default Controller (DefaultController.php)
<?
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\Employer;
use App\Form\EmployerType;
class DefaultController extends AbstractController
{ /**
* #Route("/preview", name="preview")
*/
public function preview(Request $request)
{
$employer = new Employer();
$form = $this->createForm(EmployerType::class, $employer, ['csrf_protection' => false]);
$form->handleRequest($request);
//the next two lines were added to force the form to submit, which it wasn't doing prior to
if ($request->isMethod('POST')) {
$form->submit($request->request->get($form->getName()));
if ($form->isSubmitted() && $form->isValid()) {
$employer = $form->getData();
dump($form); /****** ALL ENTRIES FROM THIS DUMP ARE NULL. *****/
exit; /***Added to capture dump ******/
return $this->redirectToRoute('homepage'); /** Works when the exit is removed ***/
}
}
return $this->render('preview.html.twig',
['form'=> $form->createView()]
);
}}
The Employer Class (Employer.php)
namespace App\Entity;
class Employer
{
protected $companyName;
protected $companyAddress;
public function setCompanyName($companyName)
{ $this->companyName = trim($companyName); }
public function getCompanyName()
{ return $this->companyName; }
public function setCompanyAddress($companyAddress)
{ $this->companyAddress = trim($companyAddress); }
public function getCompanyAddress()
{ return $this->companyAddress; }
}
Form Builder (EmployerType.php)
<?php
namespace App\Form;
use App\Entity\Employer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class EmployerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('companyName', TextType::class, ['label' => 'Company Name', 'required' => false])
->add('companyAddress', TextType::class, ['label' => 'Address', 'required' => false])
->add('submit',SubmitType::class, ['label' => 'Submit & Preview'])
->getForm() //I've added and removed this line multiple times. Not sure if its needed.
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Employer::class,
]);
}
}
For Display (Preview.html.twig) ** Displays Form Correctly **
{{ form(form) }}
A few rabbit holes:
The site is running on localhost (Symfony, Apache, MySQL).
The form input is sent via POST.
The Default Controller redirects after the Submit; the "exit" was added to pause my code.
The form is not embedded. I've scaled back the entire project because I thought the embedded form was the issue.
I changed the method to PUT and can see the form values appended to the URL, but $employer = $form->getData() still populates $employer with null values.
I tried to get individual form fields upon submit using $form->get('companyName')->getData(); The data remains null.
I'm out of ideas on how to save the form data to the Employer object.
You must delete getForm() in EmployeType.
In DefaultController, delete the line that contains form->submit(). Here the employee that you initialized is the form which fills it automatically. To retrieve your employee, you no longer need to do $form->getData(). The employee is already full. You can check with dd($employer) instead of $employer = $form->getData()
I gave up and created a fresh instance of Symfony using Composer. Starting over led me to the issue. Something (I'm not yet confident of what) in my custom twig file was causing the problem. I'll update everyone once I figure it out.
Final Findings:
The name attribute for my form inputs were incorrect. The controller was expecting named input in the form of:
formName[formField] //Ex: employer[companyName]
and mine were the standard type generated by Twig (formName_formField)
The addition of:
<p>form.vars: </p>
{{ dump(form.vars) }}
in my Twig file led me to the answer. I modified the input using a custom form theme by first adding the following line to twig.yaml:
form_themes: ['custom_bootstrap.html.twig']
Then, in the custom file, I created a new instance for each type of input I use to override the defaults. For example, for checkboxes my code is:
{% use "form_div_layout.html.twig" %}
{%- block checkbox_widget -%}
<input type="checkbox" id="{{ form.vars.name }}" name="{{ full_name }}"
{%- if disabled %} disabled="disabled"{% endif -%}
{%- if required %} required="required"{% endif -%}
{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %}
{{ block('attributes') }}
/>
{%- endblock checkbox_widget -%}
You should be able to add the name values directly to your twig template without using a custom file. I really hope this helps someone save time.
I have a project related to restaurants.
I have an entity restaurant with several fields, and a foreign key related to another entity called people
Once I created the restaurant page with its form, and can view the restaurant in its view (show.html.twig), I should be able to click on a link that lets me add a value for how many people can eat there
This should open a new page, with a little form where I can add this value. Once submitted, I should be redirected to the restaurant page (show.html.twig) and then see the value which I just entered.
The FormType I created to add the number of people
class PeopleType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('value', NumberType::class, array(
'label' => 'How many people',
'required' => false,
))
->add('save', SubmitType::class, array(
'label' => 'submit'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => People::class,
));
}
}
and I created a specific controller for that
class PeopleController extends Controller
{
public function PeopleAction(Request $request, Restaurant $restaurant)
{
$people = new People();
$form = $this->createForm(PeopleType::class, $people);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($people);
$em->flush();
return $this->redirectToRoute('admin_restaurant_show', array('id' => $restaurant->getId()));
}
return $this->render('admin/restaurant/people.html.twig', array(
'restaurant' => $restaurant,
'form' => $form->createView()
));
}
}
and the route I created
admin_restaurant_people:
path: /{id}/people
defaults: { _controller: "AdminBundle:Retrocession:people" }
in my restaurant view(show.html.twig) where I already added a restaurant with a form. I have the link to the route going to my PeopleType form view
Add people
So once on this page I can add my value, and then redirect to the restaurant page show.html.twig when clicking on submit
And then to be able to display the value in the restaurant view, I added a twig field to be able to show it
<p>People</p>
{% for value in restaurant.people.values %}
<p>{{ value }}</p>
{% endfor %}
But then the value that was entered in the form, doesn'tshow up in the view. It is right in the database, but the view itself doesn't let me see it even with the twig.
here is my database with the People entity
I think I missed something somewhere. Can you help me find the problem?
Thank you
So in fact, you have an array. To resolve your issue, you can try to display your data in for loop
{% for value in restaurant.people.values %}
{{ value }}
{% endfor %}
I think you are expecting a single value. If that's the case, you should check your entities relations to figure out why you are getting an array.
To display only the first value, here is a ugly workaround with slice
{% for value in restaurant.people.values[:1] %}
{{ value }}
{% endfor %}
If you want sum all the values, you try that :
{% set sum = 0 %}
{% for value in restaurant.people.values[:1] %}
{% set sum = sum + value %}
{% endfor %}
At least, the above solutions will suppress your error.
I would like to render the same form multiple times to handle the same action for two different tabs.
The problem is that when I try, only the form of the first tab is shown, event if I change the id and name of the form.
I found out it's the expected behavior of symfony, but I still need it to work.
I found that it may works with a collection but don't get how it would work.
twig:
{{ form(contactForm, {'attr': {'id': 'contactFormId' ~ Client.Id}, 'name': "contactFormName" ~ Client.Id})}}
Form:
$this->contactForm = $this->createFormBuilder($contact, array('allow_extra_fields' =>true))
->add('Nom', TextType::class, array('mapped'=>false))
->add('Prenom', TextType::class, array('mapped'=>false))
->add('Telephone', TextType::class, array(
'label' => 'Téléphone'))
->add('Email', TextType::class)
->add('Ajouter', SubmitType::class)
->getForm();
It is an older question, but I just came across it facing a similar situation. I wanted to have multiple versions of one form object in a list view. For me the solution was to move the createView() call on the form object to the view instead of calling it in the controller. This is kind of a dirty solution regarding separation of concerns, but I thought to post it so it may help others anyway.
My controller action looks like this:
/**
* #Route("", name="cart_show")
* #Method("GET")
*/
public function showAction(Request $request)
{
/** #var CartInterface $cart */
$cart = $this->get('rodacker.cart');
$deleteForm = $this->createDeleteForm();
return $this->render(
'AppBundle:Cart:show.html.twig',
['cart' => $cart, 'deleteForm' => $deleteForm]
);
// ...
private function createDeleteForm()
{
return $this->createForm(
OrderItemDeleteType::class,
null,
[
'action' => $this->generateUrl('cart_remove_item'),
'method' => 'DELETE',
]
);
}
}
and in the view I set the form variable by calling the createView function on the form variable (deleteForm) passed from the controller:
{% for item in items %}
{% set form = deleteForm.createView %}
{{ form_start(form) }}
{{ form_widget(form.item, {'value': item.image.filename}) }}
<button type="submit" class="btn btn-xs btn-danger" title="Artikel entfernen">
<i class="fa fa-trash-o"></i> entfernen
</button>
{{ form_end(form) }}
{% endfor %}
Once you render a Symfony form, the same form will not render again.
I would suggest creating a form class and calling Controller::createForm() multiple times to create the desired amount of Form instances; you can call isSubmitted etc. on all forms independently.
http://symfony.com/doc/current/book/forms.html#creating-form-classes
I'm working with Symfony 2.0.14 and I would like to display the default value in my form template.
Well a FormType is bound to an entity, when I want to add extra field, I know the option property_path = false allow to add non-entity fields, right ?
When I m in the opposite case, I want to set an entity field without a form field.
Ok I just have to give a default entity to "createForm".
Howewver how can I render it in my template form ?
Controller code :
public function newAction(Request $request)
{
$game = new Game();
$local = new Role();
$visitor = new Role();
$local->setType('LOCAL');
$visitor->setType('VISITOR');
$game->addRole($local);
$game->addRole($visitor);
$form = $this->createForm(new GameType(), $game);
GameType code :
public function buildForm(FormBuilder $builder, array $options){
$builder->add('teams', 'collection', array( 'type' => new RoleType()));
}
RoleType code :
public function buildForm(FormBuilder $builder, array $options){
$builder->add('type', 'text'); // <= I would like read only for end-User
$builder->add('score', 'integer');
form template :
{% for role in form.teams %}
<li>
<div class="role-team">
{{ role.type }} {# WRONG way, how to do ? #}
{{ form_row(role.score) }}
</div>
</li>
{% endfor %}
If you want just to display your entity field value (without passing the entire entity to the view) you can print it with:
{{ form.vars.value.type }}
(assuming your role entity has type property).
EDIT: i realized you're inside the loop. Try figuring out the right property path using:
{% for role in form.teams %}
{% debug role %}
{% endfor %}
I've got a problem with displaying collection in my form.
When displaying my entity collection I've got something like this :
0
Name: myInputName
Address: myInputAddress
1
Name: myInputName
Address: myInputAddress
My question is why Symfony2 display the index...
And this for all saved entities into my collection...
Here the code I use:
$builder
->add('person', 'collection', array(
'label' => ' ',
'type' => new PersonType(),
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
))
;
In my twig file:
<div>
{{ form_widget(edit_form) }}
</div>
Help please
Sam
Removing indexes (labels) for collection items:
$builder
->add('person', 'collection', array(
...
'options' => array('label' => false)
))
;
Use key entry_options instead of options for Symfony 3 and 4
If you want to add custom labels per row you can produce the form yourself:
{{ form_start(edit_form) }}
{% for person in form.persons %}
{{ form_row(person, {'label': 'custom label per item' }) }}
{% endfor %}
{{ form_end(edit_form) }}
Note: tested on Symfony 2.3 & 2.4
This one is some days ago but because I was facing same question for Symfony 3 the answer of sectus is the correct one.
Use the
'entry_options' => ['label'=>false],
option within your builder to hide he object item.
Best Regards
You can custom the rendering of your collection for don't display the index with, by example:
{% block _FORMNAME_person_widget %}
{% spaceless %}
{% for child in form %}
{{ form_widget(child.Name) }}
{{ form_widget(child.Address) }}
{% endfor %}
{% endspaceless %}
{% endblock %}
I know this has been closed for a while. And not sure if this has been solved elsewhere. This issue is actually pretty simple to fix and I am surprised there is no documentation about this anywhere. In the PersonType or any type that is used in a collections just modify the vars['name'] in the buildView to be what you want displayed as the label.
public function buildView(FormView $view, FormInterface $form, array $options)
{
// Adjust the view based on data passed
$this->vars['name'] = $form->getData();
// Or...
$this->vars['name'] = 'Some random string';
}
If you want it dynamic, you would use the object by form->getData(). Since, in my problem, I am using a form theme, overriding the twig is not really an option for me.
Hope this helps someone.
Using #MrBandersnatch's solution below, I had to use $view->vars['name'] instead of $this->vars['name'] (Symfony 2.3).
(apologies for not adding this as a comment on #MrBandersnatch's answer, I've not got enough reputation yet).