I want to select an option in the dropdown from the controller. I am trying with the following piece of code:
$form = $this->createForm(new SearchAdvancedType());
$form->get('option')->setData($session->get('option'));
But it is doing nothing in the dropdown. Nothing is selected when the page loads.
To check if the value was well set I print it using:
$form->get('brand')->Data();
and the result was a number (it changes depending of what I choosed in the dropdown before).
I need to know the way to select the value of the dropdown properly.
To preset a select option I would pass the value into the form.
class MyFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', 'choice', [
'required' => true,
'choices' => ['yes' => 'Yes', 'no' => 'No'],
'data' => $options['select_option']
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'select_option' => null
));
}
/**
* #return string
*/
public function getName()
{
return 'my_form';
}
}
Then in your controller, pass the value in;
$form = $this->createForm(new MyFormType(), null, ['select_option' => 'no');
Related
I want to build a form with ChoiceType and the option values/choices are based on database table (with records already).
When the form displayed, the list of religions will be available at the dropdown list/combo box.
Example :
$builder->add('name', ChoiceType::class, array(
'choices' => $religions //List of religions
));
So far here are my codes:
class Religion
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50)
*/
protected $name;
/*** getter/setter ... ***/
}
/Form/ReligionType
class ReligionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', ChoiceType::class, array(
'choices' => ____________
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Religion'
));
}
public function getName()
{
return 'app_bundle_religion_type';
}
}
/Controller
/**
* #Route("/religion/select", name="religion_select")
*
*/
public function selectAction()
{
$em = $this->getDoctrine()->getManager();
$religions = $em->getRepository('AppBundle:Religion')->findAll();
$form = $this->createForm(ReligionType::class, ________);
return $this->render(
'religion/index.html.twig', array(
'form' => $form->createView()
)
);
}
I don't really know what to write so i leave it as __________ and what are the missing codes.
/ReligionRepository
class ReligionRepository extends EntityRepository
{
public function findAll()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Religion p ORDER BY p.name ASC'
)
->getResult();
}
}
/Twig File
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
{{ form_widget(form) }}
<button type="submit">Save</button>
{{ form_end(form) }}
{% endblock %}
as suggested by Rvanlaak, here's the solution.
//ReligionType
$builder->add('religions', EntityType::class, array(
'class' => 'AppBundle\Entity\Religion',
'choice_label' => 'name'
));
//Controller
public function newAction(Request $request)
{
$religion = new Religion();
$form = $this->createForm(ReligionType::class, $religion);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($religion);
$em->flush();
//return $this->redirectToRoute('homepage');
}
return $this->render(
'religion/new.html.twig',
array(
'form' => $form->createView()
)
);
}
I did like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('currency', null, [
'expanded' => true,
'multiple' => true,
'label' => 'Currency'
])
In my entity like this:
protected $currency;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->Currency = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \MyBundle\Entity\Currency $Currency
*
* #return $this
*/
public function addCurrency(\MyBundle\Entity\Currency $Currency)
{
$this->Currency[] = $Currency;
return $this;
}
/**
* Remove Currency
*
* #param \MyBundle\Entity\Currency $Currency
*/
public function removeCurrency(\MyBundle\Entity\Currency $Currency)
{
$this->Currency->removeElement($Currency);
}
/**
* Get Currency
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCurrency()
{
return $this->Currency;
}
Both posted solutions didn't work for me because the ChoiceType type was removed from it. Here is a solution with the ChoiceType still existing:
I wanted to generate a list with types of absents users can choose from. These types are created by a user in the system settings. I first load them from db, then create one array (for loop) with where the ["name"] will be the name the user sees. The id is the value that the html select field will have and return when selected and saved.
What I added and could not find anywhere was the extra option to pass an option in the createForm function. This enables you to send data to the form basicly.
$systemAbsents = $this->getDoctrine()->getRepository(SystemAbsent::class)->getAllNonDeletedSystemAbsents();
$choices = [];
// Add each choice to the list. The id's have to match correctly so the html choicetype will return the chosen id that then will be saved in the db.
for ($i = 0; $i < count($systemAbsents); $i++) {
$choices += [$systemAbsents[$i]["name"] => $systemAbsents[$i]["id"]];
}
$absent = new Absent();
// Create form and pass the choices to later connect them with the ChoiceType field.
$form = $this->createForm(AbsentType::class, $absent, ['choices' => $choices]);
$form->handleRequest($request);
Then in the form, you can use the data and pass it to the 'choices' option.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->choices = $options['choices'];
$builder
->add('choices', ChoiceType::class, [
'choices' => $this->choices,
'label_attr' => [
'class' => 'bold',
],
'attr' => [
'class' => 'input-margin select-field w-100',
],
'mapped' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => [],
]);
}
The array I create in the for loop looks like this:
[
"absent-type-1" => 10
"absent-type-2" => 11
"absent-type-3" => 12
"absent-type-4" => 13
"absent-type-5" => 14
"absent-type-6" => 15
"absent-type-7" => 16
"absent-type-8" => 17
]
The ID starts at 10 because I deleted some while testing.
If you by default want to show a selected value in the ChoiceType field you can do setData after you create the form. Like this:
// Create form
$form = $this->createForm(AbsentType::class, $absent, ['choices' => $choices]);
// Add this line to set a default. But make sure you pass the value equal to the html `value` attribute.
$form->get('system_absents')->setData($absent->getSystemAbsentID());
$form->handleRequest($request);
There is a possiblity you get an Array to string conversion error. I dont know how exactly this works but i've once used this solution before.
Getting below error..
An exception has been thrown during the rendering of a template ("The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class Proxies__CG__\BLA\MyBundle\Entity\TransportType. You can avoid this error by setting the "data_class" option to "Proxies__CG__\BLA\MyBundle\Entity\TransportType" or by adding a view transformer that transforms an instance of class Proxies__CG__\BLA\MyBundle\Entity\TransportType to scalar, array or an instance of \ArrayAccess.") in MyBundle:Shipping:form.html.twig at line 8.
$builder->add('variables','collection', array(
'type' => new AbcType(),
'options' => array(
'required' => true,
),
'constraints' => new NotNull()));
AbcType.php
class AbcType extends AbstractType
{
/**
* Build form
*
* #param FormBuilder $builder
* #param array $options
*
* #return void
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('importance', null, array('empty_value'=>false,'expanded'=>true,
'required'=>true,'multiple'=>false,
'constraints' => new NotNull()))
->add('timeSpent', null, array(
'empty_value'=>false,'expanded'=>true,
'required'=>true,'multiple'=>false,
'constraints' => new NotNull()
)
);
}
/**
* setDefaultOptions set Default values
*
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Demo\MyBundle\Entity\Abc'
));
}
/**
* getName will return Form name
* #return string
*/
public function getName()
{
return 'demo_mybundle_abctype';
}
}
I fixed issue using below link....
https://github.com/symfony/symfony/issues/14877
Make sure you have the 'data_class' default specified in the setDefaultOptions in the AbcType class:
... build form ...
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AbcBundle\Entity\Abc'
));
}
In symfony I have a multi-stage form that embeds other forms. Some of these embedded forms have dynamic validation groups set via the validation_groups callback in the configureOptions (used to be setDefaultOptions) method for the type.
When the form is submitted via a submit that does not have its validation_groups option set, then these callbacks are run and the correct validation groups are used. But when I set the validation_groups option of a submit to a type that has this callback, then the callback is not run and the groups are not set as needed.
Are there any options that need to be set to have this working?
Controller
$form = $this->createForm(new RegistrationType());
$form->add('Submit1', 'submit', array(
'validation_groups' => array('Person'),
))
->add('Submit2', 'submit', array(
'validation_groups' => array('Contact', 'Address'),
))
;
...
Registration type
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RegistrationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('person', new PersonType())
->add('maritalStatus', 'entity', array(
'class' => 'AppBundle:MaritalStatus',
'choice_label' => 'status',
))
->add('spouse', new SpouseType())
->add('homeAddress', new AddressType())
->add('postalAddress', new AddressType())
->add('contact', new ContactType())
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Registration',
'cascade_validation' => true,
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_registration';
}
}
PersonType
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PersonType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('name', 'text')
->add('surname', 'text')
->add('fullName', 'text', array('required' => false))
->add('southAfrican', 'checkbox', array(
'required' => false,
'data' => true,
))
->add('identification', 'text')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Person',
// Setting some groups dynamically based on input data
'validation_groups' => function (FormInterface $form) {
$data = $form->getData();
$groups = array('Person');
// Add southAfrican validation group if is a southAfrican
if ($data->isSouthAfrican() === true) {
$groups[] = 'southAfrican';
}
// Add Married validation group is this is spouse and Married is selected
if ($form->getParent()->getConfig()->getName() === 'spouse') {
if ($form->getParent()->getParent()->getData()->getMaritalStatus()->getStatus() === 'Married') {
$groups[] = 'married';
} elseif (($key = array_search('southAfrican', $groups)) !== false) {
unset($groups[$key]);
}
// If this is not spouse then this is applicant so add its validation group
} else {
$groups[] = 'applicant';
}
return $groups;
},
'cascade_validation' => true,
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_person';
}
}
Submit button validation groups have precedence over Type's validation groups. Form::getClickedButton returns a clicked button that is bound to a current form or its parents.
The case you described can be resolved by passing callbacks instead of plain groups to the submit buttons. E.g.:
$form->add('Submit1', 'submit', array(
'validation_groups' => function (FormInterface $form) {
if ($form->isRoot()) {
return array('Person');
} else {
return ($form->getConfig()->getOptions('validation_groups'))();
}
},
))
How can you have a form type with a collection field of the same form type embedded inside it? I've got the following form type within my Symfony 2 project:
class MenuType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('label')
->add('uri')
// the following does not work
->add('children', 'collection', array(
'type' => new MenuType(),
'allow_add' => true,
'by_reference' => false
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mango\CoreDomain\Model\Menu',
'csrf_protection' => false,
'cascade_validation' => true
));
}
/**
* #return string
*/
public function getName()
{
return 'menu';
}
}
When you run this, it will not work. How would something like this be solved in a clean way?
The class that this form type uses is called Menu and has a property children which is an array of Menu objects.
Thanks!
Hello everybody (please excuse my English).
I want to do an application which needs to allow that the users must fill out on a form their personal data, their children, grandchildren and great-grandchildren (a little family tree).
class Person
{
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $firstname;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $lastname;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime")
*/
private $dateOfBirth;
/**
* #var Person
*
* #ORM\ManyToMany(targetEntity="Person")
*/
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
}
}
In the PersonType class, I do the following:
class PersonType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstname');
$builder->add('lastname');
$builder->add('dateOfBirth');
$builder->add('children', 'collection', array(
'type' => new PersonType(),
'allow_add' => true,
'by_reference' => false,)
);
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person'
));
}
/**
* #return string
*/
public function getName()
{
return 'person';
}
}
In this way, I use the PersonType in the controller as below:
public function newAction()
{
$entity = new Person();
$form = $this->createForm(new PersonType(), $entity, array(
'action' => $this->generateUrl('person_create'),
'method' => 'POST',
));
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
But the problem is when I request the url of this action, and the view of this action has to be rendered, there is a problem because doesn't give a response, because is in a infinite loop (I think that is the reason). I would like to know if is this possible to do using the Symfony forms, or if I have to look at other alternatives. If this was possible, how could I do that and how could I limit the form to only render the four levels that I need (me, my children, my grandchildren and my great-grandchildren)??
I hope that the problem has been understood.
Thanks in advance.
You could add a custom parameter to your form that indicates the current level of recursion.
To archive this you first need to implement a new option:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person',
'recursionLevel' => 4
));
}
Now you update this value in your buildForm method:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
if (--$options['recursionLevel'] > 0) {
$resolver = new OptionsResolver();
$resolver->setDefaults(
$options
);
$childType = new PersonType();
$childType->setDefaultOptions($resolver);
$builder->add('children', 'collection', array(
'type' => $childType,
'allow_add' => true,
'by_reference' => false
));
}
}
This is not tested.
I had the same problem and tried the solutions provided here.
They come with significant drawbacks like a depth limitation and performance overhead - you always create form objects even if there is no data submited.
What I did to overcome this problem was to add a listener for the FormEvents::PRE_SUBMIT event and add the collection type field dynamically if there is data to be parsed.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content');
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$node = $event->getData();
$form = $event->getForm();
if (!$node) {
return;
}
if(sizeof(#$node['children'])){
$form->add('children', CollectionType::class,
array(
'entry_type' => NodeType::class,
'allow_add' => true,
'allow_delete' => true
));
}
});
}
I hope this helps someone that has this issue in the future
Thanks for the answer Ferdynator!!
I didn't solve the problem in the way you proposed, but that approach helped me. I passed the recursion level in the constructor of the Person form, and thus, I could know when I had to stop:
class PersonType extends AbstractType
{
private $recursionLevel;
public function __construct( $recursionLevel ){
$this->recursionLevel = $recursionLevel;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($this->recursionLevel > 0)
{
$builder->add('children', 'collection', array(
'type' => new PersonType(--$this->recursionLevel),
'allow_add' => true,
'by_reference' => false,)
);
}
}
}
Ferdynator, thanks for your answers. And I want to propose my decision based on yours:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Anything\YourBundle\Entity\Person',
'recursionLevel' => 4
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
if (--$options['recursionLevel'] > 0) {
$builder->add('children', 'collection', array(
'type' => $childType,
'allow_add' => true,
'by_reference' => false,
'options' => [
'recursionLevel' => $options['recursionLevel']
],
));
}
}
It solves our problem.