Symfony UX Autocomplete: Live-Suche mit Tom Select ohne eigenes JavaScript
AI generated
SF
{ }
Symfony · UX · Stimulus · Forms
Symfony UX Autocomplete
Live-Suche mit Tom Select ohne eigenes JavaScript

Symfony UX Autocomplete verbindet Doctrine-Entities, eigene Suchlogik und externe APIs mit einem einzigen Formularfeld-Typ und liefert dabei serverseitiges Rendering, Stimulus-Steuerung und Tom Select als Frontend, ohne dass ein einziges eigenes JavaScript-Modul geschrieben werden muss.

18 Min. Lesezeit Symfony UX Autocomplete · Tom Select · Stimulus Symfony 7.x · PHP 8.4

1. Warum Symfony UX Autocomplete existiert

Symfony UX Autocomplete löst ein Problem, das in fast jedem größeren Formular auftaucht: Eine Dropdown-Liste mit tausenden Einträgen lässt sich nicht mehr sinnvoll als normales Select-Feld rendern. Vor Symfony UX war die übliche Lösung, select2 oder Choices.js manuell einzubinden, eigene JavaScript-Initialisierung zu schreiben und die Serverkommunikation per Fetch selbst zu implementieren. Das führte in jedem Projekt zu einer eigenen, leicht abweichenden Lösung mit eigenen Bugs.

Symfony UX Autocomplete kapselt genau diese Aufgabe in ein wiederverwendbares Bundle. Der Formularfeld-Typ generiert automatisch das passende Stimulus-Attribut, das Bundle liefert einen fertigen Controller mit, der Tom Select initialisiert, und die Serverkommunikation läuft über einen generierten Controller-Endpunkt, der Doctrine-Repository-Queries kapselt. Das Ergebnis: Live-Suche in Formularen ohne eine einzige Zeile eigenes JavaScript, dafür mit klar definierten PHP-Erweiterungspunkten für alles, was über den Standardfall hinausgeht.

Der Unterschied zu einer reinen JavaScript-Bibliothek liegt in der Integration. Symfony UX Autocomplete versteht Doctrine-Entities, Validierungsregeln und CSRF-Schutz von Haus aus, weil es Teil des Symfony-Form-Systems ist und nicht nebenher als unabhängiges Frontend-Widget existiert.

2. Installation und Grundkonfiguration

Die Installation von Symfony UX Autocomplete erfolgt über Composer und bringt bei AssetMapper-Projekten die JavaScript-Abhängigkeiten automatisch über den Symfony Asset Mapper mit. Bei Encore-Projekten muss zusätzlich yarn beziehungsweise npm ausgeführt werden, um Tom Select in den Build aufzunehmen. Nach der Installation registriert das Bundle automatisch einen Stimulus-Controller und stellt den Formularfeld-Typ Symfony\UX\Autocomplete\Form\AutocompleteField sowie den Options-Parameter autocomplete für EntityType bereit.

Ein wichtiger Punkt bei der Grundkonfiguration: Symfony UX Autocomplete registriert für jede Entity, die Autocomplete nutzt, automatisch einen internen Controller unter /_ux_entity_autocomplete/{alias}. Dieser Endpunkt muss nicht manuell in der routes.yaml eingetragen werden, wird aber durch den Security-Layer wie jede andere Route behandelt. Wer Autocomplete auf sensiblen Entities einsetzt, muss also explizit prüfen, ob der generierte Endpunkt durch Firewalls und Access-Control-Regeln abgedeckt ist.


# Install Symfony UX Autocomplete
composer require symfony/ux-autocomplete

# AssetMapper projects: assets are wired automatically
bin/console importmap:require symfony/ux-autocomplete

# Encore projects only: build the JS integration
yarn add @symfony/ux-autocomplete --dev
yarn encore dev

3. Autocomplete direkt im EntityType aktivieren

Der schnellste Einstieg in Symfony UX Autocomplete ist die Option autocomplete direkt am EntityType. Damit wird aus einem gewöhnlichen Dropdown mit tausenden Produkten oder Kunden ein Live-Suchfeld, ohne dass eine eigene Klasse geschrieben werden muss. Symfony generiert im Hintergrund den passenden Endpunkt, der über das Repository der Entity sucht und die Ergebnisse als JSON an Tom Select liefert.

Diese einfache Form von Symfony UX Autocomplete eignet sich für den Standardfall: Suche über ein oder zwei Felder, keine komplexe Sortierlogik, keine Rechteprüfung pro Ergebnis. Sobald die Suchlogik komplizierter wird, etwa wenn mehrere Felder kombiniert oder Berechtigungen pro Zeile geprüft werden müssen, wechselt man zur eigenen AutocompleteField-Klasse aus Abschnitt vier.


// src/Form/OrderType.php
namespace App\Form;

use App\Entity\Customer;
use App\Entity\Order;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;

final class OrderType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('customer', EntityType::class, [
            'class' => Customer::class,
            'choice_label' => 'fullName',
            // Enable Symfony UX Autocomplete for this field
            'autocomplete' => true,
            'placeholder' => 'Kunde suchen...',
        ]);
    }

    public function getBlockPrefix(): string
    {
        return '';
    }
}

4. Eigene AutocompleteField-Klasse für komplexe Suchen

Sobald die Suche über Symfony UX Autocomplete mehr können soll als ein einfaches LIKE-Query auf ein Feld, wird eine eigene Klasse angelegt, die AbstractType erweitert und das Interface EntityAutocompleteFieldInterface implementiert. Diese Klasse definiert eine createFilteredQueryBuilder-Methode, in der beliebig komplexe Doctrine-QueryBuilder-Logik stehen kann, inklusive Joins über verknüpfte Entities, Sortierung nach Relevanz oder Filterung nach dem aktuell eingeloggten Benutzer.

Der entscheidende Vorteil dieser Variante von Symfony UX Autocomplete: Die Suchlogik lebt an genau einer Stelle im Code und kann in mehreren Formularen wiederverwendet werden, ohne dass Optionen dupliziert werden. Die Klasse kann zusätzlich eine getLabel-Methode überschreiben, um das angezeigte Label pro Treffer individuell zusammenzusetzen, etwa Kundenname plus Kundennummer plus Stadt in einer Zeile.


// src/Form/CustomerAutocompleteField.php
namespace App\Form;

use App\Entity\Customer;
use App\Security\CurrentUserProvider;
use Doctrine\ORM\QueryBuilder;
use Symfony\UX\Autocomplete\Doctrine\EntityAutocompleteFieldInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;

final class CustomerAutocompleteField extends EntityType implements EntityAutocompleteFieldInterface
{
    public function __construct(
        private readonly CurrentUserProvider $currentUserProvider,
    ) {
    }

    public function getLabel(object $entity): string
    {
        /** @var Customer $entity */
        return sprintf('%s (%s, %s)', $entity->getFullName(), $entity->getCustomerNumber(), $entity->getCity());
    }

    public function createFilteredQueryBuilder(QueryBuilder $queryBuilder, string $query): void
    {
        $tenantId = $this->currentUserProvider->getCurrentTenantId();

        $queryBuilder
            ->andWhere('entity.tenant = :tenantId')
            ->setParameter('tenantId', $tenantId)
            ->andWhere('entity.fullName LIKE :query OR entity.customerNumber LIKE :query')
            ->setParameter('query', '%' . $query . '%')
            ->orderBy('entity.fullName', 'ASC')
            ->setMaxResults(20);
    }

    public function getOptionsForAutocomplete(): array
    {
        return [
            'class' => Customer::class,
            'choice_label' => 'fullName',
        ];
    }
}

5. Externe APIs statt Doctrine-Entities anbinden

Nicht jede Suche in Symfony UX Autocomplete muss gegen die eigene Datenbank laufen. Für Produktdaten aus einem externen PIM-System, Adressvorschläge aus einer Geodaten-API oder Artikelsuchen gegen Elasticsearch bietet das Bundle die Möglichkeit, einen eigenen Controller als Datenquelle zu registrieren, statt auf ein Doctrine-Repository zurückzugreifen. Der Controller muss lediglich das erwartete JSON-Format zurückgeben, das Tom Select für die Ergebnisliste erwartet.

Dieser Ansatz von Symfony UX Autocomplete entkoppelt die Frontend-Komponente vollständig von der Datenquelle. Das Stimulus-Attribut zeigt statt auf den internen Entity-Endpunkt direkt auf eine eigene Route, die intern einen HTTP-Client für die externe API nutzt, das Ergebnis normalisiert und als JSON zurückgibt. Für den Nutzer sieht die Interaktion identisch zur Entity-Variante aus, technisch läuft im Hintergrund aber eine komplett andere Anfrage.


// src/Controller/ProductSearchAutocompleteController.php
namespace App\Controller;

use App\Client\ExternalPimClient;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

final class ProductSearchAutocompleteController extends AbstractController
{
    public function __construct(
        private readonly ExternalPimClient $pimClient,
    ) {
    }

    #[Route('/autocomplete/products', name: 'app_autocomplete_products', methods: ['GET'])]
    public function __invoke(Request $request): JsonResponse
    {
        $query = (string) $request->query->get('query', '');

        if (mb_strlen($query) < 2) {
            return new JsonResponse(['results' => []]);
        }

        $products = $this->pimClient->search($query, limit: 15);

        // Tom Select expects a "results" array with id/text pairs
        $results = array_map(
            static fn (array $product): array => [
                'id' => $product['sku'],
                'text' => sprintf('%s (%s)', $product['name'], $product['sku']),
            ],
            $products,
        );

        return new JsonResponse(['results' => $results]);
    }
}

6. Tom Select konfigurieren: Multiple, Preload, Rendering

Unter der Haube nutzt Symfony UX Autocomplete Tom Select als Rendering-Bibliothek. Die meisten Tom-Select-Optionen lassen sich über data-Attribute am generierten Element oder über Optionen des Formularfeld-Typs steuern, ohne eine eigene Stimulus-Controller-Klasse schreiben zu müssen. Die Option multiple aktiviert Mehrfachauswahl mit Tags, die Option tom_select_options reicht ein assoziatives Array direkt an die Tom-Select-Instanz durch, etwa für eigene render-Callbacks.

Für Anwendungsfälle, in denen bereits beim Laden der Seite ein vorausgewählter Wert angezeigt werden soll, unterstützt Symfony UX Autocomplete das Preloading des aktuellen Wertes, indem das Label serverseitig mitgerendert wird. Das verhindert den sonst üblichen kurzen Moment, in dem nur die ID sichtbar ist, bis JavaScript den passenden Text nachlädt. Wer eigene Render-Templates für die Dropdown-Optionen benötigt, etwa mit Avatar-Bild und Zusatzinformationen, kann dafür render_option und render_item als JavaScript-Funktionen über die Optionen einreichen.


{# templates/order/new.html.twig #}
{{ form_start(form) }}
  {{ form_row(form.customer, {
    attr: {
      'data-tom-select-multiple': false,
      'data-tom-select-preload': 'focus'
    }
  }) }}

  {{ form_row(form.products, {
    attr: { 'data-tom-select-multiple': true }
  }) }}
{{ form_end(form) }}

7. Styling mit Tailwind ohne eigenes CSS-Framework

Tom Select bringt ein eigenes Standard-Theme mit, das visuell nicht zu einem Hyvä-nahen Tailwind-Frontend passt. Statt das komplette Tom-Select-CSS zu importieren, überschreibt man in Symfony UX Autocomplete-Projekten gezielt die generierten CSS-Klassen von Tom Select mit eigenen Tailwind-Utility-Klassen. Tom Select erzeugt vorhersehbare Klassennamen wie ts-control, ts-dropdown und ts-wrapper, die sich über eine dünne CSS-Schicht an das eigene Design-System anpassen lassen.

Wichtig bei diesem Ansatz von Symfony UX Autocomplete ist, nicht das komplette Standard-Stylesheet von Tom Select zu laden, sondern nur die minimale Basisdatei ohne Theme, und darauf die eigenen Tailwind-Klassen zu setzen. Das reduziert die CSS-Bundle-Größe erheblich und verhindert Konflikte zwischen Tailwinds Utility-First-Ansatz und Tom Selects vorgefertigten Klassen.


/* assets/styles/autocomplete.css */
/* Minimal overrides on top of Tom Select's base (unstyled) CSS */

.ts-wrapper.single .ts-control {
  @apply rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm shadow-sm;
}

.ts-wrapper.focus .ts-control {
  @apply border-gray-700 ring-2 ring-gray-200;
}

.ts-dropdown {
  @apply mt-1 rounded-lg border border-gray-200 bg-white shadow-lg;
}

.ts-dropdown .option.active {
  @apply bg-gray-100 text-gray-900;
}

8. Performance: Debounce, Mindestlänge und Caching

Ohne Feinabstimmung sendet Symfony UX Autocomplete bei jedem Tastendruck eine Anfrage an den Server, was bei schnellem Tippen unnötig viele parallele Requests erzeugt. Das Bundle debounced Eingaben standardmäßig, die Wartezeit lässt sich über die Option debounce in Millisekunden anpassen. Zusätzlich verhindert eine konfigurierbare Mindestlänge der Sucheingabe, dass bei ein oder zwei Zeichen bereits tausende Treffer aus der Datenbank geladen werden.

Auf Serverseite lohnt es sich, bei häufig wiederholten Suchanfragen einen kurzen Cache einzuziehen, etwa über den Symfony Cache-Layer mit einer TTL von wenigen Sekunden. Das reduziert die Datenbanklast bei populären Suchbegriffen, ohne dass Ergebnisse merklich veraltet wirken. Wer Symfony UX Autocomplete gegen eine externe API einsetzt, sollte den Cache zusätzlich nutzen, um Rate-Limits der externen API nicht zu überschreiten.

9. Symfony UX Autocomplete im Vergleich

Bevor man sich für Symfony UX Autocomplete entscheidet, lohnt sich ein Blick auf die Alternativen, die in bestehenden Projekten häufig anzutreffen sind. Jeder Ansatz hat unterschiedliche Auswirkungen auf Wartungsaufwand, Bundle-Größe und Integrationstiefe mit dem Symfony-Formsystem.

Ansatz Integration Eigener JS-Code Bundle-Größe
select2 manuell eingebunden Lose, eigenes Fetch nötig Viel Mittel
Choices.js manuell Lose, eigenes Fetch nötig Viel Mittel
Symfony UX Autocomplete Tief, Form-System-nativ Keiner Gering
Eigener Stimulus-Controller Mittel, selbst gebaut Viel Variabel
Native HTML datalist Sehr lose Keiner Keine

Die native datalist-Lösung ohne jede Bibliothek wirkt verlockend, weil sie null Bytes an zusätzlichem JavaScript kostet, scheitert in der Praxis aber an fehlender Server-Suche, fehlendem Debouncing und inkonsistentem Browser-Rendering. Symfony UX Autocomplete liegt genau zwischen den beiden Extremen: minimaler eigener Code, aber volle Kontrolle über Serverlogik und Darstellung, wo sie gebraucht wird.

Mironsoft

Symfony-Entwicklung mit modernem UX-Frontend

Formulare, die sich anfühlen wie eine SPA, ganz ohne eigenes JavaScript?

Wir integrieren Symfony UX Autocomplete in bestehende Formulare, bauen eigene AutocompleteField-Klassen für komplexe Suchen und binden externe APIs sauber an euer Symfony-Projekt an.

UX-Audit

Bestehende Formulare auf Autocomplete-Potenzial prüfen

Implementierung

Eigene AutocompleteField-Klassen und externe API-Anbindung

Tailwind-Styling

Tom Select nahtlos in euer Design-System integrieren

10. Zusammenfassung

Symfony UX Autocomplete löst das klassische Problem großer Dropdown-Listen, indem es Doctrine-Repositories, eigene Suchlogik und externe APIs unter einer einheitlichen Stimulus-Integration mit Tom Select vereint. Für den einfachen Fall genügt die Option autocomplete am EntityType, für komplexere Anforderungen übernimmt eine eigene AutocompleteField-Klasse mit vollständiger QueryBuilder-Kontrolle die Suche. Externe Datenquellen lassen sich über einen eigenen Controller anbinden, ohne die Frontend-Integration zu verändern.

Der größte Vorteil gegenüber manuell eingebundenen JavaScript-Bibliotheken liegt in der Wartbarkeit: Symfony UX Autocomplete bleibt Teil des Symfony-Ökosystems, profitiert von Sicherheitsupdates über Composer und benötigt keinen separaten Build-Prozess für die Kernfunktionalität. Wer zusätzlich Debounce, Mindestlänge und serverseitigen Cache konfiguriert, bekommt eine Live-Suche, die sich unter Last genauso zuverlässig verhält wie unter normalen Bedingungen.

Symfony UX Autocomplete — Das Wichtigste auf einen Blick

Schnellstart

Option autocomplete: true am EntityType reicht für den Standardfall, ganz ohne eigenen JavaScript-Code.

Komplexe Suchen

Eigene AutocompleteField-Klasse mit createFilteredQueryBuilder für Joins, Rechte und individuelle Labels.

Externe Quellen

Eigener Controller als Datenquelle für PIM-Systeme, Elasticsearch oder Geodaten-APIs.

Performance

Debounce, Mindestlänge und Server-Cache verhindern unnötige Datenbank- oder API-Last.

11. FAQ: Symfony UX Autocomplete

1Was ist Symfony UX Autocomplete?
Ein Bundle, das Formularfelder mit Tom Select und Stimulus zu Live-Suchfeldern macht, mit fertigem Backend für Doctrine-Entities und eigenen Erweiterungspunkten.
2Brauche ich eigenes JavaScript?
Nein, im Standardfall nicht. Der mitgelieferte Stimulus-Controller initialisiert Tom Select automatisch.
3Externe API statt Doctrine anbinden?
Über einen eigenen Controller, der das JSON-Format mit einer results-Liste aus id und text zurückgibt.
4Endpunkt richtig absichern?
Der Endpunkt läuft unter /_ux_entity_autocomplete/{alias} und muss explizit in access_control-Regeln berücksichtigt werden.
5Zu viele Anfragen beim Tippen verhindern?
Über die Option debounce in Millisekunden und eine Mindestlänge der Sucheingabe.
6Mehrfachauswahl möglich?
Ja, über die multiple-Option, die Tom Select in den Tag-Modus versetzt.
7Tom Select mit Tailwind stylen?
Nur die ungestylte Basis-CSS-Datei laden und generierte Klassen wie ts-control mit Tailwind-Utilities überschreiben.
8AssetMapper oder Encore?
Beides wird unterstützt, bei Encore muss die JS-Abhängigkeit zusätzlich per yarn installiert werden.
9Vorausgewählten Wert ohne Nachladen anzeigen?
Das Label des aktuellen Wertes wird serverseitig mitgerendert, kein zusätzlicher Request nötig.
10Lohnt sich Caching?
Ja, besonders bei externen APIs mit Rate-Limits, ein kurzer Server-Cache reduziert Last spürbar.