Skip to content

Commit

Permalink
[Admin][UX] Filter translatable autocomplete results based on current…
Browse files Browse the repository at this point in the history
… locale

[Admin][DX] Extract translatable autocomplete processing to a separate form type
  • Loading branch information
NoResponseMate authored and GSadee committed May 20, 2024
1 parent 71ff79c commit f083d82
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;

#[AsEntityAutocompleteField(
alias: 'sylius_admin_product_attribute',
Expand All @@ -35,7 +34,6 @@ public function configureOptions(OptionsResolver $resolver): void
'class' => $this->productAttributeClass,
'choice_name' => 'name',
'choice_value' => 'code',
'searchable_fields' => ['code', 'translations.name'],
]);
}

Expand All @@ -46,6 +44,6 @@ public function getBlockPrefix(): string

public function getParent(): string
{
return BaseEntityAutocompleteType::class;
return TranslatableAutocompleteType::class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;

#[AsEntityAutocompleteField(
alias: 'sylius_admin_product',
Expand All @@ -35,7 +34,6 @@ public function configureOptions(OptionsResolver $resolver): void
'class' => $this->productClass,
'choice_name' => 'name',
'choice_value' => 'code',
'searchable_fields' => ['code', 'translations.name'],
]);
}

Expand All @@ -46,6 +44,6 @@ public function getBlockPrefix(): string

public function getParent(): string
{
return BaseEntityAutocompleteType::class;
return TranslatableAutocompleteType::class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;

#[AsEntityAutocompleteField(
alias: 'sylius_admin_product_variant',
Expand All @@ -35,7 +34,6 @@ public function configureOptions(OptionsResolver $resolver): void
'class' => $this->productVariantClass,
'choice_name' => 'descriptor',
'choice_value' => 'code',
'searchable_fields' => ['code', 'translations.name'],
]);
}

Expand All @@ -46,6 +44,6 @@ public function getBlockPrefix(): string

public function getParent(): string
{
return BaseEntityAutocompleteType::class;
return TranslatableAutocompleteType::class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;

#[AsEntityAutocompleteField(
alias: 'sylius_admin_taxon',
Expand All @@ -35,7 +34,6 @@ public function configureOptions(OptionsResolver $resolver): void
'class' => $this->taxonClass,
'choice_label' => 'fullname',
'choice_value' => 'code',
'searchable_fields' => ['code', 'translations.name'],
]);
}

Expand All @@ -46,6 +44,6 @@ public function getBlockPrefix(): string

public function getParent(): string
{
return BaseEntityAutocompleteType::class;
return TranslatableAutocompleteType::class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\AdminBundle\Form\Type;

use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\UX\Autocomplete\Form\BaseEntityAutocompleteType;

final class TranslatableAutocompleteType extends AbstractType
{
public const ENTITY_ALIAS = 'entity';

public const TRANSLATION_ALIAS = 'translation';

public function __construct(
private readonly LocaleContextInterface $localeContext,
) {
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('entity_fields', ['code']);
$resolver->setAllowedTypes('entity_fields', 'array');
$resolver->setNormalizer('entity_fields', fn (Options $options, array $entityFields) => array_map(
fn (string $field) => self::ENTITY_ALIAS . '.' . $field,
$entityFields,
));

$resolver->setDefault('translation_fields', ['name']);
$resolver->setAllowedTypes('translation_fields', 'array');
$resolver->setNormalizer('translation_fields', fn (Options $options, array $translationFields) => array_map(
fn (string $field) => self::TRANSLATION_ALIAS . '.' . $field,
$translationFields,
));

$resolver->setDefault('locale_code', $this->localeContext->getLocaleCode());
$resolver->setAllowedTypes('locale_code', ['string', 'null']);

$resolver->setDefault('filter_query', null);
$resolver->setNormalizer(
'filter_query',
fn (Options $options, ?callable $filterQuery) => $this->normalizeFilterQuery($options, $filterQuery),
);
}

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

public function getParent(): string
{
return BaseEntityAutocompleteType::class;
}

private function normalizeFilterQuery(Options $options, ?callable $filterQuery): callable
{
return function (
QueryBuilder $builder,
string $query,
EntityRepository $repository,
) use ($options, $filterQuery): void {
$expr = $builder->expr();

$entityConditions = self::getComparisons($expr, $options['entity_fields']);
$translationConditions = self::getComparisons($expr, $options['translation_fields']);

$builder
->innerJoin(
sprintf('%s.translations', self::ENTITY_ALIAS),
self::TRANSLATION_ALIAS,
Expr\Join::WITH,
sprintf('%s.locale = :locale', self::TRANSLATION_ALIAS),
)
->andWhere(
$expr->orX(
...$entityConditions,
...$translationConditions,
),
)
->setParameter('locale', $options['locale_code'])
->setParameter('query', '%' . $query . '%')
;

if (null !== $filterQuery) {
$filterQuery($builder, $query, $repository);
}
};
}

/**
* @param array<string> $fields
*
* @return iterable<Expr\Comparison>
*/
private static function getComparisons(Expr $expr, array $fields): iterable
{
foreach ($fields as $field) {
yield $expr->like($field, ':query');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
<tag name="form.type_extension" />
</service>

<service id="sylius_admin.form.type.translatable_autocomplete" class="Sylius\Bundle\AdminBundle\Form\Type\TranslatableAutocompleteType">
<argument type="service" id="sylius.context.locale" />
<tag name="form.type" />
</service>

<service id="sylius_admin.form.type.product_autocomplete" class="Sylius\Bundle\AdminBundle\Form\Type\ProductAutocompleteType">
<argument>%sylius.model.product.class%</argument>
<tag name="form.type" />
Expand Down

0 comments on commit f083d82

Please sign in to comment.