mirror of
https://github.com/symfony/form.git
synced 2026-03-24 00:02:23 +01:00
[Form] Add form type guesser for EnumType
This commit is contained in:
@@ -5,6 +5,7 @@ CHANGELOG
|
||||
---
|
||||
|
||||
* Add `input=date_point` to `DateTimeType`, `DateType` and `TimeType`
|
||||
* Add support for guessing form type of enum properties
|
||||
|
||||
7.3
|
||||
---
|
||||
|
||||
78
EnumFormTypeGuesser.php
Normal file
78
EnumFormTypeGuesser.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
||||
use Symfony\Component\Form\Guess\Guess;
|
||||
use Symfony\Component\Form\Guess\TypeGuess;
|
||||
use Symfony\Component\Form\Guess\ValueGuess;
|
||||
|
||||
final class EnumFormTypeGuesser implements FormTypeGuesserInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<string, string|false>>
|
||||
*/
|
||||
private array $cache = [];
|
||||
|
||||
public function guessType(string $class, string $property): ?TypeGuess
|
||||
{
|
||||
if (!($enum = $this->getPropertyType($class, $property))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TypeGuess(EnumType::class, ['class' => ltrim($enum, '?')], Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
|
||||
public function guessRequired(string $class, string $property): ?ValueGuess
|
||||
{
|
||||
if (!($enum = $this->getPropertyType($class, $property))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ValueGuess('?' !== $enum[0], Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
|
||||
public function guessMaxLength(string $class, string $property): ?ValueGuess
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function guessPattern(string $class, string $property): ?ValueGuess
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getPropertyType(string $class, string $property): string|false
|
||||
{
|
||||
if (isset($this->cache[$class][$property])) {
|
||||
return $this->cache[$class][$property];
|
||||
}
|
||||
|
||||
try {
|
||||
$propertyReflection = new \ReflectionProperty($class, $property);
|
||||
} catch (\ReflectionException) {
|
||||
return $this->cache[$class][$property] = false;
|
||||
}
|
||||
|
||||
$type = $propertyReflection->getType();
|
||||
if (!$type instanceof \ReflectionNamedType || !enum_exists($type->getName())) {
|
||||
$enum = false;
|
||||
} else {
|
||||
$enum = $type->getName();
|
||||
if ($type->allowsNull()) {
|
||||
$enum = '?'.$enum;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->cache[$class][$property] = $enum;
|
||||
}
|
||||
}
|
||||
208
Tests/EnumFormTypeGuesserTest.php
Normal file
208
Tests/EnumFormTypeGuesserTest.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests;
|
||||
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Form\EnumFormTypeGuesser;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EnumType as FormEnumType;
|
||||
use Symfony\Component\Form\Guess\Guess;
|
||||
use Symfony\Component\Form\Guess\TypeGuess;
|
||||
use Symfony\Component\Form\Guess\ValueGuess;
|
||||
use Symfony\Component\Form\Tests\Fixtures\BackedEnumFormTypeGuesserCaseEnum;
|
||||
use Symfony\Component\Form\Tests\Fixtures\EnumFormTypeGuesserCase;
|
||||
use Symfony\Component\Form\Tests\Fixtures\EnumFormTypeGuesserCaseEnum;
|
||||
|
||||
class EnumFormTypeGuesserTest extends TestCase
|
||||
{
|
||||
#[DataProvider('provideGuessTypeCases')]
|
||||
public function testGuessType(?TypeGuess $expectedTypeGuess, string $class, string $property)
|
||||
{
|
||||
$typeGuesser = new EnumFormTypeGuesser();
|
||||
|
||||
$typeGuess = $typeGuesser->guessType($class, $property);
|
||||
|
||||
self::assertEquals($expectedTypeGuess, $typeGuess);
|
||||
}
|
||||
|
||||
#[DataProvider('provideGuessRequiredCases')]
|
||||
public function testGuessRequired(?ValueGuess $expectedValueGuess, string $class, string $property)
|
||||
{
|
||||
$typeGuesser = new EnumFormTypeGuesser();
|
||||
|
||||
$valueGuess = $typeGuesser->guessRequired($class, $property);
|
||||
|
||||
self::assertEquals($expectedValueGuess, $valueGuess);
|
||||
}
|
||||
|
||||
public static function provideGuessTypeCases(): iterable
|
||||
{
|
||||
yield 'Undefined class' => [
|
||||
null,
|
||||
'UndefinedClass',
|
||||
'undefinedProperty',
|
||||
];
|
||||
|
||||
yield 'Undefined property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'undefinedProperty',
|
||||
];
|
||||
|
||||
yield 'Undefined enum' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'undefinedEnum',
|
||||
];
|
||||
|
||||
yield 'Non-enum property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'string',
|
||||
];
|
||||
|
||||
yield 'Enum property' => [
|
||||
new TypeGuess(
|
||||
FormEnumType::class,
|
||||
[
|
||||
'class' => EnumFormTypeGuesserCaseEnum::class,
|
||||
],
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enum',
|
||||
];
|
||||
|
||||
yield 'Nullable enum property' => [
|
||||
new TypeGuess(
|
||||
FormEnumType::class,
|
||||
[
|
||||
'class' => EnumFormTypeGuesserCaseEnum::class,
|
||||
],
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'nullableEnum',
|
||||
];
|
||||
|
||||
yield 'Backed enum property' => [
|
||||
new TypeGuess(
|
||||
FormEnumType::class,
|
||||
[
|
||||
'class' => BackedEnumFormTypeGuesserCaseEnum::class,
|
||||
],
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'backedEnum',
|
||||
];
|
||||
|
||||
yield 'Nullable backed enum property' => [
|
||||
new TypeGuess(
|
||||
FormEnumType::class,
|
||||
[
|
||||
'class' => BackedEnumFormTypeGuesserCaseEnum::class,
|
||||
],
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'nullableBackedEnum',
|
||||
];
|
||||
|
||||
yield 'Enum union property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enumUnion',
|
||||
];
|
||||
|
||||
yield 'Enum intersection property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enumIntersection',
|
||||
];
|
||||
}
|
||||
|
||||
public static function provideGuessRequiredCases(): iterable
|
||||
{
|
||||
yield 'Unknown class' => [
|
||||
null,
|
||||
'UndefinedClass',
|
||||
'undefinedProperty',
|
||||
];
|
||||
|
||||
yield 'Unknown property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'undefinedProperty',
|
||||
];
|
||||
|
||||
yield 'Undefined enum' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'undefinedEnum',
|
||||
];
|
||||
|
||||
yield 'Non-enum property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'string',
|
||||
];
|
||||
|
||||
yield 'Enum property' => [
|
||||
new ValueGuess(
|
||||
true,
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enum',
|
||||
];
|
||||
|
||||
yield 'Nullable enum property' => [
|
||||
new ValueGuess(
|
||||
false,
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'nullableEnum',
|
||||
];
|
||||
|
||||
yield 'Backed enum property' => [
|
||||
new ValueGuess(
|
||||
true,
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'backedEnum',
|
||||
];
|
||||
|
||||
yield 'Nullable backed enum property' => [
|
||||
new ValueGuess(
|
||||
false,
|
||||
Guess::HIGH_CONFIDENCE,
|
||||
),
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'nullableBackedEnum',
|
||||
];
|
||||
|
||||
yield 'Enum union property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enumUnion',
|
||||
];
|
||||
|
||||
yield 'Enum intersection property' => [
|
||||
null,
|
||||
EnumFormTypeGuesserCase::class,
|
||||
'enumIntersection',
|
||||
];
|
||||
}
|
||||
}
|
||||
36
Tests/Fixtures/EnumFormTypeGuesserCase.php
Normal file
36
Tests/Fixtures/EnumFormTypeGuesserCase.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Fixtures;
|
||||
|
||||
class EnumFormTypeGuesserCase
|
||||
{
|
||||
public string $string;
|
||||
public UndefinedEnum $undefinedEnum;
|
||||
public EnumFormTypeGuesserCaseEnum $enum;
|
||||
public ?EnumFormTypeGuesserCaseEnum $nullableEnum;
|
||||
public BackedEnumFormTypeGuesserCaseEnum $backedEnum;
|
||||
public ?BackedEnumFormTypeGuesserCaseEnum $nullableBackedEnum;
|
||||
public EnumFormTypeGuesserCaseEnum|BackedEnumFormTypeGuesserCaseEnum $enumUnion;
|
||||
public EnumFormTypeGuesserCaseEnum&BackedEnumFormTypeGuesserCaseEnum $enumIntersection;
|
||||
}
|
||||
|
||||
enum EnumFormTypeGuesserCaseEnum
|
||||
{
|
||||
case Foo;
|
||||
case Bar;
|
||||
}
|
||||
|
||||
enum BackedEnumFormTypeGuesserCaseEnum: string
|
||||
{
|
||||
case Foo = 'foo';
|
||||
case Bar = 'bar';
|
||||
}
|
||||
Reference in New Issue
Block a user