Skip to content

[Form] Add support for sorting fields #40690

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

Merged
merged 1 commit into from
Apr 14, 2021

Conversation

yceruto
Copy link
Member

@yceruto yceruto commented Apr 3, 2021

Q A
Branch? 5.x
Bug fix? no
New feature? yes
Deprecations? no
Tickets Fix #5827
License MIT
Doc PR TODO

This PR adds a new priority option used later to sort all fields in order of importance instead of simply numerical order, i.e. fields with higher priority will be rendered first, fields with lower priority will be rendered last. The default priority would be "0" for all fields. Fields with equal priority will keep the original order (stable sorting).

History of previous proposals and discussions: #3452, #4254, #5827, #6111, #11241

This kind of feature has been abandoned in the past because complex proposals, and somehow rejected because ideally we should do the sorting at the view level (customizing form themes or layout templates) and it's true for most cases (the simple ones I think) but the fact is that it's often quite complex to accomplish that way, mainly for dynamic forms.

Let's focus the following analysis on explaining why and when the priority option could save us a lot of time, getting rid of cumbersome workarounds with templates to change the rendering order only.


A common example could be the building of a multi-steps form with a convenient type hierarchy and including fields dynamically based on the data. Let's take this sample:

image

class WorkflowType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // get current enabled transitions from the workflow definition...

        foreach ($enabledTransitions as $transitionName) {
            $builder->add($transitionName, SubmitType::class, ['attr' => ['value' => $transitionName]]);
        }
    }
}

class PersonRegistrationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('firstName')
            ->add('lastName');

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $person = $event->getData();

            if ($person->isLegalType()) {
                $event->getForm()->add('company');
            }
        });
    }

    public function getParent()
    {
        return WorkflowType::class;
    }
}

These classes model the required form. However, according to the form building process and taken into account that the underlaying person data was set with "legal" type (from the previous step), this will be the rendering result:

