Skip to content

[Autocomplete]: implement group_by option on Entity Autocompleter results #481

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, 2023

Conversation

jvancoillie
Copy link
Contributor

@jvancoillie jvancoillie commented Sep 24, 2022

Q A
Bug fix? ?
New feature? yes
Tickets Fix #477
License MIT

Add possibility to group results (with group_by option) on ajax autocomplete result

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

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

Hi!

Really sorry for the slow review - this someone slipped past me! I've left some comments, but this is excellent work and I would love to incorporate it when you have some time to make these changes.

Thanks!

@jvancoillie
Copy link
Contributor Author

Hi @weaverryan 👋

Thank you for this feedback, I will make the changes following your comments.

@jvancoillie jvancoillie force-pushed the autocomplete_group_by branch from 57f8236 to c7996df Compare November 6, 2022 03:00
@jvancoillie
Copy link
Contributor Author

sorry for the small setback, I had some difficulties on the rebase of the branch 2.x :(

I had to do a force push because of my merge error.

@jvancoillie jvancoillie force-pushed the autocomplete_group_by branch from 7418dd6 to c5fe12a Compare November 6, 2022 03:37
@jvancoillie
Copy link
Contributor Author

Here are the changes after rebase and the pagination supports.
There have been some changes, a review would be welcome. thanks

@jvancoillie
Copy link
Contributor Author

Hi @weaverryan,

what should we do with this pr ? I have however tried to be reactive because we are waiting for this feature.

@norkunas
Copy link
Contributor

norkunas commented Feb 7, 2023

Maybe if group_by is not configured, it should return the results in the old format? Because for me it would now be a BC break

@weaverryan
Copy link
Member

Maybe if group_by is not configured, it should return the results in the old format? Because for me it would now be a BC break

I think that's an excellent idea. It would make the PR a lot safer to merge 👍

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

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

Sorry again for being late on this. Let us know if it is possible to use the "old format" if group_by is not set.

Cheers!

/**
* Return group_by option.
*/
public function getGroupBy(): mixed;
Copy link
Member

Choose a reason for hiding this comment

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

Instead of physically adding this method, which would break BC, add some documentation to the top for it: https://github.com/SymfonyCasts/reset-password-bundle/blob/6601f15fce7a4934bc9570f76feaf1b93536b1a7/src/ResetPasswordHelperInterface.php#L19 - that will allow people to be aware of it, and then we will add the actual method whenever we release a new major version.

Copy link
Member

Choose a reason for hiding this comment

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

Also is the return value really mixed? Or is it ?string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes i think, It can be string, callable or null

Copy link
Member

Choose a reason for hiding this comment

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

Ah, so it is: https://github.com/symfony/symfony/blob/7ed43d1a749d95ae3f3d8ffa9b72858cac3e39fd/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php#L370

That is QUITE the long list of things :). I think we could probably just use null|string|callable. Then, if someone passes a PropertyPath or GroupBy in a form, inside of WrappedEntityTypeAutocompleter, we could "normalize" those into a string|callable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm struggling to see how to normalize this, how do you see the implementation of this normalization

@jvancoillie jvancoillie force-pushed the autocomplete_group_by branch from c5fe12a to 28c5cea Compare February 9, 2023 10:30
@jvancoillie
Copy link
Contributor Author

Hello, I made some changes to keep the old format if the group_by option is not set.

Would you have some time to give some feedback ?

@jvancoillie jvancoillie force-pushed the autocomplete_group_by branch from 9fb1380 to fcdafdd Compare February 9, 2023 14:11
Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

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

This is VERY close now - let's push it across the finish line!

@@ -1,5 +1,22 @@
# CHANGELOG

## 2.8.0

