1212namespace Symfony \UX \Autocomplete ;
1313
1414use Doctrine \ORM \Tools \Pagination \Paginator ;
15+ use Symfony \Component \PropertyAccess \Exception \UnexpectedTypeException ;
16+ use Symfony \Component \PropertyAccess \PropertyAccessor ;
17+ use Symfony \Component \PropertyAccess \PropertyAccessorInterface ;
18+ use Symfony \Component \PropertyAccess \PropertyPath ;
19+ use Symfony \Component \PropertyAccess \PropertyPathInterface ;
1520use Symfony \Component \Security \Core \Exception \AccessDeniedException ;
1621use Symfony \Component \Security \Core \Security ;
1722use Symfony \UX \Autocomplete \Doctrine \DoctrineRegistryWrapper ;
2126 */
2227final class AutocompleteResultsExecutor
2328{
29+ private PropertyAccessorInterface $ propertyAccessor ;
30+ private ?Security $ security ;
31+
2432 public function __construct (
2533 private DoctrineRegistryWrapper $ managerRegistry ,
26- private ?Security $ security = null
34+ $ propertyAccessor ,
35+ /* Security $security = null */
2736 ) {
37+ if ($ propertyAccessor instanceof Security) {
38+ 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);
39+ $ this ->security = $ propertyAccessor ;
40+ $ this ->propertyAccessor = new PropertyAccessor ();
41+ } else {
42+ $ this ->propertyAccessor = $ propertyAccessor ;
43+ $ this ->security = \func_num_args () >= 3 ? func_get_arg (2 ) : null ;
44+ }
2845 }
2946
3047 public function fetchResults (EntityAutocompleterInterface $ autocompleter , string $ query , int $ page ): AutocompleteResults
@@ -50,15 +67,61 @@ public function fetchResults(EntityAutocompleterInterface $autocompleter, string
5067 $ paginator = new Paginator ($ queryBuilder );
5168
5269 $ nbPages = (int ) ceil ($ paginator ->count () / $ queryBuilder ->getMaxResults ());
70+ $ hasNextPage = $ page < $ nbPages ;
5371
5472 $ results = [];
73+
74+ if (null === $ groupBy = $ autocompleter ->getGroupBy ()) {
75+ foreach ($ paginator as $ entity ) {
76+ $ results [] = [
77+ 'value ' => $ autocompleter ->getValue ($ entity ),
78+ 'text ' => $ autocompleter ->getLabel ($ entity ),
79+ ];
80+ }
81+
82+ return new AutocompleteResults ($ results , $ hasNextPage );
83+ }
84+
85+ if (\is_string ($ groupBy )) {
86+ $ groupBy = new PropertyPath ($ groupBy );
87+ }
88+
89+ if ($ groupBy instanceof PropertyPathInterface) {
90+ $ accessor = $ this ->propertyAccessor ;
91+ $ groupBy = function ($ choice ) use ($ accessor , $ groupBy ) {
92+ try {
93+ return $ accessor ->getValue ($ choice , $ groupBy );
94+ } catch (UnexpectedTypeException ) {
95+ return null ;
96+ }
97+ };
98+ }
99+
100+ if (!\is_callable ($ groupBy )) {
101+ throw new \InvalidArgumentException (sprintf ('Option "group_by" must be callable, "%s" given. ' , get_debug_type ($ groupBy )));
102+ }
103+
104+ $ optgroupLabels = [];
105+
55106 foreach ($ paginator as $ entity ) {
56- $ results [] = [
107+ $ result = [
57108 'value ' => $ autocompleter ->getValue ($ entity ),
58109 'text ' => $ autocompleter ->getLabel ($ entity ),
59110 ];
111+
112+ $ groupLabels = $ groupBy ($ entity , $ result ['value ' ], $ result ['text ' ]);
113+
114+ if (null !== $ groupLabels ) {
115+ $ groupLabels = \is_array ($ groupLabels ) ? array_map ('strval ' , $ groupLabels ) : [(string ) $ groupLabels ];
116+ $ result ['group_by ' ] = $ groupLabels ;
117+ $ optgroupLabels = array_merge ($ optgroupLabels , $ groupLabels );
118+ }
119+
120+ $ results [] = $ result ;
60121 }
61122
62- return new AutocompleteResults ($ results , $ page < $ nbPages );
123+ $ optgroups = array_map (fn (string $ label ) => ['value ' => $ label , 'label ' => $ label ], array_unique ($ optgroupLabels ));
124+
125+ return new AutocompleteResults ($ results , $ hasNextPage , $ optgroups );
63126 }
64127}
0 commit comments