Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit 4f95f86

Browse files
authored
Merge pull request #13 from KrzysztofMoskalik/embed-forms
Embed forms
2 parents 6462b01 + 0d5a77e commit 4f95f86

File tree

13 files changed

+279
-3
lines changed

13 files changed

+279
-3
lines changed

Annotations/Embed.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Codete\FormGeneratorBundle\Annotations;
4+
5+
/**
6+
* @Annotation
7+
*/
8+
class Embed extends Display
9+
{
10+
public $view = 'default';
11+
12+
public $type = 'embed';
13+
}

FieldTypeMapper.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class FieldTypeMapper
99
private static $typeNS = [
1010
'Symfony\\Component\\Form\\Extension\\Core\\Type',
1111
'Symfony\\Bridge\\Doctrine\\Form\\Type',
12+
'Codete\\FormGeneratorBundle\\Form\\Type',
1213
];
1314

1415
/**

Form/Type/EmbedType.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Codete\FormGeneratorBundle\Form\Type;
4+
5+
use Codete\FormGeneratorBundle\FormGenerator;
6+
use Symfony\Component\Form\AbstractType;
7+
use Symfony\Component\Form\FormBuilderInterface;
8+
use Symfony\Component\OptionsResolver\OptionsResolver;
9+
10+
11+
class EmbedType extends AbstractType
12+
{
13+
const TYPE = 'embed';
14+
15+
/**
16+
* @var FormGenerator $formGenerator
17+
*/
18+
private $formGenerator;
19+
20+
/**
21+
* @param FormGenerator $formGenerator
22+
*/
23+
public function __construct(FormGenerator $formGenerator)
24+
{
25+
$this->formGenerator = $formGenerator;
26+
}
27+
28+
/**
29+
* @param FormBuilderInterface $builder
30+
* @param array $options
31+
*/
32+
public function buildForm(FormBuilderInterface $builder, array $options)
33+
{
34+
$this->formGenerator->populateFormBuilder($builder, $options['model'], $options['view'], isset($options['context']) ? $options['context'] : []);
35+
}
36+
37+
/**
38+
* @param OptionsResolver $resolver
39+
*/
40+
public function configureOptions(OptionsResolver $resolver)
41+
{
42+
$resolver->setDefaults([
43+
'view' => 'default',
44+
'class' => '',
45+
'context' => [],
46+
'model' => null
47+
]);
48+
}
49+
50+
/**
51+
* @return string
52+
*/
53+
public function getBlockPrefix()
54+
{
55+
return self::TYPE;
56+
}
57+
58+
/**
59+
* This should stay until we drop Symfony 2.7 support
60+
*
61+
* @return string
62+
*/
63+
public function getName()
64+
{
65+
return self::TYPE;
66+
}
67+
}

FormConfigurationFactory.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
use Codete\FormGeneratorBundle\Annotations\Display;
66
use Codete\FormGeneratorBundle\Annotations\Form;
7+
use Codete\FormGeneratorBundle\Form\Type\EmbedType;
78
use Doctrine\Common\Annotations\AnnotationReader;
89
use Doctrine\Common\Annotations\Reader;
10+
use Doctrine\Instantiator\Instantiator;
911

1012
/**
1113
* FormConfigurationFactory creates initial form configuration that is adjusted later.
@@ -24,13 +26,19 @@ class FormConfigurationFactory
2426
*/
2527
private $annotationReader;
2628

29+
/**
30+
* @var Instantiator
31+
*/
32+
private $instantiator;
33+
2734
/**
2835
* @param AdjusterRegistry $adjusterRegistry
2936
*/
3037
public function __construct(AdjusterRegistry $adjusterRegistry)
3138
{
3239
$this->adjusterRegistry = $adjusterRegistry;
3340
$this->annotationReader = new AnnotationReader();
41+
$this->instantiator = new Instantiator();
3442
}
3543

