Skip to content

Commit 36c5f28

Browse files
committed
Merge branch '4.x' into 5.x
* 4.x: Minor tweak Fix filter to use translated labels for enums implementing TranslatableInterface
2 parents 6980d4d + db8abdc commit 36c5f28

5 files changed

Lines changed: 143 additions & 1 deletion

File tree

src/Filter/ChoiceFilter.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,44 @@ public function setChoices(array $choices): self
3939
}
4040

4141
/**
42-
* @param array<string|TranslatableInterface> $choiceGenerator
42+
* @param array<string|TranslatableInterface|\UnitEnum> $choiceGenerator
4343
*/
4444
public function setTranslatableChoices(array $choiceGenerator): self
4545
{
46+
// support passing a list of enum cases (e.g. MyEnum::cases()) when the
47+
// enum implements TranslatableInterface. In that case, the submitted
48+
// value must be the enum backing value (or its name for UnitEnum),
49+
// otherwise the keys (0, 1, 2, ...) of the list would be submitted
50+
// and the query would never match any row.
51+
if (array_is_list($choiceGenerator) && [] !== $choiceGenerator && $this->areAllEnums($choiceGenerator)) {
52+
$choices = [];
53+
foreach ($choiceGenerator as $case) {
54+
$key = $case instanceof \BackedEnum ? $case->value : $case->name;
55+
$choices[$key] = $case;
56+
}
57+
$choiceGenerator = $choices;
58+
}
59+
4660
$this->dto->setFormTypeOption('value_type_options.choices', array_keys($choiceGenerator));
4761
$this->dto->setFormTypeOption('value_type_options.choice_label', static fn ($value) => $choiceGenerator[$value]);
4862

4963
return $this;
5064
}
5165

66+
/**
67+
* @param array<mixed> $values
68+
*/
69+
private function areAllEnums(array $values): bool
70+
{
71+
foreach ($values as $value) {
72+
if (!$value instanceof \UnitEnum) {
73+
return false;
74+
}
75+
}
76+
77+
return true;
78+
}
79+
5280
public function renderExpanded(bool $isExpanded = true): self
5381
{
5482
$this->dto->setFormTypeOption('value_type_options.expanded', $isExpanded);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter;
4+
5+
use EasyCorp\Bundle\EasyAdminBundle\Filter\ChoiceFilter;
6+
use EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures\BackedEnumChoice;
7+
use EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures\TranslatableBackedEnumChoice;
8+
use EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures\TranslatableUnitEnumChoice;
9+
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\Translation\TranslatableMessage;
11+
12+
class ChoiceFilterTest extends TestCase
13+
{
14+
public function testSetTranslatableChoicesWithTranslatableMessages(): void
15+
{
16+
$filter = ChoiceFilter::new('status')->setTranslatableChoices([
17+
'open' => new TranslatableMessage('Open'),
18+
'closed' => new TranslatableMessage('Closed'),
19+
]);
20+
$dto = $filter->getAsDto();
21+
22+
$this->assertSame(['open', 'closed'], $dto->getFormTypeOption('value_type_options.choices'));
23+
24+
$choiceLabel = $dto->getFormTypeOption('value_type_options.choice_label');
25+
$this->assertIsCallable($choiceLabel);
26+
$this->assertInstanceOf(TranslatableMessage::class, $choiceLabel('open'));
27+
$this->assertInstanceOf(TranslatableMessage::class, $choiceLabel('closed'));
28+
}
29+
30+
public function testSetTranslatableChoicesWithTranslatableBackedEnumCases(): void
31+
{
32+
$filter = ChoiceFilter::new('status')->setTranslatableChoices(TranslatableBackedEnumChoice::cases());
33+
$dto = $filter->getAsDto();
34+
35+
// the submitted values must be the enum backing values, not the list indices (0, 1, ...)
36+
$this->assertSame(['draft', 'published'], $dto->getFormTypeOption('value_type_options.choices'));
37+
38+
$choiceLabel = $dto->getFormTypeOption('value_type_options.choice_label');
39+
$this->assertIsCallable($choiceLabel);
40+
$this->assertSame(TranslatableBackedEnumChoice::Draft, $choiceLabel('draft'));
41+
$this->assertSame(TranslatableBackedEnumChoice::Published, $choiceLabel('published'));
42+
}
43+
44+
public function testSetTranslatableChoicesWithBackedEnumCases(): void
45+
{
46+
$filter = ChoiceFilter::new('status')->setTranslatableChoices(BackedEnumChoice::cases());
47+
$dto = $filter->getAsDto();
48+
49+
$this->assertSame(['draft', 'published'], $dto->getFormTypeOption('value_type_options.choices'));
50+
51+
$choiceLabel = $dto->getFormTypeOption('value_type_options.choice_label');
52+
$this->assertSame(BackedEnumChoice::Draft, $choiceLabel('draft'));
53+
}
54+
55+
public function testSetTranslatableChoicesWithTranslatableUnitEnumCases(): void
56+
{
57+
$filter = ChoiceFilter::new('status')->setTranslatableChoices(TranslatableUnitEnumChoice::cases());
58+
$dto = $filter->getAsDto();
59+
60+
$this->assertSame(['Draft', 'Published'], $dto->getFormTypeOption('value_type_options.choices'));
61+
62+
$choiceLabel = $dto->getFormTypeOption('value_type_options.choice_label');
63+
$this->assertSame(TranslatableUnitEnumChoice::Draft, $choiceLabel('Draft'));
64+
}
65+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures;
4+
5+
enum BackedEnumChoice: string
6+
{
7+
case Draft = 'draft';
8+
case Published = 'published';
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures;
4+
5+
use Symfony\Contracts\Translation\TranslatableInterface;
6+
use Symfony\Contracts\Translation\TranslatorInterface;
7+
8+
enum TranslatableBackedEnumChoice: string implements TranslatableInterface
9+
{
10+
case Draft = 'draft';
11+
case Published = 'published';
12+
13+
public function trans(TranslatorInterface $translator, ?string $locale = null): string
14+
{
15+
return match ($this) {
16+
self::Draft => $translator->trans('TranslatableBackedEnumChoice.draft', locale: $locale),
17+
self::Published => $translator->trans('TranslatableBackedEnumChoice.published', locale: $locale),
18+
};
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Unit\Filter\Fixtures;
4+
5+
use Symfony\Contracts\Translation\TranslatableInterface;
6+
use Symfony\Contracts\Translation\TranslatorInterface;
7+
8+
enum TranslatableUnitEnumChoice implements TranslatableInterface
9+
{
10+
case Draft;
11+
case Published;
12+
13+
public function trans(TranslatorInterface $translator, ?string $locale = null): string
14+
{
15+
return match ($this) {
16+
self::Draft => $translator->trans('TranslatableUnitEnumChoice.draft', locale: $locale),
17+
self::Published => $translator->trans('TranslatableUnitEnumChoice.published', locale: $locale),
18+
};
19+
}
20+
}

0 commit comments

Comments
 (0)