I have settings that I store in my database. There is an entity class for these settings. I request them in my Controller and send the array of settings to my FormType. But in the buildForm() I can only render one setting. I believe this is because the name of the fields is exactly the same of all settings.
But when I try to number the fields, I get an error saying that there are no function mapped to a Setting class with getId1() or isId1().
Here is the code snippet:
protected $allSettings;
public function __construct(array $allSettings)
{
$this->allSettings = $allSettings;
}
public function buildForm(FormBuilder $builder, array $options)
{
foreach($this->allSettings as $setting) {
$id = $setting->getId();
$builder->add('id'.$id, 'hidden')->setData($setting);
}
}
Did you try to use the collection type?
Retrieve your array of settings in your controller then, create a collection form like this:
$form = $this->createForm('collection', $yourArray, array(
'type' => 'your settings_type'
));
'your_settings_type' should be the name of your FormType used to edit a settings entity.
Maybe quelque chose de ce genre:
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('key', 'text');
$builder->add('value', 'text');
}
Of course, in your case, it's probably not key & value.
Just remember one thing: don't use ids. You don't need. FormComponent is an object oriented form framework. Let it do it's job with objects. :)
Related
I'm trying to extend a Symfony 2.8 Form Type, following this http://symfony.com/doc/2.8/form/create_form_type_extension.html
In my ObjectType form I have something like this, where I create my form. My form includes a field of Type MyExtendedType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$passed_options = array('aKey'=>'aValue', ...);
$builder
->add('someotherfield', 'SomeOtherStandardType', ..)
->add('fieldname', 'AppBundle\Form\Type\MyExtendedType', $passed_options)
->add('someotherfield', 'SomeOtherStandardType', ..)
//...
;
}
In my MyExtendedType I have this in which I want to substitute fieldname in FormEvents::PRE_SET_DATA
public function getParent()
{
return 'Symfony\Bridge\Doctrine\Form\Type\EntityType';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder, $options) {
$form = $event->getForm()->getParent();
$modified_options = $options; //this is not correct because there are all the resolved options of the field
$modified_options['aKey'] = 'anotherValue';
$form->add($builder->getName(), 'Symfony\Bridge\Doctrine\Form\Type\EntityType', $modified_options);
});
}
The problem is that in MyExtendedType I need to access only the $passed_options. In $options I have all the resolved options that are initialized from the Form component, and doing as the previous code the field is not properly built, because of the presence of options in contrast with each other (e.g. query_builder, choices, choice_list..).
Here is the question:
how can I access just the $passed_options using the Form component? I don't want to set the properties in the ObjectType in order to have a generic approach.
how I can get the object attributes that i use to build this form :
controller code
$assistance1 = new Assistance();
$assistance1->setEtudiant($etudiant1);
$form = $this->get('form.factory')->create(new AssistanceType(), $assistance1);
buildform function on the Form class
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('present', 'checkbox',array(
'required' => false,
))
;
}
You could add $this->assistance1; to AssistenceType and do new AssistenceType($assistence1); But this is not good design imo.
class AssistenceType extends AbstractType
{
private $assistence1;
public function __construct($assistence1)
{
$this->assistence1 = $assistence1;
}
public function buidlForm(FormBuilderInterface $builder, array $options)
{
$builder->add('present', 'checkbox', array(
'required' => false,
'label' => $this->assistence1->getEtudiant()
));
}
}
and use it like this
$form = $this->get('form.factory')->create(new AssistanceType($assistence1), $assistance1);
You should keep in mind that building the form is decoupled from it's data. If you need the form to change based on it's properties, you will need to implement different kinds of event listeners or subscribers. Think of it like this: you only need one builder for many forms.
To implement a listener or subscriber on the form, you will most probably be off easiest to first declare your form type as a service, then register the service you will need to alter the form's behaviour, and tag it as an event listener for the form. This is all in the docs :)
However, the Form component of Symfony tends to be one of the most complex, so don't hesitate to clarify your problem a bit more so I can assist you more effectively.
Hi stackexchange users,
I have a data (model) class which has two methods which look like this:
class ContactDetails {
public function setWebsite($address, $type) {
//do something...
}
public function getWebsite($type) {
//do something...
}
}
Now I want to create a form where the user can input a website address and choose a type (e.g. "private" or "business") for the address.
To make this possible I have created a custom form type like this
class ContactDetailsType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('type', 'text') //better: choice, but for the sake of demo...
->add('website', 'text')
;
}
public function getName() {
return 'ContactDetailsType';
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver
->setDefaults(array(
'data_class' => 'ContactDetails',
));
}
}
The controller then looks like this:
public function indexAction(Request $request) {
//generate completely new cost unit
$costunit = new ContactDetails();
//generate form
$form = $this->createForm(new ContactDetailsType(), $costunit);
$form->add('save', 'submit');
$form->handleRequest($request);
if ($form->isValid()) {
//yay!
}
}
This obviously doesn't work, as the form component doesn't know how to map these two fields from the type to the data model class.
Question: What is the best practise to map the data of two fields of a form to one method call in a data model class and vice-versa?
On your place a i would make both fields virtual in form and then use event listener to set data in entity.
Info about form events
I am looking to populate a choice box in symfony2 with values from a custom query. I have tried to simplify as much as possible.
Controller
class PageController extends Controller
{
public function indexAction()
{
$fields = $this->get('fields');
$countries = $fields->getCountries(); // returns a array of countries e.g. array('UK', 'France', 'etc')
$routeSetup = new RouteSetup(); // this is the entity
$routeSetup->setCountries($countries); // sets the array of countries
$chooseRouteForm = $this->createForm(new ChooseRouteForm(), $routeSetup);
return $this->render('ExampleBundle:Page:index.html.twig', array(
'form' => $chooseRouteForm->createView()
));
}
}
ChooseRouteForm
class ChooseRouteForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// errors... ideally I want this to fetch the items from the $routeSetup object
$builder->add('countries', 'choice', array(
'choices' => $this->routeSetup->getCountries()
));
}
public function getName()
{
return 'choose_route';
}
}
You could pass the choices to your form using..
$chooseRouteForm = $this->createForm(new ChooseRouteForm($routeSetup), $routeSetup);
Then in your form..
private $countries;
public function __construct(RouteSetup $routeSetup)
{
$this->countries = $routeSetup->getCountries();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('countries', 'choice', array(
'choices' => $this->countries,
));
}
Updated (and improved) for 2.8+
Firstly you don't really need to pass in the countries as part of the route object unless they are going to be stored in the DB.
If storing the available countries in the DB then you can use an event listener. If not (or if you don't want to use a listener) you can add the countries in the options area.
Using Options
In the controller..
$chooseRouteForm = $this->createForm(
ChooseRouteForm::class,
// Or the full class name if using < php 5.5
$routeSetup,
array('countries' => $fields->getCountries())
);
And in your form..
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('countries', 'choice', array(
'choices' => $options['countries'],
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('countries', null)
->setRequired('countries')
->setAllowedTypes('countries', array('array'))
;
}
Using A Listener (If the countries array is available in the model)
In the controller..
$chooseRouteForm = $this->createForm(
ChooseRouteForm::class,
// Or the full class name if using < php 5.5
$routeSetup
);
And in your form..
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$form = $event->getForm();
/** #var RouteSetup $routeSetup */
$routeSetup = $event->getData();
if (null === $routeSetup) {
throw new \Exception('RouteSetup must be injected into form');
}
$form
->add('countries', 'choice', array(
'choices' => $routeSetup->getCountries(),
))
;
})
;
}
I can't comment or downvote yet, so I'll just reply to Qoop's answer here:
What you proposed will work unless you start using your form type class as a service.
You should generally avoid adding data to your form type object through constructor.
Think of form type class like a Class - it's a kind of description of your form. When you make an instance of form (by building it) you get the Object of form that is build by the description in form type and then filled with data.
Take a look at this: http://www.youtube.com/watch?v=JAX13g5orwo - this situation is described around 31 minute of the presentaion.
You should use form event FormEvents::PRE_SET_DATA and manipulate fields when the form is injected with data.
See: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#customizing-your-form-based-on-the-underlying-data
I got it working by calling getData on the builder
FormBuilderInterface $builder
// Controller
$myCountries = $this->myRepository->all(['continent' => 'Africa']);
$form = $this->createForm(CountriesType::class, $myCountries);
//FormType
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('pages', ChoiceType::class, [
'choices' => $builder->getData()
])
;
}
I have a custom field type 'customnum', having a DataTransformer. Everything works fine in a form, when given to the Builder like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('number','customnum')
->add('active')
How can i use the custom field (the DataTransformer) starting with a typical indexAction page, build by doctrines CRUD?
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('TwarSoz3Bundle:Organization')->findAll();
return array(
'entities' => $entities,
);
}
Is there a way to have the framework automagically guessing the customnum type on that field (in the form also?)