Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Autocomplete Bundle Laravel Package

makinacorpus/autocomplete-bundle

A Symfony bundle that adds AJAX-powered autocomplete form fields, with reusable form types, controllers, and Doctrine integration to serve typeahead suggestions from your application's data.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and enable

composer require makinacorpus/autocomplete-bundle

Register the bundle (namespace/class name — confirm with composer show makinacorpus/autocomplete-bundle and the README, this package predates a lot of conventions):

// config/bundles.php  (Symfony 4+)
return [
    // ...
    MakinaCorpus\AutocompleteBundle\MakinaCorpusAutocompleteBundle::class => ['all' => true],
];

// Legacy Symfony 3: add to AppKernel::registerBundles()

Then publish the widget assets and check routing:

bin/console assets:install --symlink public
bin/console debug:router | grep -i autocomplete
bin/console debug:form | grep -i autocomplete

2. Where to look first

With one star and no dependents, the README is the only contract you have. Read these in order:

  • README.md — the form type's options and the expected JSON payload of the autocomplete endpoint.
  • src/Form/Type/ — the form type you extend; debug:form gives you the authoritative option list.
  • src/Controller/ — whether the bundle ships its own endpoint or expects you to provide one.
  • Resources/public/ — the JavaScript; this defines the AJAX contract (query param name, response shape) and is the thing you'll most likely end up overriding.
  • composer.json — Symfony version constraints. This is the make-or-break detail.

3. First use case

An entity picker in a form: a Document field where the user types a title and picks from a server-side search, optionally uploading a new file inline instead of selecting an existing one. Wire one field, hit the endpoint with curl, then wire the JS.

Implementation Patterns

Form type with an external endpoint

The common pattern is: the bundle gives you the widget and the form type, you give it a route.

use MakinaCorpus\AutocompleteBundle\Form\Type\AutocompleteType;

$builder->add('document', AutocompleteType::class, [
    'route' => 'app_autocomplete_documents',
    'min_length' => 2,
    'multiple' => false,
    // 'upload' => true,  // only if you actually want the upload widget
]);

Your endpoint

Keep it dumb and fast — it is called on every keystroke:

#[Route('/_autocomplete/documents', name: 'app_autocomplete_documents', methods: ['GET'])]
public function documents(Request $request, DocumentRepository $repo): JsonResponse
{
    $q = trim((string) $request->query->get('q', ''));
    if (mb_strlen($q) < 2) {
        return $this->json([]);   // don't run a LIKE on 1 char
    }

    $items = $repo->searchByTitle($q, 20);   // LIMIT 20, indexed column

    return $this->json(array_map(fn (Document $d) => [
        'id'   => $d->getId(),
        'text' => $d->getTitle(),
    ], $items));
}

Verify the exact key names against Resources/public/*.js — bundles of this vintage vary between id/text, value/label, and {results: [...]}. A 200 with the wrong shape looks identical to a broken widget.

Never trust the submitted value. The widget submits an id (or a title) that the client chose. Either build a ChoiceType with a choice_loader and let the bundle's type delegate, or add a PRE_SUBMIT/POST_SUBMIT listener that re-fetches the entity and rejects anything outside the allowed set. 'id' => 999999 in a POST body is trivial.

Entity relationship pattern

Pair a hidden id field with a DataTransformer so the entity hydrates on PRE_SET_DATA and serialises back on transform(). This keeps the rest of your form code typed instead of juggling scalars.

Upload variant

If you use the upload widget: upload to a non-public temp directory, persist a reference, and clean up orphans with a scheduled command. Do mime/size validation server-side (File constraint), never in JS only. Treat the autocomplete-of-existing-files as a search, not an authorisation check.

Theming and assets

Override the template (form_themes in config/packages/twig.yaml) rather than editing vendor files, and if you're on Encore/AssetMapper, prefer re-implementing the JS against your endpoint over fighting assets:install — the widget is the least valuable part of this bundle.

Gotchas and Tips

  • It is effectively unmaintained. One star, zero dependents, opportunity score 3.18. Pin an exact version ("makinacorpus/autocomplete-bundle": "1.0.x"), read the last-commit date, and assume you own the code. If composer.json caps Symfony at 3.x/4.x, budget for forking or writing a 60-line Stimulus controller instead — an autocomplete field is genuinely cheap to rebuild.
  • Check PHP and Symfony constraints before you design anything. If you're on Symfony 6/7, debug:form failing or the type not appearing is your first signal; don't debug Twig before you've confirmed the bundle booted.
  • Endpoint security. The route is public by default. Add a firewall rule or #[IsGranted], rate-limit it (symfony/rate-limiter), and cap results. This is the most common way such a bundle becomes a DoS vector.
  • N+1 and unindexed LIKE '%…%'. Profile the endpoint with the Symfony profiler; a leading wildcard on a large table is a sequential scan on every keystroke.
  • CSRF / method mismatch. Autocomplete endpoints are usually GET; the upload endpoint is not. Mixing them up produces silent 405s in the browser console and nothing in the PHP log.
  • Debugging checklist: curl -s 'https://app.test/_autocomplete/documents?q=foo' -H 'Accept: application/json' | jq → check shape; browser Network tab → check the actual query param name the JS sends (often term, not q); bin/console debug:router → confirm the name matches the route option exactly.
  • XSS. The widget injects labels into the DOM. If you return user-supplied text, confirm the JS sets textContent rather than innerHTML, or escape server-side.
  • Accessibility. JS from this era rarely implements role="combobox" / aria-activedescendant. If a11y is in scope, plan to replace the widget and keep the form type purely as a validation/hydration shim.
  • Extension points, in order of preference: (1) decorate the controller/service, (2) override the Twig template via form_themes, (3) replace the JS entirely, (4) fork. Avoid editing vendor/ — you will forget, and composer install on deploy will revert it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels