diff --git a/config/services.php b/config/services.php index b620fde529..5fc2805a75 100644 --- a/config/services.php +++ b/config/services.php @@ -318,6 +318,7 @@ ->set(ChoiceFilterConfigurator::class) ->set(CommonFilterConfigurator::class) + ->arg(0, service(EntityTranslationIdGeneratorInterface::class)) ->tag(EasyAdminExtension::TAG_FILTER_CONFIGURATOR, ['priority' => 9999]) ->set(ComparisonFilterConfigurator::class) diff --git a/src/Filter/Configurator/CommonConfigurator.php b/src/Filter/Configurator/CommonConfigurator.php index 1516d41487..f85c2279b9 100644 --- a/src/Filter/Configurator/CommonConfigurator.php +++ b/src/Filter/Configurator/CommonConfigurator.php @@ -4,6 +4,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext; use EasyCorp\Bundle\EasyAdminBundle\Contracts\Filter\FilterConfiguratorInterface; +use EasyCorp\Bundle\EasyAdminBundle\Contracts\Translation\EntityTranslationIdGeneratorInterface; use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto; use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDto; @@ -13,8 +14,13 @@ /** * @author Javier Eguiluz */ -final class CommonConfigurator implements FilterConfiguratorInterface +final readonly class CommonConfigurator implements FilterConfiguratorInterface { + public function __construct( + private EntityTranslationIdGeneratorInterface $entityTranslationIdGenerator, + ) { + } + public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): bool { return true; @@ -27,7 +33,15 @@ public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $ if ($fieldLabel instanceof TranslatableMessage) { $fieldLabel = $fieldLabel->getMessage(); } - $label = $fieldLabel ?? LabelGenerator::humanize($filterDto->getProperty()); + + if (null !== $fieldLabel) { + $label = $fieldLabel; + } elseif ($context->isUseEntityTranslations()) { + $label = $this->entityTranslationIdGenerator->generateForProperty($entityDto->getFqcn(), $filterDto->getProperty()); + } else { + $label = LabelGenerator::humanize($filterDto->getProperty()); + } + $filterDto->setLabel($label); } } diff --git a/tests/Unit/Filter/Configurator/CommonConfiguratorTest.php b/tests/Unit/Filter/Configurator/CommonConfiguratorTest.php new file mode 100644 index 0000000000..986494226b --- /dev/null +++ b/tests/Unit/Filter/Configurator/CommonConfiguratorTest.php @@ -0,0 +1,142 @@ +setIdentifier(['id']); + $this->entityDto = new EntityDto('App\Entity\Product', $metadata); + } + + public function testSupportsAllFilters(): void + { + $configurator = $this->createConfigurator(); + $filter = TextFilter::new('name'); + $filterDto = $filter->getAsDto(); + $adminContext = $this->createAdminContext(); + + $this->assertTrue($configurator->supports($filterDto, null, $this->entityDto, $adminContext)); + } + + public function testConfigureUsesFieldLabelWhenAvailable(): void + { + $configurator = $this->createConfigurator(); + $filter = TextFilter::new('name'); + $filterDto = $filter->getAsDto(); + + $field = TextField::new('name', 'Product Name'); + $fieldDto = $field->getAsDto(); + + $adminContext = $this->createAdminContext(); + + $configurator->configure($filterDto, $fieldDto, $this->entityDto, $adminContext); + + $this->assertSame('Product Name', $filterDto->getLabel()); + } + + public function testConfigureUsesFieldTranslatableMessageLabel(): void + { + $configurator = $this->createConfigurator(); + $filter = TextFilter::new('name'); + $filterDto = $filter->getAsDto(); + + $field = TextField::new('name'); + $fieldDto = $field->getAsDto(); + $fieldDto->setLabel(new TranslatableMessage('product.name')); + + $adminContext = $this->createAdminContext(); + + $configurator->configure($filterDto, $fieldDto, $this->entityDto, $adminContext); + + $this->assertSame('product.name', $filterDto->getLabel()); + } + + public function testConfigureHumanizesLabelWhenFieldIsNullAndEntityTranslationsDisabled(): void + { + $configurator = $this->createConfigurator(); + $filter = TextFilter::new('productName'); + $filterDto = $filter->getAsDto(); + + $adminContext = $this->createAdminContext(useEntityTranslations: false); + + $configurator->configure($filterDto, null, $this->entityDto, $adminContext); + + $this->assertSame('Product Name', $filterDto->getLabel()); + } + + public function testConfigureUsesEntityTranslationWhenFieldIsNullAndEntityTranslationsEnabled(): void + { + $expectedTranslationId = 'entities.App\\Entity\\Product.properties.productName'; + + $generator = $this->createMock(EntityTranslationIdGeneratorInterface::class); + $generator->expects($this->once()) + ->method('generateForProperty') + ->with('App\Entity\Product', 'productName') + ->willReturn($expectedTranslationId); + + $configurator = new CommonConfigurator($generator); + $filter = TextFilter::new('productName'); + $filterDto = $filter->getAsDto(); + + $adminContext = $this->createAdminContext(useEntityTranslations: true); + + $configurator->configure($filterDto, null, $this->entityDto, $adminContext); + + $this->assertSame($expectedTranslationId, $filterDto->getLabel()); + } + + public function testConfigureDoesNotOverrideExplicitFilterLabel(): void + { + $configurator = $this->createConfigurator(); + $filter = TextFilter::new('name')->setLabel('Custom Label'); + $filterDto = $filter->getAsDto(); + + $adminContext = $this->createAdminContext(); + + $configurator->configure($filterDto, null, $this->entityDto, $adminContext); + + $this->assertSame('Custom Label', $filterDto->getLabel()); + } + + private function createConfigurator(): CommonConfigurator + { + $generator = $this->createMock(EntityTranslationIdGeneratorInterface::class); + + return new CommonConfigurator($generator); + } + + private function createAdminContext(bool $useEntityTranslations = false): AdminContext + { + $dashboardDto = Dashboard::new()->getAsDto(); + if ($useEntityTranslations) { + $dashboardDto->setUseEntityTranslations(true); + } + + return AdminContext::forTesting( + RequestContext::forTesting(new Request()), + null, + DashboardContext::forTesting($dashboardDto), + I18nContext::forTesting('en', 'ltr') + ); + } +}