acseo / select-autocomplete-bundle
Make Symfony form autocompletion easily
Installs: 1 660
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 4
Forks: 1
Open Issues: 0
Type:symfony-bundle
Requires
- php: ^7.2 || ^8.0
- symfony/dependency-injection: ^3.4 || ^4.0 || ^5.0
- symfony/flex: ^1.3.0
- symfony/form: ^3.4 || ^4.0 || ^5.0
- symfony/property-access: ^3.4 || ^4.0 || ^5.0
- symfony/serializer: ^3.4 || ^4.0 || ^5.0
Requires (Dev)
- doctrine/doctrine-bundle: ^1.6 || ^2.0
- doctrine/doctrine-fixtures-bundle: ^3.0
- doctrine/mongodb-odm-bundle: ^4.1
- friendsofphp/php-cs-fixer: ^2.13
- jangregor/phpstan-prophecy: ^0.2.0
- phpstan/phpstan-symfony: ^0.10.2
- sensio/framework-extra-bundle: ^3.4 || ^4.0 || ^5.0
- symfony/config: ^3.4 || ^4.0 || ^5.0
- symfony/console: ^3.4 || ^4.0 || ^5.0
- symfony/dotenv: ^3.4 || ^4.0 || ^5.0
- symfony/framework-bundle: ^3.4 || ^4.0 || ^5.0
- symfony/http-kernel: ^3.4 || ^4.4 || ^5.0
- symfony/phpunit-bridge: ^3.4 || 4.3 || ^5.0
- symfony/twig-bundle: ^3.4 || ^4.4 || ^5.0
- symfony/yaml: ^3.4 || ^4.0 || ^5.0
This package is auto-updated.
Last update: 2024-12-22 08:03:49 UTC
README
Introduction
This bundle helps you to build autocomplete fields in your symfony forms without declaring any controller or action.
Fully configurable, you can override any part of the autocompletion process very easily.
Doctrine ORM & ODM are supported by default, but you can create your own providers for other extensions !
Installation
Install source code with composer :
$ composer require acseo/select-autocomplete-bundle
Enable the bundle :
// config/bundles.php return [ Acseo\SelectAutocomplete\SelectAutocompleteBundle::class => ['all' => true] ];
Use the bundle form theme globally (or locally in your templates) :
# config/packages/twig.yaml twig: form_themes: - '@SelectAutocomplete/form_theme.html.twig'
Usage:
Let's start by a simple example :
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\User; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => User::class, // The searchable properties used for query 'properties' => ['profile.firstName', 'profile.lastName'], ]) ;
With your favorite js library, transform the rendered input to autocomplete input (example with select2) :
$('.acseo-select-autocomplete').each((i, el) => { const $el = $(el); $el.select2({ minimumInputLength: 1, ajax: { cache: false, url: $el.data('autocomplete-url'), // Get autocomplete url to retrieve search responses delay: 250, dataType: 'json', data: params => ({ q: params.term || '', // Append the search terms to url // response_format: 'json' // (optional, json by default, see form type options for allowed values) }), processResults: data => ({ // Transform entrypoint resuls results: Object.keys(data).map(value => ({ id: value, text: data[value] })) }) } }); });
Please note 3 important things in this js example :
- The rendered input has a
data-autocomplete-url
attribute and the value inside can be used to retrieve search results. - The query param
q
, which represents the search terms, has to be added to data-autocomplete-url value. - By default search results are returned by entrypoint like
[{ "value": "label" }]
.
Your autocomplete is now functional !
Form options
Tips : You can also override any part of the process more globally by creating a class which extends AutocompleteType.
class
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class ]) ;
properties
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, 'properties' => 'targetProperty', // OR 'properties' => ['name', 'profile.email'], ]) ;
display
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, 'display' => 'targetPropertyOrMethod', // OR 'display' => 'nestedProperty.targetProperty', // OR 'display' => ['user.firstName', 'user.lastName'], // OR 'display' => function(TargetClass $object): string { return $object->getTargetProperty(); }, ]) ;
strategy
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, 'strategy' => 'starts_with', // LIKE ...% // OR 'strategy' => 'ends_with', // LIKE %... // OR 'strategy' => 'contains', // LIKE %...% // OR 'strategy' => 'equals', // = ... ]) ;
multiple
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, 'multiple' => true, ]) ;
format
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; use Symfony\Component\HttpFoundation\Response; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, // Options values are provided by your serializer (these are default format supported by symfony serializer) // Format can be override from js by add response_format param in data-autocomplete-url 'format' => 'json', // xml|csv|yaml|... // OR // Encode response with your logic 'format' => function (array $normalized, Response $response): Response { return $response->setContent(json_encode($normalized)); } ]) ;
identifier
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, // Identifier is used as choice value 'identifier' => 'targetPropertyOrMethod', ]) ;
autocomplete_url
Sometimes you will need this option to retrieve search results from specific entrypoint.
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, // This option value will be set in data-autocomplete-url of select input attributes 'autocomplete_url' => '/my-custom-entrypoint?param1=kevin', ]) ;
transformer
use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, // Don't transform identifiers values to objects 'transformer' => false, // OR // Use custom transformer 'transformer' => $myCustomTransformer ]) ;
provider
use Acseo\SelectAutocomplete\DataProvider\Doctrine\AbstractDoctrineDataProvider; use Acseo\SelectAutocomplete\Form\Type\AutocompleteType; use App\Entity\TargetClass; $formBuilder ->add('example', AutocompleteType::class, [ 'class' => TargetClass::class, // Override provider on search action to retrieve custom collection (Usage of partial query is allowed) // The second argument is the default provider which supports the model class 'provider' => function(string $terms, AbstractDoctrineDataProvider $provider) { return $provider->getRepository(TargetClass::class) ->createQueryBuilder('o') ->where('o.name LIKE :name') ->setParameter('name', $terms.'%') ->getQuery() ->getResult() ; // You can also just override default query (available with ORM & ODM Doctrine providers) // // if ($provider instanceof ORMDataProvider) { // $qb = $provider->createSearchQueryBuilder('o', TargetClass::class, ['name'], $terms, 'starts_with'); // // Custom query // return $qb->getQuery()->getResult(); // } // // if ($provider instanceof ODMDataProvider) { // $qb = $provider->createSearchAggregationBuilder(TargetClass::class, ['name'], $terms, 'starts_with'); // // Custom query // return $qb->execute()->toArray(); // } }, // OR // Use your own provider object 'provider' => $myProvider, // OR // You can specify provider to use (the service has to be tagged as acseo_select_autocomplete.data_provider). // 2 providers are included by default : ORMDataProvider and ODMDataProvider. // You can add many providers, for specific model class or other kind of databases ! 'provider' => MyCustomProvider::class, // OR // Create custom provider // To know more about providers, please see Providers section. 'provider' => [ 'find_by_ids' => function(array $ids, AbstractDoctrineDataProvider $provider) { return $provider->getRepository(TargetClass::class)->findBy(['id' => $ids]); }, 'find_by_terms' => function(string $terms, AbstractDoctrineDataProvider $provider) { return $provider->getRepository(TargetClass::class) ->createQueryBuilder('o') ->where('o.name LIKE :name') ->setParameter('name', $terms.'%') ->getQuery() ->getResult() ; } ], // If provider option is not set, the provider used is the first which supports model class ]) ;
Providers
Providers classes are used to retrieve search results form database and transform form view data to model object.
2 Doctrine providers are included by default : ORMDataProvider and ODMDataProvider which supports multiple db connexions.
You can create your own provider for specific model class or specific database. This is an arbitrary example :
<?php namespace App\Form\Autocomplete\DataProvider; use Acseo\SelectAutocomplete\DataProvider\DataProviderInterface; class CustomDataProvider implements DataProviderInterface { private $manager; public function __construct(\SomeWhere\CustomManager $manager) { $this->manager = $manager; } /** * Does provider supports the model class. */ public function supports(string $class): bool { return $this->manager->supports($class); // To make specific provider for specific model class // return $class === \App\Entity\Foo::class } /** * Used to retrieve object with form view data (reverseTransform). */ public function findByIds(string $class, string $identifier, array $ids): array { return $this->manager->findOneBy([ $identifier => $ids ]); } /** * Find collection results of autocomplete action. */ public function findByTerms(string $class, array $properties, string $terms, string $strategy): array { $qb = $this->manager->createQuery($class); switch ($strategy) { case 'contains': $qb->contains($properties, $terms); break; // ... Rest of strategies code } return $qb->limit(20)->exec(); } }
Finally, tag this service with acseo_select_autocomplete.data_provider
.
services: App\Form\Autocomplete\DataProvider\CustomDataProvider: autowire: true tags: ['acseo_select_autocomplete.data_provider']
Now, this provider will be invoked by default if it supports the given model class.