Skip to content

Commit e60fbf4

Browse files
committed
merged branch bschussek/issue5899 (PR #6573)
This PR was merged into the master branch. Discussion ---------- [2.3] [Form] Renamed option "virtual" to "inherit_data" and improved handling of such forms Bug fix: yes Feature addition: yes Backwards compatibility break: yes Symfony2 tests pass: yes Fixes the following tickets: #5899, #5720, #5578 Todo: - License of the code: MIT Documentation PR: symfony/symfony-docs#2107 This PR renames the option "virtual" to "inherit_data" for more clarity (the old option is deprecated and usable until 2.3). It also fixes the behavior of forms having that option set. Forms with that option set will now correctly return their parents' data from `getData()`, `getNormData()` and `getViewData()`. Furthermore, `getPropertyPath()` was fixed for forms that inherit their parent data. Commits ------- 1290b80 [Form] Fixed the deprecation notes for the "virtual" option ac2ca44 [Form] Moved parent data inheritance from data mappers to Form 8ea5e1a [Form] Renamed option "virtual" to "inherit_data"
2 parents 36a0ae1 + 84293cc commit e60fbf4

17 files changed

+518
-208
lines changed

ButtonBuilder.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,18 @@ public function setFormProcessor(FormProcessorInterface $formProcessor)
492492
throw new \BadMethodCallException('Buttons do not support form processors.');
493493
}
494494

495+
/**
496+
* Unsupported method.
497+
*
498+
* @param Boolean $inheritData
499+
*
500+
* @throws \BadMethodCallException
501+
*/
502+
public function setInheritData($inheritData)
503+
{
504+
throw new \BadMethodCallException('Buttons do not support data inheritance.');
505+
}
506+
495507
/**
496508
* Builds and returns the button configuration.
497509
*
@@ -759,6 +771,16 @@ public function getFormProcessor()
759771
return null;
760772
}
761773

774+
/**
775+
* Unsupported method.
776+
*
777+
* @return null Always returns null.
778+
*/
779+
public function getInheritData()
780+
{
781+
return null;
782+
}
783+
762784
/**
763785
* Returns all options passed during the construction of the button.
764786
*

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ CHANGELOG
1111
* added FormProcessorInterface and FormInterface::process()
1212
* deprecated passing a Request instance to FormInterface::bind()
1313
* added options "method" and "action" to FormType
14+
* deprecated option "virtual" in favor "inherit_data"
15+
* deprecated VirtualFormAwareIterator in favor of InheritDataAwareIterator
16+
* [BC BREAK] removed the "array" type hint from DataMapperInterface
17+
* improved forms inheriting their parent data to actually return that data from getData(), getNormData() and getViewData()
1418

1519
2.2.0
1620
-----

DataMapperInterface.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ interface DataMapperInterface
1919
/**
2020
* Maps properties of some data to a list of forms.
2121
*
22-
* @param mixed $data Structured data.
23-
* @param array $forms A list of {@link FormInterface} instances.
22+
* @param mixed $data Structured data.
23+
* @param FormInterface[] $forms A list of {@link FormInterface} instances.
2424
*
2525
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
2626
*/
27-
public function mapDataToForms($data, array $forms);
27+
public function mapDataToForms($data, $forms);
2828

2929
/**
3030
* Maps the data of a list of forms into the properties of some data.
3131
*
32-
* @param array $forms A list of {@link FormInterface} instances.
33-
* @param mixed $data Structured data.
32+
* @param FormInterface[] $forms A list of {@link FormInterface} instances.
33+
* @param mixed $data Structured data.
3434
*
3535
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
3636
*/
37-
public function mapFormsToData(array $forms, &$data);
37+
public function mapFormsToData($forms, &$data);
3838
}

Exception/RuntimeException.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Exception;
13+
14+
/**
15+
* Base RuntimeException for the Form component.
16+
*
17+
* @author Bernhard Schussek <bschussek@gmail.com>
18+
*/
19+
class RuntimeException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}

Extension/Core/DataMapper/PropertyPathMapper.php

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Form\Extension\Core\DataMapper;
1313

