[Form] Fixed MergeCollectionListener when used with a custom property path

This commit is contained in:
Bernhard Schussek 2012-02-09 17:03:48 +01:00
parent b56502f023
commit da2447e118
3 changed files with 98 additions and 18 deletions

View File

@ -18,6 +18,7 @@ use Symfony\Component\Form\Event\FilterDataEvent;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Util\FormUtil;
use Symfony\Component\Form\Util\PropertyPath;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
@ -150,10 +151,31 @@ class MergeCollectionListener implements EventSubscriberInterface
$form = $event->getForm();
$data = $event->getData();
$parentData = $form->hasParent() ? $form->getParent()->getClientData() : null;
$childPropertyPath = null;
$parentData = null;
$addMethod = null;
$removeMethod = null;
$getMethod = null;
$propertyPath = null;
$plural = null;
if ($form->hasParent() && $form->getAttribute('property_path')) {
$propertyPath = new PropertyPath($form->getAttribute('property_path'));
$childPropertyPath = $propertyPath;
$parentData = $form->getParent()->getClientData();
$lastElement = $propertyPath->getElement($propertyPath->getLength() - 1);
// If the property path contains more than one element, the parent
// data is the object at the parent property path
if ($propertyPath->getLength() > 1) {
$parentData = $propertyPath->getParent()->getValue($parentData);
// Property path relative to $parentData
$childPropertyPath = new PropertyPath($lastElement);
}
// The plural form is the last element of the property path
$plural = ucfirst($lastElement);
}
if (null === $data) {
$data = array();
@ -169,7 +191,6 @@ class MergeCollectionListener implements EventSubscriberInterface
// Check if the parent has matching methods to add/remove items
if (($this->mergeStrategy & self::MERGE_INTO_PARENT) && is_object($parentData)) {
$plural = ucfirst($form->getName());
$reflClass = new \ReflectionClass($parentData);
$addMethodNeeded = $this->allowAdd && !$this->addMethod;
$removeMethodNeeded = $this->allowDelete && !$this->removeMethod;
@ -235,18 +256,6 @@ class MergeCollectionListener implements EventSubscriberInterface
));
}
}
if ($addMethod || $removeMethod) {
$getMethod = 'get' . $plural;
if (!$this->isAccessible($reflClass, $getMethod, 0)) {
throw new FormException(sprintf(
'The public method "%s" could not be found on class %s',
$getMethod,
$reflClass->getName()
));
}
}
}
// Calculate delta between $data and the snapshot created in PRE_BIND
@ -287,7 +296,7 @@ class MergeCollectionListener implements EventSubscriberInterface
}
}
$event->setData($parentData->$getMethod());
$event->setData($childPropertyPath->getValue($parentData));
} elseif ($this->mergeStrategy & self::MERGE_NORMAL) {
if (!$originalData) {
// No original data was set. Set it if allowed

View File

@ -110,6 +110,16 @@ class PropertyPath implements \IteratorAggregate
return $this->string;
}
/**
* Returns the length of the property path.
*
* @return integer
*/
public function getLength()
{
return $this->length;
}
/**
* Returns the parent property path.
*

View File

@ -49,6 +49,20 @@ class MergeCollectionListenerTest_CarOnlyRemover
public function getAxes() {}
}
class MergeCollectionListenerTest_CompositeCar
{
public function getStructure() {}
}
class MergeCollectionListenerTest_CarStructure
{
public function addAxis($axis) {}
public function removeAxis($axis) {}
public function getAxes() {}
}
abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
{
private $dispatcher;
@ -74,9 +88,11 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
return new FormBuilder($name, $this->factory, $this->dispatcher);
}
protected function getForm($name = 'name')
protected function getForm($name = 'name', $propertyPath = null)
{
return $this->getBuilder($name)->getForm();
$propertyPath = $propertyPath ?: $name;
return $this->getBuilder($name)->setAttribute('property_path', $propertyPath)->getForm();
}
protected function getMockForm()
@ -359,6 +375,51 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('RESULT', $event->getData());
}
/**
* @dataProvider getModesWithMergeIntoParent
*/
public function testCallAdderIfCustomPropertyPath($mode)
{
$this->form = $this->getForm('structure_axes', 'structure.axes');
$parentData = $this->getMock(__CLASS__ . '_CompositeCar');
$parentForm = $this->getForm('car');
$parentForm->setData($parentData);
$parentForm->add($this->form);
$modifData = $this->getMock(__CLASS__ . '_CarStructure');
$originalDataArray = array(1 => 'second');
$originalData = $this->getData($originalDataArray);
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
$listener = new MergeCollectionListener(true, false, $mode);
$this->form->setData($originalData);
$event = new DataEvent($this->form, $newData);
$listener->preBind($event);
$parentData->expects($this->once())
->method('getStructure')
->will($this->returnValue($modifData));
$modifData->expects($this->at(0))
->method('addAxis')
->with('first');
$modifData->expects($this->at(1))
->method('addAxis')
->with('third');
$modifData->expects($this->at(2))
->method('getAxes')
->will($this->returnValue('RESULT'));
$event = new FilterDataEvent($this->form, $newData);
$listener->onBindNormData($event);
$this->assertEquals('RESULT', $event->getData());
}
/**
* @dataProvider getModesWithMergeIntoParent
*/