Symfony2 Setting a default choice field selection - forms

I am creating a form in the following manner:
$form = $this->createFormBuilder($breed)
->add('species', 'entity', array(
'class' => 'BFPEduBundle:Item',
'property' => 'name',
'query_builder' => function(ItemRepository $er){
return $er->createQueryBuilder('i')
->where("i.type = 'species'")
->orderBy('i.name', 'ASC');
}))
->add('breed', 'text', array('required'=>true))
->add('size', 'textarea', array('required' => false))
->getForm()
How can I set a default value for the species listbox?
Thank you for your response, I apologise, I think I should rephrase my question. Once I have a value that I retrieve from the model, how do I set that value as SELECTED="yes" for the corresponding value in the species choice list?
So, that select option output from the TWIG view would appear like so:
<option value="174" selected="yes">Dog</option>

You can define the default value from the 'data' attribute. This is part of the Abstract "field" type (http://symfony.com/doc/2.0/reference/forms/types/field.html)
$form = $this->createFormBuilder()
->add('status', 'choice', array(
'choices' => array(
0 => 'Published',
1 => 'Draft'
),
'data' => 1
))
->getForm();
In this example, 'Draft' would be set as the default selected value.

If you use Cristian's solution, you'll need to inject the EntityManager into your FormType class. Here is a simplified example:
class EntityType extends AbstractType{
public function __construct($em) {
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options){
$builder
->add('MyEntity', 'entity', array(
'class' => 'AcmeDemoBundle:Entity',
'property' => 'name',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('e')
->orderBy('e.name', 'ASC');
},
'data' => $this->em->getReference("AcmeDemoBundle:Entity", 3)
));
}
}
And your controller:
// ...
$form = $this->createForm(new EntityType($this->getDoctrine()->getManager()), $entity);
// ...
From Doctrine Docs:
The method EntityManager#getReference($entityName, $identifier) lets you obtain a reference to an entity for which the identifier is known, without loading that entity from the database. This is useful, for example, as a performance enhancement, when you want to establish an association to an entity for which you have the identifier.

the solution: for type entity use option "data" but value is a object. ie:
$em = $this->getDoctrine()->getEntityManager();
->add('sucursal', 'entity', array
(
'class' => 'TestGeneralBundle:Sucursal',
'property'=>'descripcion',
'label' => 'Sucursal',
'required' => false,
'data'=>$em->getReference("TestGeneralBundle:Sucursal",3)
))

I think you should simply use $breed->setSpecies($species), for instance in my form I have:
$m = new Member();
$m->setBirthDate(new \DateTime);
$form = $this->createForm(new MemberType, $m);
and that sets my default selection to the current date. Should work the same way for external entities...

If you want to pass in an array of Doctrine entities, try something like this (Symfony 3.0+):
protected $entities;
protected $selectedEntities;
public function __construct($entities = null, $selectedEntities = null)
{
$this->entities = $entities;
$this->selectedEntities = $selectedEntities;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('entities', 'entity', [
'class' => 'MyBundle:MyEntity',
'choices' => $this->entities,
'property' => 'id',
'multiple' => true,
'expanded' => true,
'data' => $this->selectedEntities,
]);
}

I don't think you should use the data option, because this does more than just setting a default value.
You're also overriding any data that's being passed to the form during creation. So basically, you're breaking
support for that feature. - Which might not matter when you're letting the user create data, but does matter when you
want to (someday) use the form for updating data.
See http://symfony.com/doc/current/reference/forms/types/choice.html#data
I believe it would be better to pass any default data during form creation. In the controller.
For example, you can pass in a class and define the default value in your class itself.
(when using the default Symfony\Bundle\FrameworkBundle\Controller\Controller)
$form = $this->createForm(AnimalType::class, [
'species' => 174 // this id might be substituted by an entity
]);
Or when using objects:
$dog = new Dog();
$dog->setSpecies(174); // this id might be substituted by an entity
$form = $this->createForm(AnimalType::class, $dog);
Even better when using a factory:
(where dog probably extends from animal)
$form = $this->createForm(AnimalType::class, DogFactory::create());
This will enable you to separate form structure and content from each other and make
your form reusable in more situations.
Or, use the preferred_choices option, but this has the side effect of moving the default option to the top of your form.
See: http://symfony.com/doc/current/reference/forms/types/choice.html#preferred-choices
$builder->add(
'species',
'entity',
[
'class' => 'BFPEduBundle:Item',
'property' => 'name',
'query_builder' => ...,
'preferred_choices' => [174] // this id might be substituted by an entity
]
);

I'm not sure what you are doing wrong here, when I build a form using form classes Symfony takes care of selecting the correct option in the list. Here's an example of one of my forms that works.
In the controller for the edit action:
$entity = $em->getRepository('FooBarBundle:CampaignEntity')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find CampaignEntity entity.');
}
$editForm = $this->createForm(new CampaignEntityType(), $entity);
$deleteForm = $this->createDeleteForm($id);
return $this->render('FooBarBundle:CampaignEntity:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
The campaign entity type class (src: Foo\BarBundle\Form\CampaignEntityType.php):
namespace Foo\BarBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Doctrine\ORM\EntityRepository;
class CampaignEntityType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('store', 'entity', array('class'=>'FooBarBundle:Store', 'property'=>'name', 'em'=>'my_non_default_em','required' => true, 'query_builder' => function(EntityRepository $er) {return $er->createQueryBuilder('s')->orderBy('s.name', 'ASC');}))
->add('reward');
}
public function getName()
{
return 'foo_barbundle_campaignentitytype';
}
}

From the docs:
public Form createNamed(string|FormTypeInterface $type, string $name, mixed $data = null, array $options = array())
mixed $data = null is the default options. So for example I have a field called status and I implemented it as so:
$default = array('Status' => 'pending');
$filter_form = $this->get('form.factory')->createNamedBuilder('filter', 'form', $default)
->add('Status', 'choice', array(
'choices' => array(
'' => 'Please Select...',
'rejected' => 'Rejected',
'incomplete' => 'Incomplete',
'pending' => 'Pending',
'approved' => 'Approved',
'validated' => 'Validated',
'processed' => 'Processed'
)
))->getForm();

Setting default choice for symfony2 radio button
$builder->add('range_options', 'choice', array(
'choices' => array('day'=>'Day', 'week'=>'Week', 'month'=>'Month'),
'data'=>'day', //set default value
'required'=>true,
'empty_data'=>null,
'multiple'=>false,
'expanded'=> true
))

The form should map the species->id value automatically to the selected entity select field. For example if your have a Breed entity that has a OnetoOne relationship with a Species entity in a join table called 'breed_species':
class Breed{
private $species;
/**
* #ORM\OneToOne(targetEntity="BreedSpecies", mappedBy="breed")
*/
private $breedSpecies;
public function getSpecies(){
return $breedSpecies->getSpecies();
}
private function getBreedSpecies(){
return $this->$breedSpecies;
}
}
The field 'species' in the form class should pick up the species->id value from the 'species' attribute object in the Breed class passed to the form.
Alternatively, you can explicitly set the value by explicitly passing the species entity into the form using SetData():
$breedForm = $this->createForm( new BreedForm(), $breed );
$species = $breed->getBreedSpecies()->getSpecies();
$breedForm->get('species')->setData( $species );
return $this->render( 'AcmeBundle:Computer:edit.html.twig'
, array( 'breed' => $breed
, 'breedForm' => $breedForm->createView()
)
);

You can either define the right default value into the model you want to edit with this form or you can specify an empty_data option so your code become:
$form = $this
->createFormBuilder($breed)
->add(
'species',
'entity',
array(
'class' => 'BFPEduBundle:Item',
'property' => 'name',
'empty_data' => 123,
'query_builder' => function(ItemRepository $er) {
return $er
->createQueryBuilder('i')
->where("i.type = 'species'")
->orderBy('i.name', 'ASC')
;
}
)
)
->add('breed', 'text', array('required'=>true))
->add('size', 'textarea', array('required' => false))
->getForm()
;

