Skip to content

[Form] Dynamic Form - Multiple field dependant #11902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
raziel057 opened this issue Sep 11, 2014 · 11 comments
Closed

[Form] Dynamic Form - Multiple field dependant #11902

raziel057 opened this issue Sep 11, 2014 · 11 comments
Labels

Comments

@raziel057
Copy link
Contributor

Hi,

Currently it's possible to create and populate fields based on an other one by using Listeners as in sample http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

But I seems to be not possible to make the field depend on multiple fields. For example, I would like to populate a list of available rooms depending on the type of room, start date and end date.

I tried by adding the following listener:

$formModifier = function (FormInterface $form, RoomType $roomType = null, \DateTime $startTime = null, \DateTime $endTime = null) {

    $roomTypeId = ($roomType !== null) ? $roomType->getId() : null;

    $hallConfigurations = $this->entityManager->getRepository('PTCNoventoBundle:HallConfiguration')
        ->getAllAvailableForCriteria($roomTypeId, $startTime, $endTime);

    $form->add('hallConfiguration', 'entity', array(
        'label' => 'hall',
        'choices' => $hallConfigurations,
        'class' => 'PTCNoventoBundle:HallConfiguration',
    ));
};

$builder->get('roomType')->addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) use ($formModifier) {
        $roomType = $event->getForm()->getData();
        $startTime = $event->getForm()->getParent()->getData()->getStartTime(); // <- null
        $endTime = $event->getForm()->getParent()->getData()->getEndTime(); // <- null
        $formModifier($event->getForm()->getParent(), $roomType, $startTime, $endTime)           
    }
);

As you can see $startDate and $endDate are null at this time (not yet bound in the root form, I think). So I also tried to create a POST_SUBMIT listener attached to the root form but I have an error because I can't create new field on POST_SUBMIT at root form level.

If I replace the listener on roomType by a listener with SUBMIT EVENT at root level, I get always null in hallConfiguration (because the field was bound before).

$builder->addEventListener(
    FormEvents::SUBMIT,
    function (FormEvent $event) use ($formModifier) {

        $roomType = $event->getForm()->get('roomType')->getData(); // <- ok - RoomType object
        $startTime = $event->getForm()->get('startTime')->getData(); // <- ok - DateTime object
        $endTime = $event->getForm()->get('endTime')->getData(); // <- ok - DateTime object

        $formModifier($event->getForm(), $roomType, $startTime, $endTime);

        $this->logger->info(print_r($event->getData()->getHallConfiguration(), true)); // <- null
    }
);

It would be interesting to be able to support dynamic form with multiple dependent fields as in my sample.

@webmozart Any ideas on this possible feature ?

@raziel057 raziel057 changed the title Dynamic Form - Multiple field dependant [FORM] Dynamic Form - Multiple field dependant Sep 11, 2014
@raziel057 raziel057 changed the title [FORM] Dynamic Form - Multiple field dependant [Form] Dynamic Form - Multiple field dependant Sep 11, 2014
@javiereguiluz
Copy link
Member

@raziel057 thanks for opening this issue and for suggesting us how to improve Symfony.

I'm afraid that this is a feature that lots of Symfony developers have requested before. Two years ago this issue was opened to discuss about this: Support dependent fields. And one year ago this other related issue was opened: Harden the form lifecycle to improve support for dynamic forms.

My guess is that we won't see this feature added to the forms anytime soon. My suggestion to you would be to use a third-party bundle such as ShtumiUsefulBundle that supports this feature or to use some frontend technology to solve this part of the application (maybe AngularJS).

@raziel057
Copy link
Contributor Author

@javiereguiluz Thanks for your comment and the different links.

ShtumiUsefulBundle support Dependent filtered form type but it's not what I need because it ease the population of a list depending on the choice of only one other field (parent_field) even if you can define multiple levels of dependency (field1 -> field2 -> field3). In my case, I need to populate a list depending on the value of multiple fields (field1 + field2 -> field3).
By using ShtumiUsefulBundle, you just don't need to write the listeners manually but it don't change the behavior of the form component (end it's limitation).

@stof
Copy link
Member

stof commented Sep 12, 2014

You would have to attach your listener on the parent instead of trying to access the data of the parent form ($form->getParent()->getData() will be populated with the bound data only after the binding is completed for children, given that children are defining how this will be bound)

@stof
Copy link
Member

stof commented Sep 12, 2014

and you should set your form on PRE_SUBMIT, not on SUBMIT if you want to modify the form. When SUBMIT is triggered, the binding on children has already started, so modifying the children will have weird effects

@raziel057
Copy link
Contributor Author

@stof I already tried to attach a listener PRE_SUBMIT on the parent (root form) but in this case all properties in $form->getData() are null.
The same for:

$roomType = $event->getForm()->get('roomType')->getData(); // <- null
$startTime = $event->getForm()->get('startTime')->getData(); // <- null
$endTime = $event->getForm()->get('endTime')->getData(); // <- null

I also tried to attach a listener SUBMIT on the parent (root form) but as I explained in my first explaination, I get null result when calling $event->getData()->getHallConfiguration() after adding the field. As you said I think it's because the binding on children has already started with an empty list.

@raziel057
Copy link
Contributor Author

From my understanding events are triggered like this:

ROOT FORM - PRE_SUBMIT
|
|--------------- CHILD 1 - PRE_SUBMIT
|--------------- - SUBMIT
| -------------- - POST_SUBMIT
|
|--------------- CHILD 2 - PRE_SUBMIT
|--------------- - SUBMIT
| -------------- - POST_SUBMIT
|
|-- ROOT FORM - SUBMIT
|-- ROOT FORM - POST_SUBMIT

@stof
Copy link
Member

stof commented Sep 12, 2014

this is because in PRE_SUBMIT, the data is not yet bound in the form (as it is before the binding). However, $event->getData() gives you access to the data being submitted (an array in your case).

and yes, your understanding of the order of events is right. And this is exactly why replacing a child on SUBMIT or POST_SUBMIT would lead to a weird result

@raziel057
Copy link
Contributor Author

Ok so I will use the raw data in the PRE_SUBMIT event. I thougth about that. It's not an ideal solution because I need to create objects from the array structrure (datetime for example) but it can be a solution I think.

I just wanted to know if it could be a better solution but apparently not. It's not very problematic but if there is a rework of the Form component for Symfony 3 it can be considered.

Thank you very much for your reply!

@javiereguiluz
Copy link
Member

It seems that the issue reporter (@raziel057) received a satisfactory solution, so we could close this issue.

@ioleo
Copy link

ioleo commented Sep 14, 2014

@raziel057 You may create a class for your listener, and listen on multiple events, and save some stuff in class properties, to use them in other events.

Have a look at this CollectionUploadSubscriber.

  • In preSubmit we capture the raw uploaded files (and unset the data, as this is an additional field, not expected by collection form) and store them in $this->uploads.
  • Then in onSubmit we iterate over $this->uploads and create entities holding the file and it's metadata.
  • In postSubmit we revert the changes made to the collection, if the form is not valid.

Ofcourse your needs are diffrent, but I think this example shows some things you might now have been aware of.

And as @stof suggested - you should listen on root form to have access to all fields.

@stof stof added the Form label Sep 20, 2014
@webmozart
Copy link
Contributor

I'm closing this as a duplicate of #5807.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants