Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/106'
Browse files Browse the repository at this point in the history
Close #106
Close #103
Fixes #102
  • Loading branch information
weierophinney committed Sep 14, 2016
2 parents 2d60b97 + cee7953 commit ef4788d
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ All notable changes to this project will be documented in this file, in reverse
ensuring that the initializer injecting a factory into a `FormFactoryAware`
instance is triggered before the initializer that calls `init()`, and also
that the initializer calling `init()` is always triggered last.
- [#106](https://github.com/zendframework/zend-form/pull/106) updates behavior
around binding collection values to a fieldset or form such that if the
collection is not part of the current validation group, its value will not be
overwritten with an empty set.

## 2.9.0 - 2016-06-07

Expand Down
27 changes: 27 additions & 0 deletions doc/book/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -816,3 +816,30 @@ cannot say, "validate the name input for the first element of the categories
collection, but don't validate it for the second one."

Now, the form validates (and the `url` is set to null as we didn't specify it).

## Preventing validation from wiping out previous collection items

In some cases, you may be representing collections within a model, but not
validating them; as an example, if you use a validation group that excludes the
collections from validation so that they remain untouched after binding.

Starting in 2.8.4, behavior around collections changed in order to fix some
underlying bugs. One such change is that if a collection is found in a form, but
has no associated data, an empty array is assigned to it, even when not in the
validation group. This effectively wipes out the collection data when you bind
values.

To prevent this behavior, starting in 2.9.1 you may pass an optional second
argument to `bindValues()` on either a fieldset or collection,
`$validationGroup`; when present, these instances will first check if the
collection is in the validation group before binding the value; if it is not,
the collection will not be represented. The `Form` class has been updated to
pass the validation group, if present, on to fieldset and collection instances
when performing `bindValues()` operations.

For more details, refer to the following issues:

- [zendframework/zend-form#19](https://github.com/zendframework/zend-form/pull/19)
- [zendframework/zend-form#102](https://github.com/zendframework/zend-form/pull/102)
- [zendframework/zend-form#103](https://github.com/zendframework/zend-form/pull/103)
- [zendframework/zend-form#106](https://github.com/zendframework/zend-form/pull/106)
6 changes: 4 additions & 2 deletions src/Element/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,18 @@ public function allowValueBinding()
* Bind values to the object
*
* @param array $values
* @param array $validationGroup
*
* @return array|mixed|void
*/
public function bindValues(array $values = [])
public function bindValues(array $values = [], array $validationGroup = null)
{
$collection = [];
foreach ($values as $name => $value) {
$element = $this->get($name);

if ($element instanceof FieldsetInterface) {
$collection[] = $element->bindValues($value);
$collection[] = $element->bindValues($value, $validationGroup);
} else {
$collection[] = $value;
}
Expand Down
10 changes: 8 additions & 2 deletions src/Fieldset.php
Original file line number Diff line number Diff line change
Expand Up @@ -557,9 +557,11 @@ public function allowValueBinding()
* Bind values to the bound object
*
* @param array $values
* @param array $validationGroup
*
* @return mixed|void
*/
public function bindValues(array $values = [])
public function bindValues(array $values = [], array $validationGroup = null)
{
$objectData = $this->extract();
$hydrator = $this->getHydrator();
Expand All @@ -568,6 +570,10 @@ public function bindValues(array $values = [])
foreach ($this->iterator as $element) {
$name = $element->getName();

if ($validationGroup && (array_key_exists($name, $validationGroup) || !in_array($name, $validationGroup))) {
continue;
}

if (!array_key_exists($name, $values)) {
if (!($element instanceof Collection)) {
continue;
Expand All @@ -579,7 +585,7 @@ public function bindValues(array $values = [])
$value = $values[$name];

if ($element instanceof FieldsetInterface && $element->allowValueBinding()) {
$value = $element->bindValues($value);
$value = $element->bindValues($value, empty($validationGroup[$name]) ? null : $validationGroup[$name]);
}

// skip post values for disabled elements, get old value from object
Expand Down
5 changes: 3 additions & 2 deletions src/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,13 +357,14 @@ public function bindValues(array $values = [])
}

$data = $this->prepareBindData($data, $this->data);
$validationGroup = $this->getValidationGroup();

// If there is a base fieldset, only hydrate beginning from the base fieldset
if ($this->baseFieldset !== null) {
$data = $data[$this->baseFieldset->getName()];
$this->object = $this->baseFieldset->bindValues($data);
$this->object = $this->baseFieldset->bindValues($data, $validationGroup[$this->baseFieldset->getName()]);
} else {
$this->object = parent::bindValues($data);
$this->object = parent::bindValues($data, $validationGroup);
}
}

Expand Down
29 changes: 29 additions & 0 deletions test/FormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,35 @@ public function testSettingValidationGroupBindsOnlyThoseValuesToModel()
$this->assertObjectNotHasAttribute('foobar', $model);
}

public function testSettingValidationGroupWithoutCollectionBindsOnlyThoseValuesToModel()
{
$model = new stdClass;
$dataWithoutCollection = [
'foo' => 'abcde'
];
$this->populateForm();
$this->form->add([
'type' => 'Zend\Form\Element\Collection',
'name' => 'categories',
'options' => [
'count' => 0,
'target_element' => [
'type' => 'ZendTest\Form\TestAsset\CategoryFieldset'
]
]
]);
$this->form->setHydrator(new Hydrator\ObjectProperty());
$this->form->bind($model);
$this->form->setData($dataWithoutCollection);
$this->form->setValidationGroup(['foo']);
$this->form->isValid();

$this->assertObjectHasAttribute('foo', $model);
$this->assertEquals('abcde', $model->foo);
$this->assertObjectNotHasAttribute('categories', $model);
$this->assertObjectNotHasAttribute('foobar', $model);
}

public function testCanBindModelsToArraySerializableObjects()
{
$model = new TestAsset\Model();
Expand Down

0 comments on commit ef4788d

Please sign in to comment.