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

Commit 9ab1500

Browse files
authored
Merge pull request #29 from codete/additional-fields-v2
Add (better) option to specify additional fields through annotation
2 parents 51bcd3f + cccfa5f commit 9ab1500

15 files changed

+152
-37
lines changed

Annotations/Display.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* @Annotation
77
*
88
* @author Maciej Malarz <malarzm@gmail.com>
9+
*
10+
* @deprecated use Field instead
911
*/
1012
class Display extends \Doctrine\Common\Annotations\Annotation
1113
{

Annotations/Embed.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/**
66
* @Annotation
77
*/
8-
class Embed extends Display
8+
class Embed extends Field
99
{
1010
public $view = 'default';
1111

Annotations/Field.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+
* @author Maciej Malarz <malarzm@gmail.com>
9+
*/
10+
class Field extends Display
11+
{
12+
13+
}

FormConfigurationFactory.php

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Codete\FormGeneratorBundle;
44

55
use Codete\FormGeneratorBundle\Annotations\Display;
6+
use Codete\FormGeneratorBundle\Annotations\Field;
67
use Codete\FormGeneratorBundle\Annotations\Form;
78
use Codete\FormGeneratorBundle\Form\Type\EmbedType;
89
use Doctrine\Common\Annotations\AnnotationReader;
@@ -80,31 +81,60 @@ private function getFieldsConfiguration($model, $fields = [])
8081
$properties = $ro->getProperties();
8182
} else {
8283
foreach (array_keys($fields) as $field) {
84+
// setting the configuration to null guarantees the order of elements in there
85+
// to match the order specified in $fields
86+
$configuration[$field] = null;
87+
if (! $ro->hasProperty($field)) {
88+
continue; // most prob a class-level field, order will be maintained due to trick above
89+
}
8390
$properties[] = $ro->getProperty($field);
8491
}
8592
}
93+
$fieldConfigurations = [];
94+
// first are coming properties
8695
foreach ($properties as $property) {
87-
$propertyIsListed = array_key_exists($property->getName(), $fields);
96+
$propertyName = $property->getName();
97+
$propertyIsListed = array_key_exists($propertyName, $fields);
8898
if (!empty($fields) && !$propertyIsListed) {
89-
continue;
99+
continue; // list of fields was specified and current one is not there
90100
}
91101
$fieldConfiguration = $this->annotationReader->getPropertyAnnotation($property, Display::class);
92102
if ($fieldConfiguration === null && !$propertyIsListed) {
93103
continue;
94104
}
95-
$configuration[$property->getName()] = (array)$fieldConfiguration;
96-
if (isset($fields[$property->getName()])) {
97-
$configuration[$property->getName()] = array_replace_recursive($configuration[$property->getName()], $fields[$property->getName()]);
105+
$fieldConfigurations[$propertyName] = $fieldConfiguration;
106+
}
107+
// later are coming class-level fields. We need to iterate through all annotations as there's no method
108+
// to get *all* occurrences of chosen annotation.
109+
foreach ($this->annotationReader->getClassAnnotations($ro) as $annotation) {
110+
if (! $annotation instanceof Display) {
111+
continue;
112+
}
113+
$propertyName = $annotation->value;
114+
if (!empty($fields) && !array_key_exists($propertyName, $fields)) {
115+
continue; // list of fields was specified and current one is not there
116+
}
117+
// @todo this was a mistake originally, need to drop default required at all in 2.0
118+
unset($annotation->required);
119+
$fieldConfigurations[$propertyName] = $annotation;
120+
}
121+
foreach ($fieldConfigurations as $propertyName => $fieldConfiguration) {
122+
if ($fieldConfiguration instanceof Display && ! $fieldConfiguration instanceof Field) {
123+
@trigger_error("Display annotation has been deprecated in 1.3 and will be removed in 2.0 - please use Field instead.", E_USER_DEPRECATED);
124+
}
125+
$configuration[$propertyName] = (array)$fieldConfiguration;
126+
if (isset($fields[$propertyName])) {
127+
$configuration[$propertyName] = array_replace_recursive($configuration[$propertyName], $fields[$propertyName]);
98128
}
99-
if ($configuration[$property->getName()]['type'] === EmbedType::TYPE) {
100-
if (($value = $property->getValue($model)) === null) {
101-
$value = $this->instantiator->instantiate($configuration[$property->getName()]['class']);
129+
if ($configuration[$propertyName]['type'] === EmbedType::TYPE) {
130+
if (! $ro->hasProperty($propertyName) || ($value = $ro->getProperty($propertyName)->getValue($model)) === null) {
131+
$value = $this->instantiator->instantiate($configuration[$propertyName]['class']);
102132
}
103-
$configuration[$property->getName()]['data_class'] = $configuration[$property->getName()]['class'];
104-
$configuration[$property->getName()]['model'] = $value;
133+
$configuration[$propertyName]['data_class'] = $configuration[$propertyName]['class'];
134+
$configuration[$propertyName]['model'] = $value;
105135
}
106136
// this variable comes from Doctrine\Common\Annotations\Annotation
107-
unset($configuration[$property->getName()]['value']);
137+
unset($configuration[$propertyName]['value']);
108138
}
109139
return $configuration;
110140
}

README.md

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ forms' configuration although YAML/XML support is planned.
1515
Basic Usages
1616
------------
1717

18+
**`@Form\Display` annotation was deprecated in 1.3 and will be removed
19+
in 2.0. Please use `@Form\Field` instead.**
20+
1821
Consider a class
1922

2023
``` php
@@ -30,32 +33,32 @@ class Person
3033
public $id;
3134

3235
/**
33-
* @Form\Display(type="choice", choices = { "mr" = "Mr.", "ms" = "Ms." })
36+
* @Form\Field(type="choice", choices = { "mr" = "Mr.", "ms" = "Ms." })
3437
*/
3538
public $title;
3639

3740
/**
38-
* @Form\Display(type="text")
41+
* @Form\Field(type="text")
3942
*/
4043
public $name;
4144

4245
/**
43-
* @Form\Display(type="text")
46+
* @Form\Field(type="text")
4447
*/
4548
public $surname;
4649

4750
/**
48-
* @Form\Display(type="file")
51+
* @Form\Field(type="file")
4952
*/
5053
public $photo;
5154

5255
/**
53-
* @Form\Display(type="checkbox")
56+
* @Form\Field(type="checkbox")
5457
*/
5558
public $active;
5659

5760
/**
58-
* @Form\Display(type="money")
61+
* @Form\Field(type="money")
5962
*/
6063
public $salary;
6164
}
@@ -87,12 +90,12 @@ namespace when specifying type**
8790
Specifying Field Options
8891
------------------------
8992

90-
By default everything you specify in `@Form\Display` (except for `type`) annotation
93+
By default everything you specify in `@Form\Field` (except for `type`) annotation
9194
will be passed as an option to generated form type. To illustrate:
9295

9396
```php
9497
/**
95-
* @Form\Display(type="choice", choices = { "mr" = "Mr.", "ms" = "Ms." }, "attr" = { "class" = "foo" })
98+
* @Form\Field(type="choice", choices = { "mr" = "Mr.", "ms" = "Ms." }, "attr" = { "class" = "foo" })
9699
*/
97100
public $title;
98101
```
@@ -118,7 +121,7 @@ all Symfony fields' options into an `options` property:
118121

119122
```php
120123
/**
121-
* @Form\Display(
124+
* @Form\Field(
122125
* type="choice",
123126
* options={ "choices" = { "mr" = "Mr.", "ms" = "Ms." }, "attr" = { "class" = "foo" } }
124127
* )
@@ -131,6 +134,28 @@ them as that field's options to the `FormBuilder`. Effectively this allows you t
131134
separate field's options from options for your configuration modifiers which can be
132135
a gain on its own.
133136

137+
Adding fields not mapped to a property
138+
--------------------------------------
139+
140+
**This feature was added in 1.3.0.**
141+
142+
Sometimes you may need to add a field that will not be mapped to a property. An example
143+
of such use case is adding buttons to the form:
144+
145+
```php
146+
/**
147+
* The first value in Field annotation specifies field's name.
148+
*
149+
* @Form\Field("reset", type="reset")
150+
* @Form\Field("submit", type="submit", "label"="Save")
151+
*/
152+
class Person
153+
```
154+
155+
All fields added on the class level come last in the generated form, unless a form view
156+
(described below) specifies otherwise. Contrary to other class-level settings, `@Field`s
157+
will not be inherited by child classes.
158+
134159
Form Views
135160
----------
136161

Tests/FormGeneratorTest.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function testDefaultForm($model, $expectedFields, $additionalCheck = null
3939
public function provideDefaultForm()
4040
{
4141
return [
42+
[new Model\DeprecatedDisplay(), ['title']],
4243
[new Model\Simple(), ['title']],
4344
[new Model\SimpleNotOverridingDefaultView(), ['author', 'title']],
4445
[new Model\SimpleOverridingDefaultView(), ['title', 'author'], function($phpunit, $form) {
@@ -56,7 +57,8 @@ public function provideDefaultForm()
5657
$phpunit->assertEquals('foo', $options['attr']['class']);
5758
$optionsIgnoreInlinedFields = $form->get('optionsIgnoreInlinedFields')->getConfig()->getOptions();
5859
$phpunit->assertEquals('foo', $optionsIgnoreInlinedFields['attr']['class']);
59-
}]
60+
}],
61+
[new Model\ClassLevelFields(), ['title', 'reset', 'submit']],
6062
];
6163
}
6264

@@ -195,7 +197,7 @@ public function testFormAnnotationViewIsInherited()
195197
$this->checkForm(new Model\Director(), ['title', 'name', 'surname', 'photo', 'active'], null, 'personal');
196198
}
197199

198-
public function testFormAnnotationViewCanBeOverriden()
200+
public function testFormAnnotationViewCanBeOverridden()
199201
{
200202
$this->checkForm(new Model\Director(), ['salary', 'department'], null, 'work');
201203
}
@@ -204,6 +206,14 @@ public function testAllParentsAreCheckedForDefaultFormView()
204206
{
205207
$this->checkForm(new Model\InheritanceTest(), ['title', 'author']);
206208
}
209+
210+
public function testFormViewCanAffectClassLevelFields()
211+
{
212+
$this->checkForm(new Model\ClassLevelFields(), ['submit', 'title'], function($phpunit, $form) {
213+
$normal = $form->get('submit')->getConfig()->getOptions();
214+
$phpunit->assertEquals('Click me', $normal['label']);
215+
}, 'tweaked');
216+
}
207217

208218
protected function checkForm($model, $expectedFields, callable $additionalCheck = null, $form = 'default', $context = [])
209219
{

Tests/Model/ClassLevelFields.php

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\Model;
4+
5+
use Codete\FormGeneratorBundle\Annotations as Form;
6+
7+
/**
8+
* @Form\Form(
9+
* tweaked = { "submit" = { "label" = "Click me" }, "title" }
10+
* )
11+
* @Form\Field("reset", type="reset")
12+
* @Form\Field("submit", type="submit")
13+
*/
14+
class ClassLevelFields
15+
{
16+
/**
17+
* @Form\Field(type="text")
18+
*/
19+
public $title;
20+
}

Tests/Model/DeprecatedDisplay.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Codete\FormGeneratorBundle\Tests\Model;
4+
5+
use Codete\FormGeneratorBundle\Annotations as Form;
6+
7+
class DeprecatedDisplay
8+
{
9+
public $id;
10+
11+
/**
12+
* @Form\Display(type="text")
13+
*/
14+
public $title;
15+
}

Tests/Model/Director.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
class Director extends Person
1313
{
1414
/**
15-
* @Form\Display(type="text")
15+
* @Form\Field(type="text")
1616
*/
1717
public $department;
1818
}

Tests/Model/DisplayOptions.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
class DisplayOptions
88
{
99
/**
10-
* @Form\Display(type="text", attr = { "class" = "foo" })
10+
* @Form\Field(type="text", attr = { "class" = "foo" })
1111
*/
1212
public $normal;
1313

1414
/**
15-
* @Form\Display(type="text", options = { "attr" = { "class" = "foo" } })
15+
* @Form\Field(type="text", options = { "attr" = { "class" = "foo" } })
1616
*/
1717
public $options;
1818

1919
/**
20-
* @Form\Display(type="text", options = { "attr" = { "class" = "foo" } }, attr = { "class" = "bar" })
20+
* @Form\Field(type="text", options = { "attr" = { "class" = "foo" } }, attr = { "class" = "bar" })
2121
*/
2222
public $optionsIgnoreInlinedFields;
2323
}

0 commit comments

Comments
 (0)