1414
use Symfony\Component\Form\DataMapperInterface;
15-
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
1615
use Symfony\Component\Form\Exception\UnexpectedTypeException;
1716
use Symfony\Component\PropertyAccess\PropertyAccess;
1817
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -42,7 +41,7 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
4241
/**
4342
* {@inheritdoc}
4443
*/
45-
public function mapDataToForms($data, array $forms)
44+
public function mapDataToForms($data, $forms)
4645
{
4746
if (null === $data || array() === $data) {
4847
return;
@@ -52,11 +51,7 @@ public function mapDataToForms($data, array $forms)
5251
throw new UnexpectedTypeException($data, 'object, array or empty');
5352
}
5453

55-
$iterator = new VirtualFormAwareIterator($forms);
56-
$iterator = new \RecursiveIteratorIterator($iterator);
57-
58-
foreach ($iterator as $form) {
59-
/* @var FormInterface $form */
54+
foreach ($forms as $form) {
6055
$propertyPath = $form->getPropertyPath();
6156
$config = $form->getConfig();
6257

@@ -69,7 +64,7 @@ public function mapDataToForms($data, array $forms)
6964
/**
7065
* {@inheritdoc}
7166
*/
72-
public function mapFormsToData(array $forms, &$data)
67+
public function mapFormsToData($forms, &$data)
7368
{
7469
if (null === $data) {
7570
return;
@@ -79,11 +74,7 @@ public function mapFormsToData(array $forms, &$data)
7974
throw new UnexpectedTypeException($data, 'object, array or empty');
8075
}
8176

82-
$iterator = new VirtualFormAwareIterator($forms);
83-
$iterator = new \RecursiveIteratorIterator($iterator);
84-
85-
foreach ($iterator as $form) {
86-
/* @var FormInterface $form */
77+
foreach ($forms as $form) {
8778
$propertyPath = $form->getPropertyPath();
8879
$config = $form->getConfig();
8980

Extension/Core/Type/FormType.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
4848
->setPropertyPath($options['property_path'])
4949
->setMapped($options['mapped'])
5050
->setByReference($options['by_reference'])
51-
->setVirtual($options['virtual'])
51+
->setInheritData($options['inherit_data'])
5252
->setCompound($options['compound'])
5353
->setData(isset($options['data']) ? $options['data'] : null)
5454
->setDataLocked(isset($options['data']))
@@ -95,8 +95,8 @@ public function buildView(FormView $view, FormInterface $form, array $options)
9595
'size' => null,
9696
'label_attr' => $options['label_attr'],
9797
'compound' => $form->getConfig()->getCompound(),
98-
'method' => $form->getConfig()->getMethod(),
99-
'action' => $form->getConfig()->getAction(),
98+
'method' => $form->getConfig()->getMethod(),
99+
'action' => $form->getConfig()->getAction(),
100100
));
101101
}
102102

@@ -150,6 +150,18 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
150150
return $options['compound'];
151151
};
152152

153+
// BC with old "virtual" option
154+
$inheritData = function (Options $options) {
155+
if (null !== $options['virtual']) {
156+
// Uncomment this as soon as the deprecation note should be shown
157+
// trigger_error('The form option "virtual" is deprecated since version 2.3 and will be removed in 3.0. Use "inherit_data" instead.', E_USER_DEPRECATED);
158+
159+
return $options['virtual'];
160+
}
161+
162+
return false;
163+
};
164+
153165
// If data is given, the form is locked to that data
154166
// (independent of its value)
155167
$resolver->setOptional(array(
@@ -169,7 +181,8 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
169181
'by_reference' => true,
170182
'error_bubbling' => $errorBubbling,
171183
'label_attr' => array(),
172-
'virtual' => false,
184+
'virtual' => null,
185+
'inherit_data' => $inheritData,
173186
'compound' => true,
174187
'method' => 'POST',
175188
// According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt)

Extension/Validator/ViolationMapper/ViolationMapper.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
1313

1414
use Symfony\Component\Form\FormInterface;
15-
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
15+
use Symfony\Component\Form\Util\InheritDataAwareIterator;
1616
use Symfony\Component\PropertyAccess\PropertyPathIterator;
1717
use Symfony\Component\PropertyAccess\PropertyPathBuilder;
1818
use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
@@ -88,8 +88,8 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
8888
}
8989

9090
// This case happens if an error happened in the data under a
91-
// virtual form that does not match any of the children of
92-
// the virtual form.
91+
// form inheriting its parent data that does not match any of the
92+
// children of that form.
9393
if (null !== $violationPath && !$match) {
9494
// If we could not map the error to anything more specific
9595
// than the root element, map it to the innermost directly
@@ -100,6 +100,9 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form
100100
$scope = $form;
101101
$it = new ViolationPathIterator($violationPath);
102102

103+
// Note: acceptsErrors() will always return true for forms inheriting
104+
// their parent data, because these forms can never be non-synchronized
105+
// (they don't do any data transformation on their own)
103106
while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) {
104107
if (!$scope->has($it->current())) {
105108
// Break if we find a reference to a non-existing child
@@ -162,9 +165,9 @@ private function matchChild(FormInterface $form, PropertyPathIteratorInterface $
162165
}
163166
}
164167

165-
// Ignore virtual forms when iterating the children
168+
// Skip forms inheriting their parent data when iterating the children
166169
$childIterator = new \RecursiveIteratorIterator(
167-
new VirtualFormAwareIterator($form->all())
170+
new InheritDataAwareIterator($form->all())
168171
);
169172

170173
// Make the path longer until we find a matching child
@@ -253,8 +256,8 @@ private function reconstructPath(ViolationPath $violationPath, FormInterface $or
253256
// Process child form
254257
$scope = $scope->get($it->current());
255258

256-
if ($scope->getConfig()->getVirtual()) {
257-
// Form is virtual
259+
if ($scope->getConfig()->getInheritData()) {
260+
// Form inherits its parent data
258261
// Cut the piece out of the property path and proceed
259262
$propertyPathBuilder->remove($i);
260263
} elseif (!$scope->getConfig()->getMapped()) {

0 commit comments

Comments
 (0)