Skip to content

Commit 4e39c0d

Browse files
committed
bug #7519 Fix filter label translation when field is hidden via hideOnIndex (Amoifr)
This PR was submitted for the 5.x branch but it was merged into the 4.x branch instead. Discussion ---------- Fix filter label translation when field is hidden via hideOnIndex ## Summary - When `useEntityTranslation` is enabled and a field is hidden via `hideOnIndex()`, the corresponding filter label was not translated - The issue was in `CommonConfigurator`: when `$fieldDto` is null (field not in collection), it fell back to `LabelGenerator::humanize()` instead of using the entity translation system - Now `CommonConfigurator` uses `EntityTranslationIdGenerator` to generate the proper translation key when the field is not available ## Test plan - [x] Added unit tests for `CommonConfigurator` - [x] Tests cover: field label available, TranslatableMessage label, humanized label (no entity translations), entity translation key (with entity translations enabled) - [ ] Manual testing with `useEntityTranslation` enabled and a field hidden via `hideOnIndex()` Fix #7512 Commits ------- 4db240e Fix filter label translation when field is hidden via hideOnIndex
2 parents 0d158f0 + 4db240e commit 4e39c0d

3 files changed

Lines changed: 159 additions & 2 deletions

File tree

config/services.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@
337337
->set(ChoiceFilterConfigurator::class)
338338

339339
->set(CommonFilterConfigurator::class)
340+
->arg(0, service(EntityTranslationIdGeneratorInterface::class))
340341
->tag(EasyAdminExtension::TAG_FILTER_CONFIGURATOR, ['priority' => 9999])
341342

342343
->set(ComparisonFilterConfigurator::class)

