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 %}
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 am working on a Symfony 2.7 WebApp and I would like to use a custom Form Widget for one of the entites. The Widgets needs to access the form.vars.value. This works fine as long as the Widget is uses within the main form. But when using the Widget in a subform, form.vars.value is empty.
The classes used within the form:
class AdressBookEntry {
// The main phone number of this contact: Type PhoneNumber
protected $mainPhoneNumber;
//...getter and setter for mainPhoneNumber
// An array of Addresses
protected $addresses;
//...getter and setter for addresses
...
}
class Address {
// The phone number of this address: Type PhoneNumber
protected $phoneNumber;
//...getter and setter for phoneNumber
...
}
class PhoneNumber {
...
}
The custom Form Types for theses classes:
// Custom FormType for AddressBookEntries
class AdressBookEntryType extends AbstractType {
...
public function buildForm(FormBuilderInterface $builder, array $options) {
// Type 'phone_number_edit' is registered in services.yml
$builder
->add('mainPhoneNumber', 'phone_number_edit', array(
'label' => '...',
...
))
->add('addresses', 'collection', array(
'label' => '...',
...
));
}
}
// Custom FormType for Address
class AddressType extends AbstractType {
...
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('mainPhoneNumber', 'phone_number_edit', array(
'label' => '...',
...
))
...;
}
}
The custom Widget for the PhoneNumberEdit
{% block phone_number_edit_widget %}
...
{{ dump(form.vars.value) }}
...
The PhoneNumberEdit for the main form (representing the AddressBookEntry) works fine. The dump statement shows the content of the assigned PhoneNumber object.
Within the Subform of the addresses collection however, the form.vars.value variable is empty. The dump shows just "".
So, how do I access form.vars.value within the subform? How can the widget recognize wether it is being uses within the main form or a subform?
UPDATE:
Some additional information as asked in the comments:
#Jeet: As described before the dump shows an empty value/string: ""
#DOZ: This is the Twig code:
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.name) }}
{{ form_widget(mainPhoneNumber) }}
<ul data-prototype"{{ _self.addressItem(form.addresses.vars.prototype)|e('html_attr') }}" >
{% for address in form.addresses %}
{{ _self.addressItem(address) }}
{% endfor %}
</u>
...
{{ form_end(form) }}
{% macro addressItem(address) %}
<li>
{{ form_widget(address.phoneNumber) }}
...
</li>
{% endmacro %}
Use value instead of form.vars.value
{% block phone_number_edit_widget %}
...
{{ dump(value) }}
...
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 %}
My app consists of Zones that can have many Devices.
When viewing a Zone, it needs to display a control for each Device in the Zone.
Each Device is completely independent, so embedding the Device forms in a Zone form seems unnecessary - I only want to deal with changes to one device at a time.
Currently I'm creating a form for each device and passing them to the Zone view template:
public function viewAction($zone_id)
{
$zone = $this->getZoneById($zone_id);
$forms = array();
foreach ($zone->getDevices() as $device) {
$forms[] = $this->createForm(new DeviceType(), $device)->createView();
}
return $this->render('AcmeBundle:Zones:view.html.twig', array('zone' => $zone, 'deviceForms' => $forms));
}
And then in the view template, I'm looping through the forms:
{% for form in deviceForms %}
{% include 'AcmeBundle:Devices:control.html.twig'
with {'zone':zone, 'form':form}
%}
{% endfor %}
This seems to be working ok, but I really need to change the template that renders based on the 'type' of Device. What's the cleanest way to do this? I can do something like:
{% if form.vars.data.type == 'foo' %}
{% include 'AcmeBundle:Devices:control-foo.html.twig'
with {'zone':zone, 'form':form}
%}
{% elseif form.vars.data.type == 'bar' %}
{% include 'AcmeBundle:Devices:control-bar.html.twig'
with {'zone':zone, 'form':form}
%}
{% endif %}
but this seems like putting too much logic in the template? It would be better to assign the template to render to the form object somehow, but I've no idea if this is possible?
You must add an option 'template' or whatever in the FormType via the controller,
In the FormType you must declare the default option 'template' and pass it the the form view.
public function viewAction($zone_id)
{
$zone = $this->getZoneById($zone_id);
$forms = array();
//You define a config for each type of device (you should use parameters)
$templates = array(
'foo' => 'AcmeBundle:Devices:control-foo.html.twig',
'bar' => 'AcmeBundle:Devices:control-bar.html.twig',
);
foreach ($zone->getDevices() as $device) {
//define your template here.
$type = $device->getType();
//add a template option in the form.
$options['template'] == $templates[$type];
$forms[] = $this->createForm(new DeviceType(), $device, $options)->createView();
}
return $this->render('AcmeBundle:Zones:view.html.twig', array('zone' => $zone, 'deviceForms' => $forms));
}
Now in the DeviceType you should set the defaults options in the form, they will be merged with options we create in the controller.
public function getDefaultOptions(array $options) {
return array(
//...other options...
//this is the default template of this form
'template' => 'AcmeBundle:Devices:control.html.twig'
);
}
Then set the attribute on the form in the Builder
public function buildForm(FormBuilder $builder, array $options)
{
$builder->setAttribute('template', $options['template']);
//...your fields here...
}
And finally, set the var template in the view.
public function buildView(FormView $view, FormInterface $form)
{
$view->set('template', $form->getAttribute('template'));
}
Now you can read the "template" option in twig, and include the corresponding template
{% for form in deviceForms %}
{% include form.get('template') with {'zone':zone, 'form':form} %}
{% endfor %}
Do not forget to add lines at the beginning of the FormType
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormBuilder;