x-one/autocomplete-bundle

There is no license information available for the latest version (v2.1.3) of this package.

Integrates Symfony UX Autocomplete with additional enhancements

Installs: 828

Dependents: 0

Suggesters: 0

Security: 0

Type:symfony-bundle

v2.1.3 2025-02-03 11:06 UTC

This package is auto-updated.

Last update: 2025-02-03 11:06:40 UTC


README

Paczka integrująca Symfony UX Autocomplete, oferująca pomocnicze klasy rozwijające funkcjonalność oraz ułatwiające definicję pól typu autocomplete.

Instalacja

Dodaj prywatne repozytorium do pliku composer.json:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "git@bitbucket.org:majchw/autocomplete-bundle.git"
        }
    ]
}

Uwaga: wymagana będzie autoryzacja poprzez klucz SSH — instrukcja.

Następnie, zainstaluj paczkę:

composer require x-one/autocomplete-bundle

Symfony Flex powinien automatycznie dodać nowe wpisy do plików config/bundles.php oraz assets/controllers.json:

return [
    // ...
    XOne\Bundle\AutocompleteBundle\XOneAutocompleteBundle::class => ['all' => true],
];
{
    "controllers": {
        "@x-one/autocomplete-bundle": {
            "autocomplete": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    }
}

Finalnie, przebuduj assety:

yarn install --force
yarn watch

Różnice w stosunku do Symfony UX Autocomplete

Ogólna definicja pól oraz możliwości konfiguracji znajdują się w oficjalnej dokumentacji Symfony UX Autocomplete.

Definicja pola autocomplete

Klasy formularzy definiujące pola typu autocomplete powinny rozszerzać klasę AbstractEntityAutocompleteType:

// src/Form/Type/ProductAutocompleteFormType.php

use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\ParentEntityAutocompleteType;
use XOne\Bundle\AutocompleteBundle\Form\Type\AbstractEntityAutocompleteType;

#[AsEntityAutocompleteField]
class ProductAutocompleteFormType extends AbstractEntityAutocompleteType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'class' => Product::class,
            'choice_label' => 'name',
        ]);
    }
}

Dzięki temu nie trzeba podawać getParent(), które w rozszerzanej klasie zwraca AutocompleteEntityType zamiast ParentEntityAutocompleteType przedstawianego w oficjalnej dokumentacji.

Obsługa filtracji w repozytoriach

Repozytoria mogą teraz implementować interfejs AutocompleteRepositoryInterface, dzięki czemu będą automatyczne wykorzystywane w procesie filtracji. Przykładowo:

// src/Repository/ProductRepository.php

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use XOne\Bundle\AutocompleteBundle\Repository\AutocompleteRepositoryInterface;

class ProductRepository extends ServiceEntityRepository implements AutocompleteRepositoryInterface
{
    public function addAutocompleteCriteria(QueryBuilder $queryBuilder, array $parameters): void
    {
        $rootAlias = current($queryBuilder->getRootAliases());

        if ($query = $parameters['query']) {
            $queryBuilder
                ->andWhere($queryBuilder->expr()->like("$rootAlias.name", ':query'))
                ->setParameter('query', "%$query%");
        }
    }
}

Tablica $parameters zawiera parametry query z URL. Wyszukiwana wartość zawsze dostępna jest pod kluczem query.

Uwaga: zwrócenie QueryBuilder w opcji filter_query pola autocomplete pomija repozytorium!

Przekazywanie parametrów

Największym problemem Symfony UX Autocomplete jest brak możliwości przekazania dodatkowych parametrów do podzapytania — ponieważ opcje, jakie przekażemy do formularza, są utracone w momencie wysłania żądania AJAX dla autocomplete.

W celu rozwiązania tego problemu bundle wprowadza nową opcję autocomplete_parameters:

// src/Form/Type/CategoryFormType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class CategoryFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        /** @var null|Category $category */
        $category = $options['data'];

        $builder
            ->add('products', ProductAutocompleteFormType::class, [
                'autocomplete_parameters' => [
                    'category_id' => $data?->getId(),
                ],
            ])
        ;
    }
}

Parametry te zostają przekazywane w URL zapytań AJAX. W powyższym przykładzie (zakładając, że przekazane ID kategorii to "1") będzie to:

/autocomplete/product?query=&product_id=1

Dzięki temu parametr ten można wyciągnąć w repozytorium:

// src/Repository/ProductRepository.php

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use XOne\Bundle\AutocompleteBundle\Repository\AutocompleteRepositoryInterface;