3644
/**
@@ -88,6 +96,13 @@ private function getFieldsConfiguration($model, $fields = [])
8896
if (isset($fields[$property->getName()])) {
8997
$configuration[$property->getName()] = array_replace_recursive($configuration[$property->getName()], $fields[$property->getName()]);
9098
}
99+
if ($configuration[$property->getName()]['type'] === EmbedType::TYPE) {
100+
if (($value = $property->getValue($model)) === null) {
101+
$value = $this->instantiator->instantiate($configuration[$property->getName()]['class']);
102+
}
103+
$configuration[$property->getName()]['data_class'] = $configuration[$property->getName()]['class'];
104+
$configuration[$property->getName()]['model'] = $value;
105+
}
91106
// this variable comes from Doctrine\Common\Annotations\Annotation
92107
unset($configuration[$property->getName()]['value']);
93108
}

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,33 @@ you to pass any additional informations you want in optional
105105
which defines order of execution (default is `0`, if two or more
106106
services have same priority then first added is executed first).
107107

108+
Embed Forms
109+
-----------
110+
111+
FormGenerator will build also forms for nested models:
112+
113+
``` php
114+
/*
115+
* @Form\Embed(
116+
* class = "Codete\FormGeneratorBundle\Tests\Model\Person",
117+
* )
118+
*/
119+
public $person;
120+
```
121+
122+
This will build form from all nested model properties,
123+
but we can specify which view we want to use:
124+
125+
``` php
126+
/*
127+
* @Form\Embed(
128+
* class = "Codete\FormGeneratorBundle\Tests\Model\Person",
129+
* view = "work"
130+
* )
131+
*/
132+
public $employee;
133+
```
134+
108135
FormViewProvider
109136
----------------
110137

Resources/config/form_generator.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
<service id="form_generator" class="Codete\FormGeneratorBundle\FormGenerator">
99
<argument type="service" id="form.factory"/>
1010
</service>
11+
12+
<service id="form_generator.type.embed" class="Codete\FormGeneratorBundle\Form\Type\EmbedType">
13+
<tag name="form.type" alias="embed" />
14+
<argument type="service" id="form_generator" />
15+
</service>
1116

1217
</services>
1318

Tests/BaseTest.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,28 @@
44

55
use Doctrine\Common\Annotations\AnnotationRegistry;
66
use Codete\FormGeneratorBundle\FormGenerator;
7+
use Symfony\Component\Form\FormFactoryBuilderInterface;
78
use Symfony\Component\Form\Forms;
89

910
abstract class BaseTest extends \PHPUnit_Framework_TestCase
1011
{
1112
/** @var \Codete\FormGeneratorBundle\FormGenerator */
1213
protected $formGenerator;
13-
14+
15+
/**
16+
* @var FormFactoryBuilderInterface
17+
*/
18+
protected $formFactoryBuilder;
19+
1420
public function setUp()
1521
{
1622
$loader = require __DIR__.'/../vendor/autoload.php';
1723
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
24+
25+
$this->formFactoryBuilder = Forms::createFormFactoryBuilder();
26+
1827
$this->formGenerator = new FormGenerator(
19-
Forms::createFormFactoryBuilder()->getFormFactory()
28+
$this->formFactoryBuilder->getFormFactory()
2029
);
2130
}
2231
}

Tests/FieldTypeMapperTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function dataProvider()
2424
['dateTime', 'dateTime'],
2525
[null, null],
2626
['unknown', 'unknown'],
27+
['embed', 'embed']
2728
];
2829
} else {
2930
return [
@@ -32,6 +33,7 @@ public function dataProvider()
3233
['dateTime', 'Symfony\\Component\\Form\\Extension\\Core\\Type\\DateTimeType'],
3334
[null, null],
3435
['unknown', 'unknown'],
36+
['embed', 'Codete\\FormGeneratorBundle\\Form\\Type\\EmbedType']
3537
];
3638
}
3739
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Codete\FormGeneratorBundle\Tests\FormConfigurationModifier;
4+
5+
use Codete\FormGeneratorBundle\FormConfigurationModifierInterface;
6+
use Codete\FormGeneratorBundle\Tests\Model\Person;
7+
8+
class NoPhotoPersonModifier implements FormConfigurationModifierInterface
9+
{
10+
public function modify($model, $configuration, $context)
11+
{
12+
unset($configuration['photo']);
13+
return $configuration;
14+
}
15+
16+
public function supports($model, $configuration, $context)
17+
{
18+
return $model instanceof Person && isset($context['no_photo']) && $context['no_photo'] === true;
19+
}
20+
}

Tests/FormGeneratorTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,32 @@
22

33
namespace Codete\FormGeneratorBundle\Tests;
44

5+
use Codete\FormGeneratorBundle\FieldTypeMapper;
6+
use Codete\FormGeneratorBundle\Form\Type\EmbedType;
7+
use Codete\FormGeneratorBundle\Tests\FormConfigurationModifier\InactivePersonModifier;
8+
use Codete\FormGeneratorBundle\Tests\FormConfigurationModifier\NoPhotoPersonModifier;
9+
use Codete\FormGeneratorBundle\Tests\Model\Person;
10+
use Codete\FormGeneratorBundle\Tests\Model\SimpleParent;
11+
use Symfony\Component\Form\PreloadedExtension;
12+
513
class FormGeneratorTest extends BaseTest
614
{
15+
/**
16+
* @var PreloadedExtension
17+
*/
18+
protected $embedTypeExtension;
19+
20+
public function setUp()
21+
{
22+
parent::setUp();
23+
24+
$embedType = new EmbedType($this->formGenerator);
25+
26+
$this->embedTypeExtension = new PreloadedExtension([
27+
$embedType->getBlockPrefix() => $embedType,
28+
], []);
29+
}
30+
731
/**
832
* @dataProvider provideDefaultForm
933
*/
@@ -172,4 +196,38 @@ protected function checkForm($model, $expectedFields, callable $additionalCheck
172196
$additionalCheck($this, $form);
173197
}
174198
}
199+
200+
public function testEmbedForms()
201+
{
202+
$sp = new SimpleParent();
203+
204+
$sp->employee = new Person();
205+
$sp->named = new Person();
206+
207+
$sp->employee->salary = 1390.86;
208+
$sp->named->active = false;
209+
210+
$sp->person = new Person('Bar', 'Baz');
211+
212+
$fb = $this->formFactoryBuilder
213+
->addExtension($this->embedTypeExtension)
214+
->getFormFactory()
215+
->createBuilder(FieldTypeMapper::map('form'), $sp);
216+
217+
$this->formGenerator->addFormConfigurationModifier(new NoPhotoPersonModifier());
218+
$this->formGenerator->addFormConfigurationModifier(new InactivePersonModifier());
219+
220+
$this->formGenerator->populateFormBuilder($fb, $sp);
221+
$form = $fb->getForm();
222+
$this->assertEquals(count(['person', 'noName', 'anonymous', 'employee']), count($form));
223+
$this->assertEquals(count(['title', 'name', 'surname', 'photo', 'active', 'salary']), $form->get('person')->count());
224+
$this->assertEquals(count(['title', 'name', 'surname', 'photo', 'active']), $form->get('named')->count());
225+
$this->assertEquals(count(['title', 'name', 'surname', 'active', 'salary']), $form->get('anonymous')->count());
226+
$this->assertEquals(count(['salary']), $form->get('employee')->count());
227+
$this->assertEquals(1390.86, $form->get('employee')->get('salary')->getData());
228+
$this->assertNull($form->get('anonymous')->get('name')->getData());
229+
$this->assertEquals('Foo', $form->get('named')->get('name')->getData());
230+
$this->assertEquals('Bar', $form->get('person')->get('name')->getData());
231+
$this->assertEquals('Baz', $form->get('person')->get('surname')->getData());
232+
}
175233
}

0 commit comments

Comments
 (0)