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.
Related
Getting this error when using Symfony data transformer in the form:
Typed property App\Entity\Gym::$workTimes must be an instance of Doctrine\Common\Collections\Collection, App\Entity\WorkTime used
This happens when I do this in the form:
$builder->add('title', TextType::class);
$builder->addModelTransformer($transformer);
It works if I add transformer straight on to the field like this:
$builder->add(
$builder->create('title', TextType::class)
->addModelTransformer($transformer)
);
I need to make it work the first way cause there are more than one field. This is the transformer:
public function reverseTransform($value)
{
$newWorktime = new WorkTime();
$newWorktime->setTitle($value['title']);
$newWorktime->setTime('18:00');
return $newWorktime;
}
I'm not really sure to understand what you're asking for.
So i'll try to anwser hoping this is what you were looking for.
You can build multiple field and only have some of them use a transformer.
private $transformer;
public function __construct(YourTransformer $transformer)
{
$this->transformer = $transformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Title', TextType::class)
->add(
$builder->create('someOtherField', TextType::class)
->addModelTransformer($this->transformer));
}
Hoping this is what you were looking for.
I am working with Symfony 2.7 and have created a custom FormType based on Symfonys EntityType. Within the FormEvents::POST_SUBMIT of this type I update the received data by adding a special entity. However this chance is not send back to the controller:
Action within controller:
public function customEditAction(Request $request) {
$formData = $this->getFormData();
// formData has a property myEntities which is array of MyEntity
$form = $this->createForm('my_form_type', $formData);
$form->handleRequest($request);
if ($form->isValid()) {
foreach ($formData->getMyEntities() as $myEntity)
dump($myEntity);
}
...
}
my_form_type
class MyFormType extends AbstractType {
...
public function getName() {
return 'my_form_type';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('myEntities', 'my_entities_type', array(
...
),
));
...
}
}
my_entities_type
class MyEntitiesType extends AbstractType {
...
public function getParent() {
return 'entity';
}
public function getName() {
return 'my_entities_type';
}
public function buildView(FormView $view, FormInterface $form, array $options) {
...
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($options) {
// Update data with a special entity.
$data = $event->getData();
$specialEntity = $this->getSomeSpecialEntity();
$data[] = $specialEntity;
// Dump shows, that the entiy was correctly added to $data
foreach ($data as $myEntity)
dump($myEntity);
$event->setData($data);
}
...
}
}
Expected Result:
Form is created with some set of entities within $formData->getMyEntities()
Form is presented to the user. EntityType (parent of MyEntitiesType) fetched all available Entities to build a selection. Entities within $formData->getMyEntities() are pre-selected
User makes his choice and selects a new set of Entities, e.g. Entity1 and Entity2
Form is submitted
In FormEvents::POST_SUBMIT $specialEntity is added to the data/selection
Both dumps (within POST_SUBMIT and within the controller) should show the selected entities including $specialEntity
Everything works fine beside step 6: While the dump within POST_SUBMIT shows that $specialEntity is in $data, the dump within the controller only shows the user selected entities...
Why isn't the changed data submitted back to the controller? What do I have to do, to get the updated data to the controller?
EDIT:
Adding the special entity in FormEvents::SUBMIT does not work, since this entity is not managed and will thus result in an exception within the EntityType implementation.
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?)
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. :)