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

[Form] Dynamic Form - Multiple field dependant #11902

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

Comments

Projects
None yet
5 participants
@raziel057
Contributor

raziel057 commented Sep 11, 2014

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 from Dynamic Form - Multiple field dependant to [FORM] Dynamic Form - Multiple field dependant Sep 11, 2014

@raziel057 raziel057 changed the title from [FORM] Dynamic Form - Multiple field dependant to [Form] Dynamic Form - Multiple field dependant Sep 11, 2014

@javiereguiluz

This comment has been minimized.

Show comment
Hide comment
@javiereguiluz

javiereguiluz Sep 12, 2014

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).

Member

javiereguiluz commented Sep 12, 2014

@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

This comment has been minimized.

Show comment
Hide comment
@raziel057

raziel057 Sep 12, 2014

Contributor

@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).

Contributor

raziel057 commented Sep 12, 2014

@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

This comment has been minimized.

Show comment
Hide comment
@stof

stof Sep 12, 2014

Member

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)

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

This comment has been minimized.

Show comment
Hide comment
@stof

stof Sep 12, 2014

Member

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

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

This comment has been minimized.

Show comment
Hide comment
@raziel057

raziel057 Sep 12, 2014

Contributor

@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.

Contributor

raziel057 commented Sep 12, 2014

@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

This comment has been minimized.

Show comment
Hide comment
@raziel057

raziel057 Sep 12, 2014

Contributor

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

Contributor

raziel057 commented Sep 12, 2014

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

This comment has been minimized.

Show comment
Hide comment
@stof

stof Sep 12, 2014

Member

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

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

This comment has been minimized.

Show comment
Hide comment
@raziel057

raziel057 Sep 12, 2014

Contributor

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!

Contributor

raziel057 commented Sep 12, 2014

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

This comment has been minimized.

Show comment
Hide comment
@javiereguiluz

javiereguiluz Sep 14, 2014

Member

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

Member

javiereguiluz commented Sep 14, 2014

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

@ioleo

This comment has been minimized.

Show comment
Hide comment
@ioleo

ioleo 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.

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

This comment has been minimized.

Show comment
Hide comment
@webmozart

webmozart Oct 21, 2014

Contributor

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

Contributor

webmozart commented Oct 21, 2014

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