Skip to content

Commit

Permalink
support also native enums
Browse files Browse the repository at this point in the history
  • Loading branch information
mhujer committed Sep 8, 2022
1 parent 51358be commit 454d3e3
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 25 deletions.
10 changes: 8 additions & 2 deletions src/Enum/EnumTranslator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@ public function __construct(
}

public function translateEnum(
Enum $enum,
Enum|\BackedEnum $enum,
string $translationDomain = 'enums'
): string
{
if ($enum instanceof MultiEnum) {
throw new \Exception('Only single enums are supported for now.');
}

$translateKey = get_class($enum) . ':' . $enum->getValue();
if ($enum instanceof Enum) {
$translateKey = get_class($enum) . ':' . $enum->getValue();
} elseif ($enum instanceof \BackedEnum) {
$translateKey = get_class($enum) . ':' . (string) $enum->value;
} else {
throw new \Exception(sprintf('Unexpected enum class "%s"', get_class($enum)));
}

return $this->translator->trans($translateKey, [], $translationDomain);
}
Expand Down
25 changes: 17 additions & 8 deletions src/FormType/EnumType/EnumTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,38 @@ public function __construct(string $enumClassName)
{
$this->enumClassName = $enumClassName;

if (!is_subclass_of($enumClassName, Enum::class)) {
if (!is_subclass_of($enumClassName, Enum::class) && !is_subclass_of($enumClassName, \BackedEnum::class)) {
throw new \Exception(sprintf(
'"%s" is not a subclass of "%s"',
'"%s" is neither a subclass of "%s" or "%s"',
$enumClassName,
Enum::class
Enum::class,
\BackedEnum::class,
));
}
}

/**
* @param \Consistence\Enum\Enum|null $enum
* @param \Consistence\Enum\Enum|\BackedEnum|null $enum
*/
public function transform($enum): ?string // phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
{
Type::checkType($enum, Enum::class . '|null');
Type::checkType($enum, Enum::class . '|' . \BackedEnum::class . '|null');

if ($enum === null) {
return null;
}

return (string) $enum->getValue();
if ($enum instanceof Enum) {
return (string) $enum->getValue();
}

return (string) $enum->value;
}

/**
* @param string|null $enumValue
*/
public function reverseTransform($enumValue): ?Enum // phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function reverseTransform($enumValue): Enum|\BackedEnum|null // phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
{
Type::checkType($enumValue, 'string|int|null');

Expand All @@ -51,7 +56,11 @@ public function reverseTransform($enumValue): ?Enum // phpcs:ignore SlevomatCodi

$enumClass = $this->enumClassName;

return $enumClass::get($enumValue);
if (is_subclass_of($enumClass, Enum::class)) {
return $enumClass::get($enumValue);
}

return $enumClass::from($enumValue);
}

}
42 changes: 30 additions & 12 deletions src/FormType/EnumType/EnumTypeChoiceLoaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,41 @@ class EnumTypeChoiceLoaderFactory
*/
public static function createLoader(string $enumClassName): ChoiceLoaderInterface
{
if (!is_subclass_of($enumClassName, Enum::class)) {
if (!is_subclass_of($enumClassName, Enum::class) && !is_subclass_of($enumClassName, \BackedEnum::class)) {
throw new \Exception(sprintf(
'"%s" is not a subclass of "%s"',
'"%s" is neither a subclass of "%s" or "%s"',
$enumClassName,
Enum::class
Enum::class,
\BackedEnum::class,
));
}

/** @var mixed[] $values */
$values = $enumClassName::getAvailableValues();

$values = ArrayType::mapByCallback($values, function (KeyValuePair $keyValuePair) use ($enumClassName) {
return new KeyValuePair(
$enumClassName . ':' . $keyValuePair->getValue(),
$keyValuePair->getValue()
);
});
if (is_subclass_of($enumClassName, Enum::class)) {
/** @var mixed[] $values */
$values = $enumClassName::getAvailableValues();

$values = ArrayType::mapByCallback($values, function (KeyValuePair $keyValuePair) use ($enumClassName) {
return new KeyValuePair(
$enumClassName . ':' . $keyValuePair->getValue(),
$keyValuePair->getValue()
);
});
} elseif (is_subclass_of($enumClassName, \BackedEnum::class)) {
/** @var \UnitEnum[] $values */
$values = $enumClassName::cases();

$values = ArrayType::mapByCallback($values, function (KeyValuePair $keyValuePair) use ($enumClassName) {
/** @var \BackedEnum $enum */
$enum = $keyValuePair->getValue();

return new KeyValuePair(
$enumClassName . ':' . $enum->value,
$enum->value,
);
});
} else {
throw new \Exception(sprintf('Unexpected enum class "%s"', $enumClassName));
}

return new CallbackChoiceLoader(function () use ($values) {
return $values;
Expand Down
2 changes: 1 addition & 1 deletion src/Twig/EnumTranslatorRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function __construct(
}

public function translateEnum(
Enum $enum,
Enum|\BackedEnum $enum,
string $translationDomain = 'enums'
): string
{
Expand Down
35 changes: 35 additions & 0 deletions tests/Enum/EnumTranslatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Mhujer\ConsistenceBundle\Enum;

use Mhujer\ConsistenceBundle\Fixtures\CardColor;
use Mhujer\ConsistenceBundle\Fixtures\CardColorNative;
use Mhujer\ConsistenceBundle\Fixtures\RolesEnum;
use Symfony\Contracts\Translation\TranslatorInterface;

Expand Down Expand Up @@ -43,6 +44,40 @@ public function testTranslateEnumWithTranslationDomain(): void
);
}

public function testTranslateNativeEnum(): void
{
$mockTranslator = $this->createMock(TranslatorInterface::class);
$mockTranslator->method('trans')
->willReturnArgument(0);

$enumTranslator = new EnumTranslator($mockTranslator);

self::assertSame(
'Mhujer\ConsistenceBundle\Fixtures\CardColorNative:red',
$enumTranslator->translateEnum(CardColorNative::RED),
);
self::assertSame(
'Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black',
$enumTranslator->translateEnum(CardColorNative::BLACK),
);
}

public function testTranslateNativeEnumWithTranslationDomain(): void
{
$mockTranslator = $this->createMock(TranslatorInterface::class);
$mockTranslator->expects($this->once())
->method('trans')
->with('Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black', [], 'enums-frontend')
->will($this->returnValue('translated:Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black'));

$enumTranslator = new EnumTranslator($mockTranslator);

self::assertSame(
'translated:Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black',
$enumTranslator->translateEnum(CardColorNative::BLACK, 'enums-frontend')
);
}

public function testTranslateEnumWithMultiEnumThrowsException(): void
{
$mockTranslator = $this->createMock(TranslatorInterface::class);
Expand Down
11 changes: 11 additions & 0 deletions tests/Fixtures/CardColorNative.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types = 1);

namespace Mhujer\ConsistenceBundle\Fixtures;

enum CardColorNative: string
{

case BLACK = 'black';
case RED = 'red';

}
19 changes: 18 additions & 1 deletion tests/FormType/EnumType/EnumTransformerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Mhujer\ConsistenceBundle\FormType\EnumType;

use Mhujer\ConsistenceBundle\Fixtures\CardColor;
use Mhujer\ConsistenceBundle\Fixtures\CardColorNative;
use stdClass;

class EnumTransformerTest extends \PHPUnit\Framework\TestCase
Expand All @@ -27,9 +28,25 @@ public function testReverseTransform(): void
public function testInvalidEnumClass(): void
{
$this->expectException(\Throwable::class);
$this->expectExceptionMessage('"stdClass" is not a subclass of "Consistence\Enum\Enum"');
$this->expectExceptionMessage('"stdClass" is neither a subclass of "Consistence\Enum\Enum" or "BackedEnum"');

new EnumTransformer(stdClass::class);
}

public function testTransformNativeEnum(): void
{
$enumTransformer = new EnumTransformer(CardColorNative::class);

self::assertSame('black', $enumTransformer->transform(CardColorNative::BLACK));
self::assertNull($enumTransformer->transform(null));
}

public function testReverseTransformNativeEnum(): void
{
$enumTransformer = new EnumTransformer(CardColorNative::class);

self::assertSame(CardColorNative::RED, $enumTransformer->reverseTransform('red'));
self::assertNull($enumTransformer->reverseTransform(null));
}

}
17 changes: 16 additions & 1 deletion tests/FormType/EnumType/EnumTypeChoiceLoaderFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Mhujer\ConsistenceBundle\FormType\EnumType;

use Mhujer\ConsistenceBundle\Fixtures\CardColor;
use Mhujer\ConsistenceBundle\Fixtures\CardColorNative;
use stdClass;

class EnumTypeChoiceLoaderFactoryTest extends \PHPUnit\Framework\TestCase
Expand All @@ -22,10 +23,24 @@ public function testLoaderLoadsChoiceList(): void
], $choiceList->getStructuredValues());
}

public function testLoaderLoadsChoiceListForNativeEnum(): void
{
$choiceLoader = EnumTypeChoiceLoaderFactory::createLoader(
CardColorNative::class
);

$choiceList = $choiceLoader->loadChoiceList();

self::assertSame([
'Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black' => 'black',
'Mhujer\ConsistenceBundle\Fixtures\CardColorNative:red' => 'red',
], $choiceList->getStructuredValues());
}

public function testInvalidEnumClass(): void
{
$this->expectException(\Throwable::class);
$this->expectExceptionMessage('"stdClass" is not a subclass of "Consistence\Enum\Enum"');
$this->expectExceptionMessage('"stdClass" is neither a subclass of "Consistence\Enum\Enum" or "BackedEnum"');

EnumTypeChoiceLoaderFactory::createLoader(
stdClass::class
Expand Down
36 changes: 36 additions & 0 deletions tests/FormType/EnumTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Mhujer\ConsistenceBundle\FormType;

use Mhujer\ConsistenceBundle\Fixtures\CardColor;
use Mhujer\ConsistenceBundle\Fixtures\CardColorNative;

class EnumTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
{
Expand Down Expand Up @@ -45,4 +46,39 @@ public function testUpdateWithData(): void
self::assertSame(CardColor::RED, $enumFromForm->getValue());
}

public function testSubmitValidDataNativeEnum(): void
{
$formElement = $this->factory->create(EnumType::class, null, [
'enum_class' => CardColorNative::class,
]);

$formElement->submit('red');

$this->assertTrue($formElement->isSynchronized());

/** @var \Mhujer\ConsistenceBundle\Fixtures\CardColorNative $enumFromForm */
$enumFromForm = $formElement->getData();
self::assertSame(CardColorNative::RED, $enumFromForm);
}

public function testUpdateWithDataNativeEnum(): void
{
$formElement = $this->factory->create(EnumType::class, CardColorNative::BLACK, [
'enum_class' => CardColorNative::class,
]);

/** @var \Mhujer\ConsistenceBundle\Fixtures\CardColorNative $enumFromForm */
$enumFromForm = $formElement->getData();
self::assertSame(CardColorNative::BLACK, $enumFromForm);

// submit form
$formElement->submit('red');

$this->assertTrue($formElement->isSynchronized());

/** @var \Mhujer\ConsistenceBundle\Fixtures\CardColorNative $enumFromForm */
$enumFromForm = $formElement->getData();
self::assertSame(CardColorNative::RED, $enumFromForm);
}

}
35 changes: 35 additions & 0 deletions tests/Twig/EnumTranslatorRuntimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Mhujer\ConsistenceBundle\Enum\EnumTranslator;
use Mhujer\ConsistenceBundle\Fixtures\CardColor;
use Mhujer\ConsistenceBundle\Fixtures\CardColorNative;
use Symfony\Contracts\Translation\TranslatorInterface;

class EnumTranslatorRuntimeTest extends \PHPUnit\Framework\TestCase
Expand Down Expand Up @@ -43,4 +44,38 @@ public function testTranslateEnumWithTranslationDomain(): void
);
}

public function testTranslateNativeEnum(): void
{
$mockTranslator = $this->createMock(TranslatorInterface::class);
$mockTranslator->method('trans')
->willReturnArgument(0);

$enumTranslatorRuntime = new EnumTranslatorRuntime(
new EnumTranslator($mockTranslator)
);

self::assertSame(
'Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black',
$enumTranslatorRuntime->translateEnum(CardColorNative::BLACK)
);
}

public function testTranslateNativeEnumWithTranslationDomain(): void
{
$mockTranslator = $this->createMock(TranslatorInterface::class);
$mockTranslator->expects($this->once())
->method('trans')
->with('Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black', [], 'enums-frontend')
->will($this->returnValue('translated:Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black'));

$enumTranslatorRuntime = new EnumTranslatorRuntime(
new EnumTranslator($mockTranslator)
);

self::assertSame(
'translated:Mhujer\ConsistenceBundle\Fixtures\CardColorNative:black',
$enumTranslatorRuntime->translateEnum(CardColorNative::BLACK, 'enums-frontend')
);
}

}

0 comments on commit 454d3e3

Please sign in to comment.