You can use "preferred_choices" and "push" the name you want to select to the top of the list. Then it will be selected by default.
'preferred_choices' => array(1), //1 is item number
entity Field Type

Related

Symfony dynamic EntityType data attribute

I have a simple symfony form and i use this form for create and edit an object. When i try to create the form there is a user should be always selectd when someone creating and when editing it should be the selected user (if the author wanted to change the default user from the list).
something like this:
->add('user', EntityType::class, [
'class' => User::class,
'label' => 'user',
'choice_label' => 'name',
'data' => $this->em->getRepository(User::class)->find(1),
])
Now it works on create but when i change the value and then try to edit after save it gives me the default User agean.
so is there is any way to make data dynamic when the form is new? by the way i have an option in my form tells when the form new ('new' => true).
I solved it this way:
I did not found a cleaner way to make it.
$userOptions = [
'class' => User::class,
'label' => 'User',
'choice_label' => 'name'
];
if ($options['new']) {
$userOptions['data'] = $this->defaultUser; // the default User i got from repository
}
It's a correct way, but it can be done like that:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$form->add('user', EntityType::class, [
'class' => User::class,
'label' => 'user',
'choice_label' => 'name',
'data' => $event->getData() ?: $this->em->getRepository(User::class)->find(1) // or $this->defaultUser
]);
});
}

Symfony form EntityType::class - How use it to edit data in form (3.2.13)