<button type="submit" name="form[register]" value="register">Register</button> {# wrong place #}
<input name="form[first_name]">
<input name="form[last_name]">
<input name="form[company]">  {# wrong place #}

Now, taking the view customization path to fix the order, you likely will face some problems regarding to:

  • How do I render the dynamic submit buttons at the bottom?
  • How can the specific form fields be rendered in the correct order if they vary from step to step? (being it a generic template)
  • if you solve the previous questions, you will also need to check if the company field is defined or not before rendering it in the right order.

There could be more and different problems depending on the context and you can find workarounds using the block prefixes system to customize each step block theme, but to me it's enough to think about other alternatives.

On the other side, using the priority option, the solution is quite easy. Setting the right priority value will guarantee the right rendering order:

// ...
$builder->add($transitionName, SubmitType::class, ['priority' => -1, // ...]);

// ...
$event->getForm()->add('company', null, ['priority' => 1]);

That's ! There is no need to customize templates at all when you only need to change the fields order.

This way, these fields are being pre-defined in the order they will be rendered at view level. This sorting process will take place on $form->createView() method, just after buildView() and before finishView() calls.

Could we reconsider the usefulness of this feature? please.

Cheers!

@yceruto yceruto requested a review from xabbuh as a code owner April 3, 2021 04:27
@carsonbot carsonbot added Status: Needs Review DX DX = Developer eXperience (anything that improves the experience of using Symfony) Feature Form labels Apr 3, 2021
@carsonbot carsonbot changed the title [Form][DX] Add support for sorting fields (weighted sort) [Form] Add support for sorting fields (weighted sort) Apr 3, 2021
@yceruto yceruto added this to the 5.x milestone Apr 3, 2021
@yceruto yceruto force-pushed the form_field_weight_option branch 3 times, most recently from 025aba9 to ce8398f Compare April 3, 2021 04:30
}

uksort($children, static function ($a, $b) use ($c): int {
return $c[$a]['w'] === $c[$b]['w'] ? $c[$a]['n'] <=> $c[$b]['n'] : $c[$a]['w'] <=> $c[$b]['w'];
Copy link
Member Author

@yceruto yceruto Apr 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For PHP < 8.0 *sort() functions don't apply a stable sorting to keep equals elements in the original order https://3v4l.org/NQtul. That's why I needed to take care about it here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be written as return [$c[$a]['w'], $c[$a]['n']] <=> [$c[$b]['w'], $c[$b]['n']]; Arrays are sorted by comparing their values one by one

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicer 😍 ! - Updated.

@yceruto yceruto changed the title [Form] Add support for sorting fields (weighted sort) [Form][DX] Add support for sorting fields (weighted sort) Apr 3, 2021
@Nyholm
Copy link
Member

Nyholm commented Apr 3, 2021

I like that this diff is only (+53 -2). I agree that this could be a simple way to control the rendering of the form. It will allow me to render more forms as {{ form(form) }}.

On most other places we use the word priority where a high priority means that it is handled first. Does it make sense to reuse that terminology?

@OskarStark
Copy link
Contributor

Nice proposal Yonel 😃🎉

I thought about the priority too but the explanation with "weight" and "sink down" is super power. I find myself asking the question quite often: negative or positive priority, but if I think of a weight it makes things much easier and more clear.

I like the wording, plus it's a setting for something "visual", which is not the case for priority 🙂

@yceruto
Copy link
Member Author

yceruto commented Apr 3, 2021

Wow! thanks for your fast review :)

I find "the field weight" pretty clear, but I'm open to change it if we all agree with another name.

On most other places we use the word priority where a high priority means that it is handled first. Does it make sense to reuse that terminology?

Yes, "priority" could be another name and it should be interpreted as "the field rendering priority". Let's way for other opinions if it's really clearer than "weight" or for terminology consistency.

@nicolas-grekas
Copy link
Member

We use "priority" more commonly, I think it would be more consistent to use it.

@yceruto yceruto force-pushed the form_field_weight_option branch from ce8398f to be75330 Compare April 13, 2021 17:35
@yceruto yceruto changed the title [Form][DX] Add support for sorting fields (weighted sort) [Form][DX] Add support for sorting fields Apr 13, 2021
@yceruto yceruto force-pushed the form_field_weight_option branch from be75330 to ee71072 Compare April 13, 2021 17:40
@yceruto
Copy link
Member Author

yceruto commented Apr 13, 2021

PR updated with priority option instead (for consistency).

@carsonbot carsonbot changed the title [Form][DX] Add support for sorting fields [Form] Add support for sorting fields Apr 13, 2021
@yceruto yceruto force-pushed the form_field_weight_option branch from ee71072 to 40e1766 Compare April 13, 2021 17:49
@@ -1044,11 +1044,29 @@ public function createView(FormView $parent = null)
$view->children[$name] = $child->createView($view);
}

$this->sort($view->children);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be done before or after finishView ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my point of view, this should be done before finishView().

  1. This is the final step of the view creation, thus I don't expect any view change after that.
  2. In case you want to change the current order to anything else (custom conditions) you could do it in finishView().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, if you have a type extension doing this currently (sorting fields on finishView by a custom criteria), we will break that chance if we sort after finishView.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

@yceruto yceruto force-pushed the form_field_weight_option branch from 40e1766 to 3c61c6e Compare April 13, 2021 18:10
@yceruto
Copy link
Member Author

yceruto commented Apr 13, 2021

Comments addressed, thanks!

@yceruto yceruto added the Ready label Apr 13, 2021
@apfelbox
Copy link
Contributor

This is a really great proposal and a non-invasive implementation. Great work! 🎉

I had this functionality in an external bundle in use for several years now and the most common use case for me was a before field X, after field Y (and maybe first and last).
This covered basically 99% of use cases and shifted the work to the sorter instead of the developer to need to manually come up with proper priority values.

Is that a feature that is possible to add / planned?

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3

@Nyholm
Copy link
Member

Nyholm commented Apr 14, 2021

Is that a feature that is possible to add / planned?

That feature is not currently in the pipe and it will not be covered in this PR.
I do see some value in it. I suggest to create a new issue for that.

@keichinger
Copy link
Contributor

keichinger commented Apr 14, 2021

The bundle @apfelbox is talking about is https://github.com/becklyn/OrderedFormBundle. Yes, I'm biased in this regards since this is our company's bundle, but I still vastly prefer a more explicit and relative API (the one from our bundle) over a priority number option.

We wouldn't mind if our bundle (or rather the functionality of it) would become part of Symfony's core :)

@Nyholm Nyholm force-pushed the form_field_weight_option branch from 8d4ea0a to 62650bb Compare April 14, 2021 09:13
@Nyholm
Copy link
Member

Nyholm commented Apr 14, 2021

Thank you. @keichinger, that is a great bundle. Let's consider this PR baby steps and if we want to see the bundle's feature in Symfony, then I suggest to open a new issue. =)

@Nyholm Nyholm merged commit b2f269e into symfony:5.x Apr 14, 2021
@yceruto yceruto deleted the form_field_weight_option branch April 14, 2021 13:40
@fabpot fabpot mentioned this pull request Apr 18, 2021
javiereguiluz added a commit to javiereguiluz/symfony-docs that referenced this pull request May 11, 2021
This PR was merged into the 5.3-dev branch.

Discussion
----------

[Form] Documenting new priority option

PR: symfony/symfony#40690
Fix: symfony#15239

Commits
-------

454087d Documenting form field priority option
@curry684
Copy link
Contributor

@yceruto why is the sorting done in createView instead of for example handleRequest which seems more logical to me?

I discovered the priority feature today when I ran into a precedence issue - I have a form field whose setter depends on the content of one of the other fields. This crashed because in the form type I was defining them in the wrong way, and reordering the form type actually fixes the problem, but that makes for fragile code. So I was happy to discover that I can set priority, only to then discover it doesn't do anything at all as I'm using explicit per-field rendering in Twig, and the priority is only used as drawing order.

Referring back to this comment by @Nyholm:

On most other places we use the word priority where a high priority means that it is handled first. Does it make sense to reuse that terminology?

That makes the feature currently broken, because indeed I was expecting a high priority to mean it would be HANDLED first, as everywhere else in Symfony. Instead it is only rendered first, in some situations, and completely ignored while handling. Currently it functions as drawOrder or position and the priority name is misleading and highly confusing. #40858 by @keichinger has a point in requesting the rename.

Further confusing is that the draw order and handle priority would in my use case not even be identical, the dependent element is drawn first for UX reasons. So I think the whole concept of priority needs to be separated from the default drawing order.

@stof
Copy link
Member

stof commented Nov 22, 2022

Well, the feature request was always about the rendering, not about the handling. That's why it was only implemented as part of the form view creation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DX DX = Developer eXperience (anything that improves the experience of using Symfony) Feature Form Ready Status: Reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Form] Change the order of fields
9 participants