فهرست منبع

[Form] Fixed reference handling in forms. Sometimes data wasn't written into the domain object, resulting in failed validation.

Bernhard Schussek 14 سال پیش
والد
کامیت
7dbc09ed8b
2فایلهای تغییر یافته به همراه37 افزوده شده و 5 حذف شده
  1. 11 5
      src/Symfony/Component/Form/Form.php
  2. 26 0
      tests/Symfony/Tests/Component/Form/FormTest.php

+ 11 - 5
src/Symfony/Component/Form/Form.php

@@ -911,12 +911,18 @@ class Form extends Field implements \IteratorAggregate, FormInterface
      */
     public function writeProperty(&$objectOrArray)
     {
-        $data = $this->getData();
+        $isReference = false;
 
-        // Don't update parent if data is a composite type (object or array)
-        // and "by_reference" option is true, because then we expect that
-        // we are working with a reference to the parent's data
-        if (!is_object($data) || !is_object($objectOrArray) || !$this->getOption('by_reference')) {
+        // If the data is identical to the value in $objectOrArray, we are
+        // dealing with a reference
+        if ($this->getPropertyPath() !== null) {
+            $isReference = $this->getData() === $this->getPropertyPath()->getValue($objectOrArray);
+        }
+
+        // Don't write into $objectOrArray if $objectOrArray is an object,
+        // $isReference is true (see above) and the option "by_reference" is
+        // true as well
+        if (!is_object($objectOrArray) || !$isReference || !$this->getOption('by_reference')) {
             parent::writeProperty($objectOrArray);
         }
     }

+ 26 - 0
tests/Symfony/Tests/Component/Form/FormTest.php

@@ -1246,6 +1246,32 @@ class FormTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals('Foo', $author->getReference()->firstName);
     }
 
+    public function testSubformCallsSettersIfTheObjectChanged()
+    {
+        // no reference
+        $author = new FormTest_AuthorWithoutRefSetter(null);
+        $newReference = new Author();
+
+        $form = new Form('author', array('validator' => $this->createMockValidator()));
+        $form->setData($author);
+        $refForm = new Form('referenceCopy');
+        $refForm->add(new TestField('firstName'));
+        $form->add($refForm);
+
+        $refForm->setData($newReference); // new author object
+
+        $form->bind($this->createPostRequest(array(
+            'author' => array(
+                // referenceCopy has a getter that returns a copy
+                'referenceCopy' => array(
+                    'firstName' => 'Foo',
+                )
+            )
+        )));
+
+        $this->assertEquals('Foo', $author->getReferenceCopy()->firstName);
+    }
+
     public function testSubformCallsSettersIfByReferenceIsFalse()
     {
         $author = new FormTest_AuthorWithoutRefSetter(new Author());