Skip to content

Commit 8f19bfc

Browse files
authored
Merge pull request #20 from yokai-php/form-type-guesser
Add EnumTypeGuesser
2 parents c5c1176 + 88d3208 commit 8f19bfc

File tree

7 files changed

+267
-8
lines changed

7 files changed

+267
-8
lines changed

DependencyInjection/EnumExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function load(array $configs, ContainerBuilder $container)
2424

2525
$xmlLoader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
2626
$xmlLoader->load('services.xml');
27-
$xmlLoader->load('form_types.xml');
27+
$xmlLoader->load('forms.xml');
2828
$xmlLoader->load('validators.xml');
2929
$xmlLoader->load('twig.xml');
3030
}

Form/Extension/EnumTypeGuesser.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace Yokai\EnumBundle\Form\Extension;
4+
5+
use Symfony\Component\Form\AbstractType;
6+
use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser;
7+
use Symfony\Component\Form\Guess\Guess;
8+
use Symfony\Component\Form\Guess\TypeGuess;
9+
use Symfony\Component\Validator\Constraint;
10+
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
11+
use Yokai\EnumBundle\Form\Type\EnumType;
12+
use Yokai\EnumBundle\Registry\EnumRegistryInterface;
13+
use Yokai\EnumBundle\Validator\Constraints\Enum;
14+
15+
/**
16+
* @author Yann Eugoné <eugone.yann@gmail.com>
17+
*/
18+
class EnumTypeGuesser extends ValidatorTypeGuesser
19+
{
20+
/**
21+
* @var EnumRegistryInterface
22+
*/
23+
private $enumRegistry;
24+
25+
/**
26+
* @var string
27+
*/
28+
private $enumFormType;
29+
30+
/**
31+
* @param MetadataFactoryInterface $metadataFactory
32+
* @param EnumRegistryInterface $enumRegistry
33+
*/
34+
public function __construct(MetadataFactoryInterface $metadataFactory, EnumRegistryInterface $enumRegistry)
35+
{
36+
parent::__construct($metadataFactory);
37+
38+
$this->enumRegistry = $enumRegistry;
39+
40+
if (method_exists(AbstractType::class, 'getBlockPrefix')) {
41+
$this->enumFormType = EnumType::class; //Symfony 3.x support
42+
} else {
43+
$this->enumFormType = 'enum'; //Symfony 2.x support
44+
}
45+
}
46+
47+
/**
48+
* @inheritdoc
49+
*/
50+
public function guessTypeForConstraint(Constraint $constraint)
51+
{
52+
if (!$constraint instanceof Enum) {
53+
return null;
54+
}
55+
56+
return new TypeGuess(
57+
$this->enumFormType,
58+
[
59+
'enum' => $constraint->enum,
60+
'multiple' => $constraint->multiple,
61+
],
62+
Guess::HIGH_CONFIDENCE
63+
);
64+
}
65+
66+
/**
67+
* @inheritDoc
68+
*/
69+
public function guessRequired($class, $property)
70+
{
71+
return null; //override parent : not able to guess
72+
}
73+
74+
/**
75+
* @inheritDoc
76+
*/
77+
public function guessMaxLength($class, $property)
78+
{
79+
return null; //override parent : not able to guess
80+
}
81+
82+
/**
83+
* @inheritDoc
84+
*/
85+
public function guessPattern($class, $property)
86+
{
87+
return null; //override parent : not able to guess
88+
}
89+
}

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,15 @@ class MemberType extends AbstractType
164164
public function buildForm(FormBuilderInterface $builder, array $options)
165165
{
166166
$builder
167-
// For Symfony >= 2.8
167+
// Let the bundle guess the form type for you (requires that you configured the validation)
168+
->add('state')
169+
->add('gender')
170+
171+
// Manual form type binding for Symfony >= 2.8
168172
->add('state', EnumType::class, ['enum' => StateEnum::NAME])
169173
->add('gender', EnumType::class, ['enum' => GenderEnum::NAME])
170174

171-
// For Symfony 2.7
175+
// Manual form type binding for Symfony 2.7
172176
->add('state', 'enum', ['enum' => StateEnum::NAME])
173177
->add('gender', 'enum', ['enum' => GenderEnum::NAME])
174178
;

Resources/config/form_types.xml renamed to Resources/config/forms.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
<tag name="form.type" alias="enum" />
1313
</service>
1414

15+
<service id="form_extention.type_guesser.enum" class="Yokai\EnumBundle\Form\Extension\EnumTypeGuesser">
16+
<argument type="service" id="validator.mapping.class_metadata_factory"/>
17+
<argument type="service" id="enum.registry"/>
18+
19+
<tag name="form.type" alias="enum" />
20+
</service>
21+
1522
</services>
1623

1724
</container>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
namespace Yokai\EnumBundle\Tests\Form\Extension;
4+
5+
use Prophecy\Prophecy\ObjectProphecy;
6+
use Symfony\Component\Form\AbstractType;
7+
use Symfony\Component\Form\Extension\Core\Type\FormType;
8+
use Symfony\Component\Form\Guess\Guess;
9+
use Symfony\Component\Form\Guess\TypeGuess;
10+
use Symfony\Component\Form\Test\TypeTestCase;
11+
use Symfony\Component\Validator\Mapping\ClassMetadata;
12+
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
13+
use Yokai\EnumBundle\Form\Extension\EnumTypeGuesser;
14+
use Yokai\EnumBundle\Form\Type\EnumType;
15+
use Yokai\EnumBundle\Registry\EnumRegistryInterface;
16+
use Yokai\EnumBundle\Tests\Fixtures\GenderEnum;
17+
use Yokai\EnumBundle\Tests\Form\TestExtension;
18+
use Yokai\EnumBundle\Validator\Constraints\Enum;
19+
20+
/**
21+
* @author Yann Eugoné <eugone.yann@gmail.com>
22+
*/
23+
class EnumTypeGuesserTest extends TypeTestCase
24+
{
25+
const TEST_CLASS = 'Yokai\EnumBundle\Tests\Form\Extension\EnumTypeGuesserTest_TestClass';
26+
27+
const TEST_PROPERTY = 'property';
28+
29+
/**
30+
* @var EnumTypeGuesser
31+
*/
32+
private $guesser;
33+
34+
/**
35+
* @var ObjectProphecy|EnumRegistryInterface
36+
*/
37+
private $enumRegistry;
38+
39+
/**
40+
* @var ClassMetadata
41+
*/
42+
private $metadata;
43+
44+
/**
45+
* @var ObjectProphecy|MetadataFactoryInterface
46+
*/
47+
private $metadataFactory;
48+
49+
protected function setUp()
50+
{
51+
$this->enumRegistry = $this->prophesize('Yokai\EnumBundle\Registry\EnumRegistryInterface');
52+
$this->enumRegistry->has('state')->willReturn(false);
53+
$this->enumRegistry->has('gender')->willReturn(true);
54+
$this->enumRegistry->get('gender')->willReturn(new GenderEnum);
55+
56+
$this->metadata = new ClassMetadata(self::TEST_CLASS);
57+
$this->metadata->addPropertyConstraint(self::TEST_PROPERTY, new Enum(['enum' => 'gender']));
58+
$this->metadataFactory = $this->prophesize('Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface');
59+
$this->metadataFactory->getMetadataFor(self::TEST_CLASS)
60+
->willReturn($this->metadata);
61+
62+
$this->guesser = new EnumTypeGuesser($this->metadataFactory->reveal(), $this->enumRegistry->reveal());
63+
64+
parent::setUp();
65+
}
66+
67+
public function testGuessType()
68+
{
69+
$guess = new TypeGuess(
70+
$this->getEnumType(),
71+
[
72+
'enum' => 'gender',
73+
'multiple' => false,
74+
],
75+
Guess::HIGH_CONFIDENCE
76+
);
77+
78+
$this->assertEquals($guess, $this->guesser->guessType(self::TEST_CLASS, self::TEST_PROPERTY));
79+
}
80+
81+
public function testGuessRequired()
82+
{
83+
$this->assertNull($this->guesser->guessRequired(self::TEST_CLASS, self::TEST_PROPERTY));
84+
}
85+
86+
public function testGuessMaxLength()
87+
{
88+
$this->assertNull($this->guesser->guessMaxLength(self::TEST_CLASS, self::TEST_PROPERTY));
89+
}
90+
91+
public function testGuessPattern()
92+
{
93+
$this->assertNull($this->guesser->guessPattern(self::TEST_CLASS, self::TEST_PROPERTY));
94+
}
95+
96+
public function testCreateForm()
97+
{
98+
$class = self::TEST_CLASS;
99+
$form = $this->factory->create($this->getFormType(), new $class, ['data_class' => $class])
100+
->add(self::TEST_PROPERTY);
101+
102+
$this->assertEquals(['Male' => 'male', 'Female' => 'female'], $form->get(self::TEST_PROPERTY)->getConfig()->getOption('choices'));
103+
}
104+
105+
protected function getFormType()
106+
{
107+
if (method_exists(AbstractType::class, 'getBlockPrefix')) {
108+
$name = FormType::class; //Symfony 3.x support
109+
} else {
110+
$name = 'form'; //Symfony 2.x support
111+
}
112+
113+
return $name;
114+
}
115+
116+
protected function getEnumType()
117+
{
118+
if (method_exists(AbstractType::class, 'getBlockPrefix')) {
119+
$name = EnumType::class; //Symfony 3.x support
120+
} else {
121+
$name = 'enum'; //Symfony 2.x support
122+
}
123+
124+
return $name;
125+
}
126+
127+
protected function getExtensions()
128+
{
129+
return [
130+
new TestExtension($this->enumRegistry->reveal(), $this->metadataFactory->reveal()),
131+
];
132+
}
133+
}
134+
135+
class EnumTypeGuesserTest_TestClass
136+
{
137+
public $property;
138+
}

Tests/Form/TestExtension.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Yokai\EnumBundle\Tests\Form;
44

55
use Symfony\Component\Form\AbstractExtension;
6+
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
7+
use Yokai\EnumBundle\Form\Extension\EnumTypeGuesser;
68
use Yokai\EnumBundle\Form\Type\EnumType;
79
use Yokai\EnumBundle\Registry\EnumRegistryInterface;
810

@@ -17,11 +19,18 @@ class TestExtension extends AbstractExtension
1719
private $enumRegistry;
1820

1921
/**
20-
* @param EnumRegistryInterface $enumRegistry
22+
* @var MetadataFactoryInterface|null
2123
*/
22-
public function __construct(EnumRegistryInterface $enumRegistry)
24+
private $metadataFactory;
25+
26+
/**
27+
* @param EnumRegistryInterface $enumRegistry
28+
* @param MetadataFactoryInterface|null $metadataFactory
29+
*/
30+
public function __construct(EnumRegistryInterface $enumRegistry, MetadataFactoryInterface $metadataFactory = null)
2331
{
2432
$this->enumRegistry = $enumRegistry;
33+
$this->metadataFactory = $metadataFactory;
2534
}
2635

2736
/**
@@ -33,4 +42,16 @@ protected function loadTypes()
3342
new EnumType($this->enumRegistry),
3443
];
3544
}
45+
46+
/**
47+
* @inheritdoc
48+
*/
49+
protected function loadTypeGuesser()
50+
{
51+
if ($this->metadataFactory === null) {
52+
return null;
53+
}
54+
55+
return new EnumTypeGuesser($this->metadataFactory, $this->enumRegistry);
56+
}
3657
}

Twig/Extension/EnumExtension.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public function __construct(EnumRegistryInterface $registry)
3131
public function getFunctions()
3232
{
3333
return [
34-
new Twig_SimpleFunction('enum_label', [ $this, 'getLabel' ]),
35-
new Twig_SimpleFunction('enum_choices', [ $this, 'getChoices' ]),
34+
new Twig_SimpleFunction('enum_label', [$this, 'getLabel']),
35+
new Twig_SimpleFunction('enum_choices', [$this, 'getChoices']),
3636
];
3737
}
3838

@@ -42,7 +42,7 @@ public function getFunctions()
4242
public function getFilters()
4343
{
4444
return [
45-
new Twig_SimpleFilter('enum_label', [ $this, 'getLabel' ]),
45+
new Twig_SimpleFilter('enum_label', [$this, 'getLabel']),
4646
];
4747
}
4848

0 commit comments

Comments
 (0)