class ProductRepository extends ServiceEntityRepository implements AutocompleteRepositoryInterface
{
    public function addAutocompleteCriteria(QueryBuilder $queryBuilder, array $parameters): void
    {
        $rootAlias = current($queryBuilder->getRootAliases());

        if (isset($categoryId = $parameters['category_id'] ?? null)) {
            $queryBuilder
                ->andWhere($queryBuilder->expr()->eq("$rootAlias.category", ':category'))
                ->setParameter('category', $categoryId);
        }
    }
}

Parametry o wartościach z innych pól formularza

W przypadku, gdy wartość parametr ma być pobrany z innego pola formularza, do opcji autocomplete_parameters można przekazać konkretne pole formularza.

Poniższy przykład zakłada, że mamy formularz dla produktu, w którym możemy wybrać jego klasę oraz grupę. Grupa może być wybrana tylko spośród tych, które należą do wybranej klasy.

// src/Form/Type/ProductFormType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class ProductFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('productClass', ProductClassAutocompleteFormType::class)
            ->add('productGroup', ProductGroupAutocompleteFormType::class, [
                'autocomplete_parameters' => [
                    'product_class_id' => $builder->get('productClass'),
                ],
            ])
        ;
    }
}

Jeśli nie ma opcji na pobranie pola formularza (bo powstaje on później w procesie budowania), można przekazać instancję FormReference, w której podajemy jedynie nazwę pola:

// src/Form/Type/ProductFormType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use XOne\Bundle\AutocompleteBundle\Form\FormReference;

class ProductFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('productClass', ProductClassAutocompleteFormType::class)
            ->add('productGroup', ProductGroupAutocompleteFormType::class, [
                'autocomplete_parameters' => [
                    'product_class_id' => new FormReference('productClass'),
                ],
            ])
        ;
    }
}

Jeśli natomiast mamy pewność co do selektora CSS pola formularza, możemy przekazać go bezpośrednio:

// src/Form/Type/ProductFormType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use XOne\Bundle\AutocompleteBundle\Form\FormReference;

class ProductFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('productClass', ProductClassAutocompleteFormType::class)
            ->add('productGroup', ProductGroupAutocompleteFormType::class, [
                'autocomplete_parameters' => [
                    'product_class_id' => '#product_form_productClass',
                ],
            ])
        ;
    }
}

W powyższych przypadkach, do żądania AJAX pobierającego grupy produktów możliwe do wyboru, zostanie doklejony parametr product_class_id z wartością wybraną w polu klasy produktu.

Przetwarzanie parametrów

Jeśli zaistnieje potrzeba, aby przetworzyć tablicę parametrów przed przekazaniem jej do repozytorium, w klasie typu pola autocomplete można zaimplementować interfejs AutocompleteParametersTransformerInterface:

// src/Form/Type/ProductAutocompleteFormType.php

use Symfony\Component\Security\Core\Security;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\ParentEntityAutocompleteType;
use XOne\Bundle\AutocompleteBundle\Form\AutocompleteParametersTransformerInterface;
use XOne\Bundle\AutocompleteBundle\Form\Type\AbstractEntityAutocompleteType;

#[AsEntityAutocompleteField]
class ProductAutocompleteFormType extends AbstractEntityAutocompleteType implements AutocompleteParametersTransformerInterface
{
    public function __construct(
        private Security $security,
    ) {
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'class' => Product::class,
            'choice_label' => 'name',
        ]);
    }

    public function transformAutocompleteParameters(array $parameters): array
    {
        // Add defaults...
        $parameters += [
	        'category_id' => null,
        ];

        // Cast to proper types if needed...
        if ($parameters['category_id']) {
            $parameters['category_id'] = (int) $parameters['category_id'];
        }

        // Add additional parameters...
        $parameters['user_id'] = $this->security->getUser()?->getId();

        return $parameters;
    }
}

Metoda transformAutocompleteParameters() otrzymuje tablicę parametrów (wraz z wartościami), które przyszły podczas żądania AJAX.

Rozwój bundle

Upewnij się, że testy nie zwracają żadnego błędu, oraz kod jest poprawnie sformatowany:

composer run-script pre-commit-checks

Wersjonowanie

Aby paczkę dało się zaktualizować przez composera, po zmergowaniu zmian do głównego brancha, należy utworzyć tag w formacie vX.Y.Z, np.

git tag -a v1.1.0 -m "Version v1.1.0"
git push origin --tags