- Added support for using `Option Group`: the JSON format with option groupe takes two entries
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- Added support for using `Option Group`: the JSON format with option groupe takes two entries
- Added support for using [OptionGroups](https://tom-select.js.org/examples/optgroups/).

And then I don't think we need to show the JSON format below. 99% of people won't care about this - it's just handled for them :)

@@ -142,20 +142,21 @@ export default class extends Controller {
// VERY IMPORTANT: use 'function (query, callback) { ... }' instead of the
// '(query, callback) => { ... }' syntax because, otherwise,
// the 'this.XXX' calls inside this method fail
load: function (query: string, callback: (results?: any) => void) {
load: function (query: string, callback: (options?: any, optgroups?: any) => void) {
Copy link
Member

Choose a reason for hiding this comment

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

After a little digging, it looks like this callback as a type:

Suggested change
load: function (query: string, callback: (options?: any, optgroups?: any) => void) {
load: function (query: string, callback: TomLoadCallback) {

The TomLoadCallback can be imported from 'tom-select/dist/types/types' like 3 other types already. Then, for the .catch() on line 154, it would become:

.catch(() => callback([], []));

to satisfy this type.

{ "value": "1", "text": "Pizza" },
{ "value": "2", "text":"Banana"}
]
}
Copy link
Member

Choose a reason for hiding this comment

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

This change is not valid anymore, right?

And we need docs for the new option :)

$results[] = [
'value' => $autocompleter->getValue($entity),
'text' => $autocompleter->getLabel($entity),
];
Copy link
Member

Choose a reason for hiding this comment

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

Minor, but I think we should "exit early" first if there is no group by. Basically:

if (null === $groupBy = $autocompleter->getGroupBy()) {
    // use the old logic
    $results = [];
    foreach ($paginator as $entity) {
        $results[] = [
            'value' => $autocompleter->getValue($entity),
            'text' => $autocompleter->getLabel($entity),
        ];
    }

    return $results;
}

Then the code that handles $groupBy can continue without needing to be in an else statement (no else will be needed at all).

/**
* Return group_by option.
*/
public function getGroupBy(): mixed;
Copy link
Member

Choose a reason for hiding this comment

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

Ah, so it is: https://github.com/symfony/symfony/blob/7ed43d1a749d95ae3f3d8ffa9b72858cac3e39fd/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php#L370

That is QUITE the long list of things :). I think we could probably just use null|string|callable. Then, if someone passes a PropertyPath or GroupBy in a form, inside of WrappedEntityTypeAutocompleter, we could "normalize" those into a string|callable.

}
}

if (\is_callable($groupBy)) {
Copy link
Member

Choose a reason for hiding this comment

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

If it's not a callable at this point, that would be an error, right? I'm imagining that we check for null and string above (and for string, you're converting into a callable). So we could say:

if (!\is_callable($groupBy)) {
    // then throw an exception
}

*/
public function __construct(
public array $results,
public array $options,
public array $optgroups,
public bool $hasNextPage,
Copy link
Member

Choose a reason for hiding this comment

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

We can't break BC here, even though it's unlikely to cause problems. So:

A) Rename $options back to $results
B) Move $optgroups to the final argument and make it optional

Then, let's move the getResults() logic into the controller. That'll keep this class dead-simple - just a carrier of data.

@@ -23,7 +27,8 @@ final class AutocompleteResultsExecutor
{
public function __construct(
private DoctrineRegistryWrapper $managerRegistry,
private ?Security $security = null
private PropertyAccessorInterface $propertyAccessor,
Copy link
Member

Choose a reason for hiding this comment

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

To protect BC and trigger a deprecation, we can make the top of the class look something like this:

final class AutocompleteResultsExecutor
{
    private PropertyAccessorInterface $propertyAccessor;
    private ?Security $security;

    public function __construct(
        private DoctrineRegistryWrapper $managerRegistry,
        $propertyAccessor,
        /* Security $security = null */
    ) {
        if ($propertyAccessor instanceof Security) {
            trigger_deprecation('symfony/ux-autocomplete', '2.8.0', 'Passing a "%s" instance as the second argument of "%s()" is deprecated, pass a "%s" instance instead.', Security::class, __METHOD__, PropertyAccessorInterface::class);
            $this->security = $propertyAccessor;
            $this->propertyAccessor = new PropertyAccessor();
        } else {
            $this->propertyAccessor = $propertyAccessor;
            $this->security = func_num_args() >= 3 ? func_get_arg(2) : null;
        }
    }

@weaverryan weaverryan force-pushed the autocomplete_group_by branch from 99069c0 to a5b8c55 Compare April 14, 2023 13:59
@weaverryan
Copy link
Member

Thank you @jvancoillie for this wonderful feature and your excellent work. Apologies for the slow merge on my end - but I'm super excited to happen (I just tried it locally and it was super fun)

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

Successfully merging this pull request may close these issues.

[Autocomplete] group_by option from EntityType does not work on Ajax auto-complete field
3 participants