A dropdown selection menu in a Form is available by using a EntityType form type. It is usefull if you add data. But when you try to edit data with the same FormType class, your $request data are overwritten by your EntityType.
How use the same FormType class when editing the data? (eg. editAction in controller) How pass Request $request data to FormType fields as "defaults, or selected" for element EntityType::class in FormBuilder? Is there something in $builder->add() method I can use like if(['choice_value'] !=== null ) xx : yy?
How to get something like html select selected, from Request object and pass it to xxxFormType class and bind to right EntityType::class element there.
<select>
<option value="volvo">Volvo</option>
<option value="vw">VW</option>
<option value="audi" selected>Audi</option>
</select>
I've looked at EntityType Field,
How to Dynamically Modify Forms Using Form Events and lots of StackOverflow posts, but can't find proper solution.
Controller:
public function editProdPackageAction(Request $request, ProdPackage $prodPackage)
{
$form = $this->createForm(ProdPackageFormType::class, $prodPackage);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$prodPackage = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($prodPackage);
$em->flush();
$this->addFlash('success', 'MSG#011E');
return $this->redirectToRoute('admin_package_list');
}
return $this->render(':admin/forms/ProdPackage:edit.html.twig',
[
'ppg' => $form->createView(),
]
);
}
Formtype:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'idCatBig',
EntityType::class,
[
'label' => '* category',
'class' => 'AppBundle\Entity\ProdCategoryBig',
'choice_value' => 'id',
'choice_label' => 'shortName',
'multiple' => false,
'expanded' => false,
]
)
->add(
'dateStart',
DateType::class,
[
'label' => '* some date',
'data' => new \DateTime('now'),
'choice_translation_domain' => true,
]
)
->add(
'dateEnd',
DateType::class,
[
'label' => '* till',
'data' => new \DateTime('now'),
]
)
->add(
'packageName',
TextType::class,
[
'label' => '* package',
'attr' => ['placeholder' => 'default Name'],
'data' => 'your default value',
]
)
This is what I do in my form, I set a "pre set data listener" to check whether or not this is an edit or a create
function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add(
'dateStart',
DateType::class,
[
'label' => '* some date'
//I don't set a data field here because it is an edit
'choice_translation_domain' => true,
]
)
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (!$data || null === $data->getId()) {
//if the entity does not have an ID it means that this is a new entity not an edit. because all edits have been in the database and have an id
$builder->add(
'dateStart',
DateType::class,
['label' => '* some date',
'data' => new \DateTime('now'), //this is a create so I override my previous setting and set a default data
'choice_translation_domain' => true,]
)
}
});
}
I mainly use this trick to change form fields from required to non required between edits and creates for password fields for example,and sometimes if there is something exceedingly complicated. To change data, honestly, setting default data in constructor is cleaner as Stephan suggests
Problem
You are overriding an object by using the data option.
https://symfony.com/doc/3.2/reference/forms/types/entity.html#data:
( ! ) The data option always overrides the value taken from the domain data (object) when rendering. This means the object value is also overriden when the form edits an already persisted object, causing it to lose its persisted value when the form is submitted.
Solution 1: use the controller
So the solution is pretty simple: don't use data. Instead, set default values in your addProdPackageAction action (or whatever it is called):
public function editProdPackageAction(Request $request)
{
$prodPackage = new ProdPackage();
$prodPackage->setDateEnd(new Datetime('now'));
//example: use Request or other controller methods to modify your entity
if ($this->getUser()->hasRole('ROLE_ADMIN')) {
$prodPackage->setCreatedByAdmin(true);
}
//your form
}
Solution 2: use your entity constructor
Alternatively, you can use your entity constructor method:
class ProdPackage
{
//your attributes
private $dateEnd;
public function __construct()
{
$this->dateEnd = new Datetime('now');
}
}
Found another interesting solution
in formTypeclass at $builder
->add(
'trBegin',
DateTimeType::class,
[
'label' => 'Tourney Begins',
'required' => true,
'date_widget' => 'single_text',
'time_widget' => 'single_text',
'date_format' => 'dd.MM.yyyy',
]
)
and continuing building form add:
$builder->get('trBegin')->addModelTransformer(
new CallbackTransformer(
function ($value) {
if (!$value) {
return new \DateTime('now + 60 minutes');
}
return $value;
},
function ($value) {
return $value;
}
)
);
it sets default date at moment when form is created. This method is very usefull also for EntityType object, where you can pass id of field in form and get selected your current real choice from database (not all list from the begining) very useful when using EntityField also for editing forms
$builder->get('productCategory')
->addModelTransformer(new CallbackTransformer(
function ($id) {
if (!$id) {
return;
}
return $this->em->getRepository('AppBundle:ProductCategory')->find($id);
},
function($category) {
return $category->getId();
}
));

Advanced FORM Symfony 2.7

i have a little problem with form. I have made form class and i need form field which will be like radio or select. Choices for select i need to pull from DB of another form. Here is the code so pls tell me where is my problem and how to solve it.
I want to have option when i create POST to choose from available pages (their names) and store that so i know for each post to which page it belongs and for each page i do query and show posts for that page..
<?php
namespace AppBundle\AppForm;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use AppBundle\Entity\Page;
class PostForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('content', 'textarea')
->add('visible', 'choice', array(
'choices' => array(
'Yes' => 1,
'No' => 0
)
))
->add('belongToPage', 'choice', array(
'choices' => array (
new Page()
// here i want to pull from class Page names of
//all pages stored and to present
// this names for options in form field
),
'choices_as_values' => true,
'choice_label' => 'getName'
//getName is function from class Page which returns name of page(s)
))
->add('save', 'submit', array('label' => 'Create Post'));
}
public function getName()
{
// TODO: Implement getName() method.
}
}
Why don't you use entity field type. It would be smth like this:
->add('belongToPage', 'entity', array(
'class' => 'Class\Namespace\Class',
'property' => 'name',
'label' => 'choice_field_label'
))
If you need smth more complicated then just findAll for this field, you could use query_builder option:
->add('belongToPage', 'entity', array(
'class' => 'Class\Namespace\Class',
'property' => 'name',
'label' => 'choice_field_label',
'query_builder' => function(EntityRepository $er) {
return $er->findAllPagesForPostForm();
//Where findAllPagesForPostForm is the name of method in your
// pagesRepo which returns queryBuilder,
//instead of this you could just write your custom query like
//$qb = $er->createQueryBuilder('p');
//$qb->andWhere(...);
//return $qb;
}
))

