[Form] Made prefix of adder and remover method configurable. Adders and removers are not called if "by_reference" is disabled.
This commit is contained in:
parent
49d1464b43
commit
9b0245b57b
@ -195,9 +195,11 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
|
|||||||
* choice fields now throw a FormException if neither the "choices" nor the
|
* choice fields now throw a FormException if neither the "choices" nor the
|
||||||
"choice_list" option is set
|
"choice_list" option is set
|
||||||
* the radio type is now a child of the checkbox type
|
* the radio type is now a child of the checkbox type
|
||||||
* the Collection, Choice (with multiple selection) and Entity (with multiple
|
* the collection, choice (with multiple selection) and entity (with multiple
|
||||||
selection) types now make use of addXxx() and removeXxx() methods in your
|
selection) types now make use of addXxx() and removeXxx() methods in your
|
||||||
model
|
model
|
||||||
|
* added options "adder_prefix" and "remover_prefix" to collection and choice
|
||||||
|
type
|
||||||
|
|
||||||
### HttpFoundation
|
### HttpFoundation
|
||||||
|
|
||||||
|
@ -35,10 +35,31 @@ class MergeCollectionListener implements EventSubscriberInterface
|
|||||||
*/
|
*/
|
||||||
private $allowDelete;
|
private $allowDelete;
|
||||||
|
|
||||||
public function __construct($allowAdd = false, $allowDelete = false)
|
/**
|
||||||
|
* Whether to search for and use adder and remover methods
|
||||||
|
* @var Boolean
|
||||||
|
*/
|
||||||
|
private $useAccessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix of the adder method to look for
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $adderPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix of the remover method to look for
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $removerPrefix;
|
||||||
|
|
||||||
|
public function __construct($allowAdd = false, $allowDelete = false, $useAccessors = true, $adderPrefix = 'add', $removerPrefix = 'remove')
|
||||||
{
|
{
|
||||||
$this->allowAdd = $allowAdd;
|
$this->allowAdd = $allowAdd;
|
||||||
$this->allowDelete = $allowDelete;
|
$this->allowDelete = $allowDelete;
|
||||||
|
$this->useAccessors = $useAccessors;
|
||||||
|
$this->adderPrefix = $adderPrefix;
|
||||||
|
$this->removerPrefix = $removerPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function getSubscribedEvents()
|
static public function getSubscribedEvents()
|
||||||
@ -68,14 +89,14 @@ class MergeCollectionListener implements EventSubscriberInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the parent has matching methods to add/remove items
|
// Check if the parent has matching methods to add/remove items
|
||||||
if (is_object($parentData)) {
|
if ($this->useAccessors && is_object($parentData)) {
|
||||||
$plural = ucfirst($form->getName());
|
$plural = ucfirst($form->getName());
|
||||||
$singulars = (array) FormUtil::singularify($plural);
|
$singulars = (array) FormUtil::singularify($plural);
|
||||||
$reflClass = new \ReflectionClass($parentData);
|
$reflClass = new \ReflectionClass($parentData);
|
||||||
|
|
||||||
foreach ($singulars as $singular) {
|
foreach ($singulars as $singular) {
|
||||||
$adderName = 'add' . $singular;
|
$adderName = $this->adderPrefix . $singular;
|
||||||
$removerName = 'remove' . $singular;
|
$removerName = $this->removerPrefix . $singular;
|
||||||
|
|
||||||
if ($reflClass->hasMethod($adderName) && $reflClass->hasMethod($removerName)) {
|
if ($reflClass->hasMethod($adderName) && $reflClass->hasMethod($removerName)) {
|
||||||
$adder = $reflClass->getMethod($adderName);
|
$adder = $reflClass->getMethod($adderName);
|
||||||
|
@ -95,7 +95,17 @@ class ChoiceType extends AbstractType
|
|||||||
if ($options['multiple']) {
|
if ($options['multiple']) {
|
||||||
$builder
|
$builder
|
||||||
->appendClientTransformer(new ChoicesToValuesTransformer($options['choice_list']))
|
->appendClientTransformer(new ChoicesToValuesTransformer($options['choice_list']))
|
||||||
->addEventSubscriber(new MergeCollectionListener(true, true))
|
->addEventSubscriber(new MergeCollectionListener(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
// If "by_reference" is disabled (explicit calling of
|
||||||
|
// the setter is desired), disable support for
|
||||||
|
// adders/removers
|
||||||
|
// Same as in CollectionType
|
||||||
|
$options['by_reference'],
|
||||||
|
$options['adder_prefix'],
|
||||||
|
$options['remover_prefix']
|
||||||
|
))
|
||||||
;
|
;
|
||||||
} else {
|
} else {
|
||||||
$builder->appendClientTransformer(new ChoiceToValueTransformer($options['choice_list']));
|
$builder->appendClientTransformer(new ChoiceToValueTransformer($options['choice_list']));
|
||||||
@ -146,6 +156,8 @@ class ChoiceType extends AbstractType
|
|||||||
'empty_data' => $multiple || $expanded ? array() : '',
|
'empty_data' => $multiple || $expanded ? array() : '',
|
||||||
'empty_value' => $multiple || $expanded || !isset($options['empty_value']) ? null : '',
|
'empty_value' => $multiple || $expanded || !isset($options['empty_value']) ? null : '',
|
||||||
'error_bubbling' => false,
|
'error_bubbling' => false,
|
||||||
|
'adder_prefix' => 'add',
|
||||||
|
'remover_prefix' => 'remove',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,13 @@ class CollectionType extends AbstractType
|
|||||||
|
|
||||||
$mergeListener = new MergeCollectionListener(
|
$mergeListener = new MergeCollectionListener(
|
||||||
$options['allow_add'],
|
$options['allow_add'],
|
||||||
$options['allow_delete']
|
$options['allow_delete'],
|
||||||
|
// If "by_reference" is disabled (explicit calling of the setter
|
||||||
|
// is desired), disable support for adders/removers
|
||||||
|
// Same as in ChoiceType
|
||||||
|
$options['by_reference'],
|
||||||
|
$options['adder_prefix'],
|
||||||
|
$options['remover_prefix']
|
||||||
);
|
);
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
@ -84,6 +90,8 @@ class CollectionType extends AbstractType
|
|||||||
return array(
|
return array(
|
||||||
'allow_add' => false,
|
'allow_add' => false,
|
||||||
'allow_delete' => false,
|
'allow_delete' => false,
|
||||||
|
'adder_prefix' => 'add',
|
||||||
|
'remover_prefix' => 'remove',
|
||||||
'prototype' => true,
|
'prototype' => true,
|
||||||
'prototype_name' => '__name__',
|
'prototype_name' => '__name__',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
@ -24,6 +24,13 @@ class MergeCollectionListenerTest_Car
|
|||||||
public function removeAxis($axis) {}
|
public function removeAxis($axis) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MergeCollectionListenerTest_CarCustomPrefix
|
||||||
|
{
|
||||||
|
public function fooAxis($axis) {}
|
||||||
|
|
||||||
|
public function barAxis($axis) {}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
private $dispatcher;
|
private $dispatcher;
|
||||||
@ -219,11 +226,12 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
public function testCallAdderIfAllowAdd()
|
public function testCallAdderIfAllowAdd()
|
||||||
{
|
{
|
||||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
$parentForm = $this->getForm('article');
|
$parentForm = $this->getForm('car');
|
||||||
$parentForm->setData($parentData);
|
$parentForm->setData($parentData);
|
||||||
$parentForm->add($this->form);
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
$originalData = $this->getData(array(1 => 'second'));
|
$originalDataArray = array(1 => 'second');
|
||||||
|
$originalData = $this->getData($originalDataArray);
|
||||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||||
|
|
||||||
$this->form->setData($originalData);
|
$this->form->setData($originalData);
|
||||||
@ -239,16 +247,47 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$listener = new MergeCollectionListener(true, false);
|
$listener = new MergeCollectionListener(true, false);
|
||||||
$listener->onBindNormData($event);
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
// The original object was modified
|
|
||||||
if (is_object($originalData)) {
|
if (is_object($originalData)) {
|
||||||
$this->assertSame($originalData, $event->getData());
|
$this->assertSame($originalData, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The data was not modified directly
|
||||||
|
// Thus it should not be written back into the parent data!
|
||||||
|
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDontCallAdderIfNotAllowAdd()
|
public function testDontCallAdderIfNotAllowAdd()
|
||||||
{
|
{
|
||||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
$parentForm = $this->getForm('article');
|
$parentForm = $this->getForm('car');
|
||||||
|
$parentForm->setData($parentData);
|
||||||
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
|
$originalDataArray = array(1 => 'second');
|
||||||
|
$originalData = $this->getData($originalDataArray);
|
||||||
|
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||||
|
|
||||||
|
$this->form->setData($originalData);
|
||||||
|
|
||||||
|
$parentData->expects($this->never())
|
||||||
|
->method('addAxis');
|
||||||
|
|
||||||
|
$event = new FilterDataEvent($this->form, $newData);
|
||||||
|
$listener = new MergeCollectionListener(false, false);
|
||||||
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
|
if (is_object($originalData)) {
|
||||||
|
$this->assertSame($originalData, $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data was not modified
|
||||||
|
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontCallAdderIfNotUseAccessors()
|
||||||
|
{
|
||||||
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
|
$parentForm = $this->getForm('car');
|
||||||
$parentForm->setData($parentData);
|
$parentForm->setData($parentData);
|
||||||
$parentForm->add($this->form);
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
@ -261,23 +300,26 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
->method('addAxis');
|
->method('addAxis');
|
||||||
|
|
||||||
$event = new FilterDataEvent($this->form, $newData);
|
$event = new FilterDataEvent($this->form, $newData);
|
||||||
$listener = new MergeCollectionListener(false, false);
|
$listener = new MergeCollectionListener(true, false, false);
|
||||||
$listener->onBindNormData($event);
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
// The original object was modified
|
|
||||||
if (is_object($originalData)) {
|
if (is_object($originalData)) {
|
||||||
$this->assertSame($originalData, $event->getData());
|
$this->assertSame($originalData, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The data was modified without accessors
|
||||||
|
$this->assertEquals($newData, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCallRemoverIfAllowDelete()
|
public function testCallRemoverIfAllowDelete()
|
||||||
{
|
{
|
||||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
$parentForm = $this->getForm('article');
|
$parentForm = $this->getForm('car');
|
||||||
$parentForm->setData($parentData);
|
$parentForm->setData($parentData);
|
||||||
$parentForm->add($this->form);
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
$originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
$originalDataArray = array(0 => 'first', 1 => 'second', 2 => 'third');
|
||||||
|
$originalData = $this->getData($originalDataArray);
|
||||||
$newData = $this->getData(array(1 => 'second'));
|
$newData = $this->getData(array(1 => 'second'));
|
||||||
|
|
||||||
$this->form->setData($originalData);
|
$this->form->setData($originalData);
|
||||||
@ -293,16 +335,47 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
$listener = new MergeCollectionListener(false, true);
|
$listener = new MergeCollectionListener(false, true);
|
||||||
$listener->onBindNormData($event);
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
// The original object was modified
|
|
||||||
if (is_object($originalData)) {
|
if (is_object($originalData)) {
|
||||||
$this->assertSame($originalData, $event->getData());
|
$this->assertSame($originalData, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The data was not modified directly
|
||||||
|
// Thus it should not be written back into the parent data!
|
||||||
|
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDontCallRemoverIfNotAllowDelete()
|
public function testDontCallRemoverIfNotAllowDelete()
|
||||||
{
|
{
|
||||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
$parentForm = $this->getForm('article');
|
$parentForm = $this->getForm('car');
|
||||||
|
$parentForm->setData($parentData);
|
||||||
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
|
$originalDataArray = array(0 => 'first', 1 => 'second', 2 => 'third');
|
||||||
|
$originalData = $this->getData($originalDataArray);
|
||||||
|
$newData = $this->getData(array(1 => 'second'));
|
||||||
|
|
||||||
|
$this->form->setData($originalData);
|
||||||
|
|
||||||
|
$parentData->expects($this->never())
|
||||||
|
->method('removeAxis');
|
||||||
|
|
||||||
|
$event = new FilterDataEvent($this->form, $newData);
|
||||||
|
$listener = new MergeCollectionListener(false, false);
|
||||||
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
|
if (is_object($originalData)) {
|
||||||
|
$this->assertSame($originalData, $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data was not modified
|
||||||
|
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontCallRemoverIfNotUseAccessors()
|
||||||
|
{
|
||||||
|
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||||
|
$parentForm = $this->getForm('car');
|
||||||
$parentForm->setData($parentData);
|
$parentForm->setData($parentData);
|
||||||
$parentForm->add($this->form);
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
@ -315,12 +388,47 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
|||||||
->method('removeAxis');
|
->method('removeAxis');
|
||||||
|
|
||||||
$event = new FilterDataEvent($this->form, $newData);
|
$event = new FilterDataEvent($this->form, $newData);
|
||||||
$listener = new MergeCollectionListener(false, false);
|
$listener = new MergeCollectionListener(false, true, false);
|
||||||
$listener->onBindNormData($event);
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
// The original object was modified
|
|
||||||
if (is_object($originalData)) {
|
if (is_object($originalData)) {
|
||||||
$this->assertSame($originalData, $event->getData());
|
$this->assertSame($originalData, $event->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The data was modified directly
|
||||||
|
$this->assertEquals($newData, $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallAccessorsWithCustomPrefixes()
|
||||||
|
{
|
||||||
|
$parentData = $this->getMock(__CLASS__ . '_CarCustomPrefix');
|
||||||
|
$parentForm = $this->getForm('car');
|
||||||
|
$parentForm->setData($parentData);
|
||||||
|
$parentForm->add($this->form);
|
||||||
|
|
||||||
|
$originalDataArray = array(1 => 'second');
|
||||||
|
$originalData = $this->getData($originalDataArray);
|
||||||
|
$newData = $this->getData(array(0 => 'first'));
|
||||||
|
|
||||||
|
$this->form->setData($originalData);
|
||||||
|
|
||||||
|
$parentData->expects($this->once())
|
||||||
|
->method('fooAxis')
|
||||||
|
->with('first');
|
||||||
|
$parentData->expects($this->once())
|
||||||
|
->method('barAxis')
|
||||||
|
->with('second');
|
||||||
|
|
||||||
|
$event = new FilterDataEvent($this->form, $newData);
|
||||||
|
$listener = new MergeCollectionListener(true, true, true, 'foo', 'bar');
|
||||||
|
$listener->onBindNormData($event);
|
||||||
|
|
||||||
|
if (is_object($originalData)) {
|
||||||
|
$this->assertSame($originalData, $event->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data was not modified directly
|
||||||
|
// Thus it should not be written back into the parent data!
|
||||||
|
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user