src/Filter/Configurator/CommonConfigurator.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
66
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Filter\FilterConfiguratorInterface;
7+
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Translation\EntityTranslationIdGeneratorInterface;
78
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
89
use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
910
use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDto;
@@ -13,8 +14,13 @@
1314
/**
1415
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
1516
*/
16-
final class CommonConfigurator implements FilterConfiguratorInterface
17+
final readonly class CommonConfigurator implements FilterConfiguratorInterface
1718
{
19+
public function __construct(
20+
private EntityTranslationIdGeneratorInterface $entityTranslationIdGenerator,
21+
) {
22+
}
23+
1824
public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): bool
1925
{
2026
return true;
@@ -27,7 +33,15 @@ public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $
2733
if ($fieldLabel instanceof TranslatableMessage) {
2834
$fieldLabel = $fieldLabel->getMessage();
2935
}
30-
$label = $fieldLabel ?? LabelGenerator::humanize($filterDto->getProperty());
36+
37+
if (null !== $fieldLabel) {
38+
$label = $fieldLabel;
39+
} elseif ($context->isUseEntityTranslations()) {
40+
$label = $this->entityTranslationIdGenerator->generateForProperty($entityDto->getFqcn(), $filterDto->getProperty());
41+
} else {
42+
$label = LabelGenerator::humanize($filterDto->getProperty());
43+
}
44+
3145
$filterDto->setLabel($label);
3246
}
3347
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Configurator;
4+
5+
use Doctrine\ORM\Mapping\ClassMetadata;
6+
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
7+
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
8+
use EasyCorp\Bundle\EasyAdminBundle\Context\DashboardContext;
9+
use EasyCorp\Bundle\EasyAdminBundle\Context\I18nContext;
10+
use EasyCorp\Bundle\EasyAdminBundle\Context\RequestContext;
11+
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Translation\EntityTranslationIdGeneratorInterface;
12+
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
13+
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
14+
use EasyCorp\Bundle\EasyAdminBundle\Filter\Configurator\CommonConfigurator;
15+
use EasyCorp\Bundle\EasyAdminBundle\Filter\TextFilter;
16+
use PHPUnit\Framework\TestCase;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\Translation\TranslatableMessage;
19+
20+
class CommonConfiguratorTest extends TestCase
21+
{
22+
private EntityDto $entityDto;
23+
24+
protected function setUp(): void
25+
{
26+
$metadata = new ClassMetadata('App\Entity\Product');
27+
$metadata->setIdentifier(['id']);
28+
$this->entityDto = new EntityDto('App\Entity\Product', $metadata);
29+
}
30+
31+
public function testSupportsAllFilters(): void
32+
{
33+
$configurator = $this->createConfigurator();
34+
$filter = TextFilter::new('name');
35+
$filterDto = $filter->getAsDto();
36+
$adminContext = $this->createAdminContext();
37+
38+
$this->assertTrue($configurator->supports($filterDto, null, $this->entityDto, $adminContext));
39+
}
40+
41+
public function testConfigureUsesFieldLabelWhenAvailable(): void
42+
{
43+
$configurator = $this->createConfigurator();
44+
$filter = TextFilter::new('name');
45+
$filterDto = $filter->getAsDto();
46+
47+
$field = TextField::new('name', 'Product Name');
48+
$fieldDto = $field->getAsDto();
49+
50+
$adminContext = $this->createAdminContext();
51+
52+
$configurator->configure($filterDto, $fieldDto, $this->entityDto, $adminContext);
53+
54+
$this->assertSame('Product Name', $filterDto->getLabel());
55+
}
56+
57+
public function testConfigureUsesFieldTranslatableMessageLabel(): void
58+
{
59+
$configurator = $this->createConfigurator();
60+
$filter = TextFilter::new('name');
61+
$filterDto = $filter->getAsDto();
62+
63+
$field = TextField::new('name');
64+
$fieldDto = $field->getAsDto();
65+
$fieldDto->setLabel(new TranslatableMessage('product.name'));
66+
67+
$adminContext = $this->createAdminContext();
68+
69+
$configurator->configure($filterDto, $fieldDto, $this->entityDto, $adminContext);
70+
71+
$this->assertSame('product.name', $filterDto->getLabel());
72+
}
73+
74+
public function testConfigureHumanizesLabelWhenFieldIsNullAndEntityTranslationsDisabled(): void
75+
{
76+
$configurator = $this->createConfigurator();
77+
$filter = TextFilter::new('productName');
78+
$filterDto = $filter->getAsDto();
79+
80+
$adminContext = $this->createAdminContext(useEntityTranslations: false);
81+
82+
$configurator->configure($filterDto, null, $this->entityDto, $adminContext);
83+
84+
$this->assertSame('Product Name', $filterDto->getLabel());
85+
}
86+
87+
public function testConfigureUsesEntityTranslationWhenFieldIsNullAndEntityTranslationsEnabled(): void
88+
{
89+
$expectedTranslationId = 'entities.App\\Entity\\Product.properties.productName';
90+
91+
$generator = $this->createMock(EntityTranslationIdGeneratorInterface::class);
92+
$generator->expects($this->once())
93+
->method('generateForProperty')
94+
->with('App\Entity\Product', 'productName')
95+
->willReturn($expectedTranslationId);
96+
97+
$configurator = new CommonConfigurator($generator);
98+
$filter = TextFilter::new('productName');
99+
$filterDto = $filter->getAsDto();
100+
101+
$adminContext = $this->createAdminContext(useEntityTranslations: true);
102+
103+
$configurator->configure($filterDto, null, $this->entityDto, $adminContext);
104+
105+
$this->assertSame($expectedTranslationId, $filterDto->getLabel());
106+
}
107+
108+
public function testConfigureDoesNotOverrideExplicitFilterLabel(): void
109+
{
110+
$configurator = $this->createConfigurator();
111+
$filter = TextFilter::new('name')->setLabel('Custom Label');
112+
$filterDto = $filter->getAsDto();
113+
114+
$adminContext = $this->createAdminContext();
115+
116+
$configurator->configure($filterDto, null, $this->entityDto, $adminContext);
117+
118+
$this->assertSame('Custom Label', $filterDto->getLabel());
119+
}
120+
121+
private function createConfigurator(): CommonConfigurator
122+
{
123+
$generator = $this->createMock(EntityTranslationIdGeneratorInterface::class);
124+
125+
return new CommonConfigurator($generator);
126+
}
127+
128+
private function createAdminContext(bool $useEntityTranslations = false): AdminContext
129+
{
130+
$dashboardDto = Dashboard::new()->getAsDto();
131+
if ($useEntityTranslations) {
132+
$dashboardDto->setUseEntityTranslations(true);
133+
}
134+
135+
return AdminContext::forTesting(
136+
RequestContext::forTesting(new Request()),
137+
null,
138+
DashboardContext::forTesting($dashboardDto),
139+
I18nContext::forTesting('en', 'ltr')
140+
);
141+
}
142+
}

0 commit comments

Comments
 (0)