pugx/autocompleter-bundle
PUGXAutocompleterBundle adds AJAX-powered autocomplete fields to Symfony forms, with built-in Doctrine entity support and jQuery UI integration. It handles suggestions, entity-to-label conversion, and form wiring, making it easy to replace select boxes or text inputs with searchable autocomplete wid...
The first thing to establish is that this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, and nothing that will auto-register under bootstrap/providers.php. composer require pugx/autocompleter-bundle inside a Laravel app installs files that will never be loaded. So "getting started" splits into two paths.
If you are actually working in the Symfony codebase this targets (the realistic case if you inherit a project):
composer require pugx/autocompleter-bundle and register PUGX\AutoCompleterBundle\PUGXAutoCompleterBundle (Flex does it for you; otherwise add it to config/bundles.php).vendor/pugx/autocompleter-bundle/Form/Type/ — that is the entire public surface. Everything else is your code.term query parameter and returns a list of {value, label} objects.If you are a Laravel developer evaluating it as a pattern to copy, the useful takeaway is much smaller than the package: a TextType extension that stamps a data-autocomplete-url attribute onto the input, plus a JSON search route. You can reproduce the whole idea in Laravel with a Blade partial and a single Route::get('/autocomplete/users', ...) in well under an hour — see the patterns below.
The bundle's contract. The form type is a thin wrapper over Symfony's TextType. You pass a route name and (depending on version) an entity class and a property; the type renders a normal <input type="text"> with a data attribute pointing at your endpoint. The bundle does not query your database and generally does not ship the JavaScript — you wire up jQuery UI Autocomplete (or Select2) yourself against that attribute. That separation is the one design decision worth stealing.
Endpoint pattern. Your controller reads term, delegates to a repository method, and returns JSON:
$term = $request->query->get('term', '');
$users = $userRepository->searchByName($term, 10);
return $this->json(array_map(fn ($u) => [
'value' => $u->getId(),
'label' => $u->getFullName(),
], $users));
Keep the shape stable (value/label) — the front-end library and any "select this record" callback both depend on it, and changing it later breaks every form that used the widget.
Repository pattern. Push the search into a repository method that applies LIMIT and an ordered LIKE term% or LIKE %term% clause. Never let the controller build the query inline; you will end up duplicating it for every autocompleted entity and diverging on limits and escaping.
The Laravel translation. If you are porting rather than installing, the idiomatic Laravel version is:
Blade component (<x-autocomplete :url="route('users.search')" name="user_id" />) that renders the input plus a small Alpine or vanilla fetch handler with debounce and abort-on-new-keystroke.response()->json(...), kept out of your resource controllers.FormRequest or explicit validation that the term is a string and length-bounded.Integration tips. Add an id-vs-label split so the input can display a human name while posting a machine ID. If the field is pre-filled on edit, hydrate it server-side and disable the autocomplete until the user actually types, otherwise the widget clears the existing value on focus in some jQuery UI versions.
entity/property options to produce results automatically. They only feed markup; the query, the limit, and the escaping are all yours.LIMIT is the classic production bug. Unbounded LIKE '%term%' scans are fine on a dev fixture and fatal on a real table. Always cap results and add an index or a pg_trgm/fulltext fallback.anonymous while debugging and forget to restore it; also confirm whether your stack expects the token as a header or a query param.label as HTML in several configurations. Escape the label server-side or restrict it to plain text, otherwise a user's display name becomes an XSS vector.data-* attribute first, then the network tab: a 302 to a login page from an expired session looks identical to "autocomplete broken."How can I help you explore Laravel packages today?