remove entity from entity form field if custom condition is matched

I have an unsubscribe form, where the first field is a entity field, fetching my UnsubscribeType entities :
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'property' => 'name',
'label' => 'Choose process type',
'empty_value' => 'Choose an option',
'empty_data' => null,
'label_attr' => array(
'class' => 'control-label',
),
'attr' => array(
'class' => 'form-control',
),
))
this collection contains 2 entities, #1 and #2. I would like to remove entity #2 from the select field if a custom test about the authed user fails.
Let's say I'd to test $this->getUser()->getCustomField(). If it is false, then i would like to remove entity #2 from the select field containing my UnsubscribeType entities.
Any idea how to do that ?
Perhaps you could pass to the form builder the result of the test from you controller and then, either use 'property' => 'name' or 'choices => // fetch entity #1 here.
So, something like this
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($options['customField'] === TRUE)
{
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'property' => 'name',
// ..
;
}
else
{
$builder
->add('type', 'entity', array(
'class' => 'Evo\SubscriptionBundle\Entity\UnsubscribeType',
'choices' => $options['customField'],
// ..
;
}
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// ..
$resolver->setRequired(array(
'customField'
));
}
And in your controller:
$form = $this->get('form.factory')->create(new EntryType(), $entry, array(
'customField' => $this->getUser()->getCustomField()
));
with getCustomField() returning either true or the entity (or collection of entities) that you want populating your select field. My solution would involve changing the getCustomField method though, don't know if that would suit.

Symfony change form collection dropdown with propel model

Is it possible to change the contents of a form dropdown that is part of a form collection that is populated using propel but the data is not mapped. Example of code to get the data below:
AddressType:
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->
->add("addressOne", new addressOneType()),
->add("addressTwo", new addressTwoType(), array(
"required" => false,
)),
}
addressOneType:
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->
->setMethod('POST')
->add('Country', 'model', array(
'mapped' => 'bundle\nameBundle\Model\Countries',
'required' => true,
'multiple' => false,
'expanded' => false,
'property' => 'label',
'query' => CountryQuery::create()->find(),
))
->getForm();
}
This collection is used for a particular part of an application however in this part in need to call a service from the form itself. Would this be possible as I've tried to extend the ContainerInterface and declare this inside of the construct method however this just throws an error.
However, I beleive this to be due to the fact that the form builder is not declared as a service.
Is there an easier way of changing the data of the drop down menu by injecting a new model to override the original. For example:
$form = $this->createForm(new AddressType());
$newData = CountriesQuery::create()
->orderBy("different_field");
$form['collectionName']['fieldname']->setData($newData);
Doing the above doesn't change or override the original model that is changing the data. With or without the ->find() at the end of the $newData field.
Does anyone know of a way to overwrite the data set by the model?
A very simple way for pass specific options to form is in constructor ...
class addressOneType
{
protected $countryQuery;
public function __constructor( $countryQuery = null )
{
$this->countryQuery = $countryQuery;
}
public function buildForm(FormBuilderInterface $builder, array $options){
$query = $this->countryQuery ? $this->countryQuery :
CountryQuery::create();
$builder
->setMethod('POST')
->add('Country', 'model', array(
'mapped' => 'bundle\nameBundle\Model\Countries',
'required' => true,
'multiple' => false,
'expanded' => false,
'property' => 'label',
'query' => $query->find(),
))
->getForm();
}
}
... and you can call to form in this way ...
$cQuery = CountriesQuery::create()->orderBy("different_field");
$form = $this->createForm(new AddressType($cQuery));