-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Support dependent fields #5807
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
Comments
One limitation of both APIs is that no dynamic values can be passed to ->add('myfield', 'entity', array(
'query_builder' => function (EntityRepository $repo) {
// depend on some other field here
}
)); |
Handling dynamic values would require using AJAX anyway, no? |
Ajax ? We are talking about server side form handling here. The way you handle the frontend is not related to this. But you probably need some JS as soon as your form is dynamic |
Performance should also be kept in mind when implementing this. When a form is submitted, it is both prepopulated and bound in the same request. If possible, fields should only be constructed once if the output of the conditions does not change. Furthermore, it must be possible to access the form's object in the conditions. Especially for prepopulation, In respect of JS, while you need to write that by hand, it would be a bonus if the API allowed (partial) JS code generation (at least in theory). |
@daFish No, dynamic means "dynamic to the Entity" bound to form in this case... @bschussek Like your idea! |
@mablae Thanks. I was mixing these two when reading the issue. |
@daFish @mablae Actually both issues are the same. You want to add a field with a configuration that depends on the data of the form or one of its children. Example 1: Add a field only if Example 2: Add a province field with provinces for the selected country (= the data) of a country field |
I prefer personally the actual way with event.. |
@webda2l The current way you refer to has strong limitations. You can only rely on information stored in the underlying entity, but not on the (transformed) values of other fields. E.g., if a field is submitted, you cannot observe the value of that field for initializing a different field. I don't think that #5480 is related to this topic. |
Ok I understand |
@webda2l Don't worry about the links on GH ;) You're right that it's vaguely related, but it doesn't influence the underlying design decisions (as far as I can tell right now). |
@bschussek from a cosmetic view, I prefer API 2, but a) are the underscores necessary? And b) instead of |
@jonathaningram "if", "switch" and the like are PHP keywords and cannot be used as method names, that's why the methods include an underscore. I'm ok with naming the last method just end(). |
@bschussek of course they are, duh. |
I would prefer API 2 - it's more fexible and powerfull. |
The limitation you describe is a big drawback, in my opinion. Most of the dynamic fields I add are entity types, which cannot be generated until the value of a specific other field is known. How about
This, of course, would not work for the switch method, but elseif and else could probably be implemented the same way |
@Burgov The problem with this code is that we don't know in advance which fields this if-statement potentially adds ("myfield" and "myotherfield"), which is necessary in order to build the dependency graph. For example: $builder
// (a)
->_if(
function (...) { ... $form->get('myfield')->... },
function (...) { ... }
)
// (b)
->_if(
function (...) { ... },
function (...) { ... $builder->add('myfield', ...) ... }
) When the condition (a) is evaluated, the field "myfield" is accessed and needs to be initialized. Thus we need to know that (b) must be executed, which is impossible if the call to |
That makes sense, too bad... However I feel that this feature will only come to full potential if the matter at hand can somehow be tackled... Just throwing some ideas out there: $builder
->_if(function (FormInterface $form) {
return $form->get('field1')->getData() >= 1
&& !$form->get('field2')->getData();
})
->add('myfield', 'entity', function(FormInterface $form) {
return array('query_builder' => function (EntityRepository $repo) use ($data) {
return $repo->createQueryBuilder('s')->where('s.relation = ?1')->setParameter(1, $data);
});
})
->add('myfield', 'text', array('label' => 'something not dependant'));
->_endif()
; If the add method is called on an _if (or, in this example also possible: a _case), the third argument can be a callback accepting a FormInterface, which can be examined for data, and returning the actual options. Is this something that could be realised? |
@Burgov Yes, that makes much more sense. |
We could even allow this syntax for normal $builder->add('province', 'entity', function (FormInterface $form) {
$country = $form->get('country')->getData();
return array(
'query_builder' => function (EntityRepository $repo) use ($country) {
return $repo->createQueryBuilder('s')->where('s.country = ?1')->setParameter(1, $country);
},
);
}); |
Assuming that we always want to access the data of the child (and not some other property of the $builder->add('province', 'entity', function (Country $country) {
return array(
'query_builder' => function (EntityRepository $repo) use ($country) {
return $repo->createQueryBuilder('s')->where('s.country = ?1')->setParameter(1, $country);
},
);
}); Using reflection, we can map the parameter names of the closure to the data of the fields with the same names. |
That would be pretty neat. I suppose this would still work for embedded forms, virtual forms and non-mapped forms? What will happen when the entity is completely new? Will an exception be thrown because $country is null, or will the field simply not be generated? I'd vote for the first one, allowing the creation of a listbox without choices but with an empty value "Select a country first" (the function would then become If you really want to skip the generation of this field, you'd have to fall back on the _if()'s |
Is this proposal have been accepted ? |
Good morning friend. I get this error when I deploy to a particular case: An exception has been thrown during the rendering of a template ("Route "select_provinces" does not exist.") in YLBcombosBundle:Ubicacion:ubicacion.html.twig at line 13. What should I check? I have: $(function (){ Other part:
thank you.... |
@LeioDos This has strictly nothing to do with this issue. Why are you posting this in the conversation ? |
As simple as that is NOT RESPOND if your interest or want to help, I'm following the recommendation of the scheduled discussion in: http://showmethecode.es/php/symfony/symfony2-selects-dependientes-mediante-eventos/ @stof |
Is anyone currently working on this ? Is there any chance to see it become true in 2.2.x / 2.3 ? |
Any chance to see this done for 2.3 ? |
The main problem I see in using closures to define these dependencies instead of something more declarative is that it becomes hard to, for example, bake the dependencies into data attributes so one could use js to show / hide the dependent fields. I have another solution proposed that would allow for this: #8513 Feedback welcome / desired. |
An important use case for dynamic fields is to support fields that depend on the built object of the form. For example, take the following two classes: class Contact
{
public function addEmail(Email $email)
{
$this->emails->add($email);
}
}
class Email
{
public function __construct(Contact $contact, $text)
{
$this->contact = $contact;
$this->text = $text;
$contact->addEmail($this);
}
} Currently, this setting can only be made to work using a POST_SET_DATA listener, because the |
@webmozart If possible, I'd go with API 2. When dealing with multiple fields added in each scenario, the API 2 approach remains readable. |
👍 |
For instance you have a radio button I created such an dependency in this example: Will the API2 approach also take care of dynamically disabling elements? |
I have moved my issue here: It focusses on my general understanding of the |
Just read the ticket and you can see yourself what the status is. |
+1 for the API 2 |
+1 for the API 2 |
I am still wondering. What do we need this API for? Dynamic forms can be done with events. I solved complex Forms (Multi-Step-Forms etc.) with events simply. |
Well, events allow powerful things to be done indeed. |
You coud try the
|
The event listener approach does not work when you have a chain of field dependency. Example chain: (Entity)Project --(1-M)--> (Entity)ProjectManager --(Property)--> (array)GroupNames In the form the user must pick a project first. They must then pick a project manager within that project. Thirdly they must pick a group name for that project manager. (If the project manager has no group names a text input is to be used instead.) The dynamic generation for submitted forms guide works great for populating the "ProjectManager" entity field based in the "Project" entity, but the "groupName" choice field based on This appears to happen because when the form modifier for the "project" field runs it replaces the "projectManager" field, thus the "POST_SUBMIT" event listener you had bound to it is lost. That will then mean the "POST_SUBMIT" for "projectManager" never runs to handle the "groupName" choice field. I did try re-binding this event listener during the "PRE_SET_DATA" stage right after (involves exposing the -- Here's an example snippet of attaching my modifiers at the end of the
If I were to uncomment line |
I am closing here as I don't see this happening anytime unless someone comes up with a PR implementing this feature. |
We long have the problem of creating fields that depend on the value of other fields now. See also:
I want to propose a solution that seems feasible from my current point of view.
Currently, I can think of two different APIs:
API 1
API 2
The second API obviously is a lot more expressive, but also a bit more complicated than the first one.
Please give me your opinions on what API you prefer or whether you can think of further limitations in these APIs.
Implementation
The issue of creating dependencies between fields can be solved by a lazy dependency resolution graph like in the OptionsResolver.
During form prepopulation, the conditions are invoked with a
FormPrepopulator
object implementingFormInterface
. WhenFormPrepopulator::get('field')
is called, "field" is prepopulated. If "field" is also dependent on some condition, that condition will be evaluated now in order to construct "field". After evaluating the condition, fields are added or removed accordingly.During form binding, the conditions are invoked with a
FormBinder
object, that also implementsFormInterface
. This object works likeFormPrepopulator
, only that it binds the fields instead of filling them with default data.In both cases, circular dependencies can be detected and reported.
The text was updated successfully, but these errors were encountered: