[Form] Implemented MergeCollectionListener which calls addXxx() and removeXxx() in your model if found
The listener is used by the Collection type as well as the Choice and Entity type (with multiple selection). The effect is that you can have for example this model: class Article { public function addTag($tag) { ... } public function removeTag($tag) { ... } public function getTags($tag) { ... } } You can create a form for the article with a field "tags" of either type "collection" or "choice" (or "entity"). The field will correctly use the three methods of the model for displaying and editing tags.
This commit is contained in:
parent
7837f50c73
commit
49d1464b43
@ -195,6 +195,9 @@ 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_list" option is set
|
||||
* the radio type is now a child of the checkbox type
|
||||
* the Collection, Choice (with multiple selection) and Entity (with multiple
|
||||
selection) types now make use of addXxx() and removeXxx() methods in your
|
||||
model
|
||||
|
||||
### HttpFoundation
|
||||
|
||||
|
@ -24,11 +24,13 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
*
|
||||
* @see Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
class MergeCollectionListener implements EventSubscriberInterface
|
||||
class MergeDoctrineCollectionListener implements EventSubscriberInterface
|
||||
{
|
||||
static public function getSubscribedEvents()
|
||||
{
|
||||
return array(FormEvents::BIND_NORM_DATA => 'onBindNormData');
|
||||
// Higher priority than core MergeCollectionListener so that this one
|
||||
// is called before
|
||||
return array(FormEvents::BIND_NORM_DATA => array('onBindNormData', 10));
|
||||
}
|
||||
|
||||
public function onBindNormData(FilterDataEvent $event)
|
||||
@ -36,25 +38,10 @@ class MergeCollectionListener implements EventSubscriberInterface
|
||||
$collection = $event->getForm()->getData();
|
||||
$data = $event->getData();
|
||||
|
||||
if (!$collection) {
|
||||
$collection = $data;
|
||||
} elseif (count($data) === 0) {
|
||||
// If all items were removed, call clear which has a higher
|
||||
// performance on persistent collections
|
||||
if ($collection && count($data) === 0) {
|
||||
$collection->clear();
|
||||
} else {
|
||||
// merge $data into $collection
|
||||
foreach ($collection as $entity) {
|
||||
if (!$data->contains($entity)) {
|
||||
$collection->removeElement($entity);
|
||||
} else {
|
||||
$data->removeElement($entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $entity) {
|
||||
$collection->add($entity);
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($collection);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface;
|
||||
use Symfony\Bridge\Doctrine\Form\EventListener\MergeCollectionListener;
|
||||
use Symfony\Bridge\Doctrine\Form\EventListener\MergeDoctrineCollectionListener;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
@ -36,7 +36,7 @@ abstract class DoctrineType extends AbstractType
|
||||
{
|
||||
if ($options['multiple']) {
|
||||
$builder
|
||||
->addEventSubscriber(new MergeCollectionListener())
|
||||
->addEventSubscriber(new MergeDoctrineCollectionListener())
|
||||
->prependClientTransformer(new CollectionToArrayTransformer())
|
||||
;
|
||||
}
|
||||
|
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\Form\Util\FormUtil;
|
||||
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\Event\FilterDataEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class MergeCollectionListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Whether elements may be added to the collection
|
||||
* @var Boolean
|
||||
*/
|
||||
private $allowAdd;
|
||||
|
||||
/**
|
||||
* Whether elements may be removed from the collection
|
||||
* @var Boolean
|
||||
*/
|
||||
private $allowDelete;
|
||||
|
||||
public function __construct($allowAdd = false, $allowDelete = false)
|
||||
{
|
||||
$this->allowAdd = $allowAdd;
|
||||
$this->allowDelete = $allowDelete;
|
||||
}
|
||||
|
||||
static public function getSubscribedEvents()
|
||||
{
|
||||
return array(FormEvents::BIND_NORM_DATA => 'onBindNormData');
|
||||
}
|
||||
|
||||
public function onBindNormData(FilterDataEvent $event)
|
||||
{
|
||||
$originalData = $event->getForm()->getData();
|
||||
$form = $event->getForm();
|
||||
$data = $event->getData();
|
||||
$parentData = $form->hasParent() ? $form->getParent()->getData() : null;
|
||||
$adder = null;
|
||||
$remover = null;
|
||||
|
||||
if (null === $data) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
if (null !== $originalData && !is_array($originalData) && !($originalData instanceof \Traversable && $originalData instanceof \ArrayAccess)) {
|
||||
throw new UnexpectedTypeException($originalData, 'array or (\Traversable and \ArrayAccess)');
|
||||
}
|
||||
|
||||
// Check if the parent has matching methods to add/remove items
|
||||
if (is_object($parentData)) {
|
||||
$plural = ucfirst($form->getName());
|
||||
$singulars = (array) FormUtil::singularify($plural);
|
||||
$reflClass = new \ReflectionClass($parentData);
|
||||
|
||||
foreach ($singulars as $singular) {
|
||||
$adderName = 'add' . $singular;
|
||||
$removerName = 'remove' . $singular;
|
||||
|
||||
if ($reflClass->hasMethod($adderName) && $reflClass->hasMethod($removerName)) {
|
||||
$adder = $reflClass->getMethod($adderName);
|
||||
$remover = $reflClass->getMethod($removerName);
|
||||
|
||||
if ($adder->isPublic() && $adder->getNumberOfRequiredParameters() === 1
|
||||
&& $remover->isPublic() && $remover->getNumberOfRequiredParameters() === 1) {
|
||||
|
||||
// We found a public, one-parameter add and remove method
|
||||
break;
|
||||
}
|
||||
|
||||
// False alert
|
||||
$adder = null;
|
||||
$remover = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check which items are in $data that are not in $originalData and
|
||||
// vice versa
|
||||
$itemsToDelete = array();
|
||||
$itemsToAdd = is_object($data) ? clone $data : $data;
|
||||
|
||||
if ($originalData) {
|
||||
foreach ($originalData as $originalKey => $originalItem) {
|
||||
foreach ($data as $key => $item) {
|
||||
if ($item === $originalItem) {
|
||||
// Item found, next original item
|
||||
unset($itemsToAdd[$key]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Item not found, remember for deletion
|
||||
$itemsToDelete[$originalKey] = $originalItem;
|
||||
}
|
||||
}
|
||||
|
||||
if ($adder && $remover) {
|
||||
// If methods to add and to remove exist, call them now, if allowed
|
||||
if ($this->allowDelete) {
|
||||
foreach ($itemsToDelete as $item) {
|
||||
$remover->invoke($parentData, $item);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->allowAdd) {
|
||||
foreach ($itemsToAdd as $item) {
|
||||
$adder->invoke($parentData, $item);
|
||||
}
|
||||
}
|
||||
} elseif (!$originalData) {
|
||||
// No original data was set. Set it if allowed
|
||||
if ($this->allowAdd) {
|
||||
$originalData = $data;
|
||||
}
|
||||
} else {
|
||||
// Original data is an array-like structure
|
||||
// Add and remove items in the original variable
|
||||
if ($this->allowDelete) {
|
||||
foreach ($itemsToDelete as $key => $item) {
|
||||
unset($originalData[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->allowAdd) {
|
||||
foreach ($itemsToAdd as $key => $item) {
|
||||
if (!isset($originalData[$key])) {
|
||||
$originalData[$key] = $item;
|
||||
} else {
|
||||
$originalData[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$event->setData($originalData);
|
||||
}
|
||||
}
|
@ -66,7 +66,8 @@ class ResizeFormListener implements EventSubscriberInterface
|
||||
return array(
|
||||
FormEvents::PRE_SET_DATA => 'preSetData',
|
||||
FormEvents::PRE_BIND => 'preBind',
|
||||
FormEvents::BIND_NORM_DATA => 'onBindNormData',
|
||||
// (MergeCollectionListener, MergeDoctrineCollectionListener)
|
||||
FormEvents::BIND_NORM_DATA => array('onBindNormData', 50),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
|
||||
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer;
|
||||
@ -80,7 +81,10 @@ class ChoiceType extends AbstractType
|
||||
|
||||
if ($options['expanded']) {
|
||||
if ($options['multiple']) {
|
||||
$builder->appendClientTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list']));
|
||||
$builder
|
||||
->appendClientTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list']))
|
||||
->addEventSubscriber(new MergeCollectionListener(true, true))
|
||||
;
|
||||
} else {
|
||||
$builder
|
||||
->appendClientTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list']))
|
||||
@ -89,12 +93,14 @@ class ChoiceType extends AbstractType
|
||||
}
|
||||
} else {
|
||||
if ($options['multiple']) {
|
||||
$builder->appendClientTransformer(new ChoicesToValuesTransformer($options['choice_list']));
|
||||
$builder
|
||||
->appendClientTransformer(new ChoicesToValuesTransformer($options['choice_list']))
|
||||
->addEventSubscriber(new MergeCollectionListener(true, true))
|
||||
;
|
||||
} else {
|
||||
$builder->appendClientTransformer(new ChoiceToValueTransformer($options['choice_list']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener;
|
||||
|
||||
class CollectionType extends AbstractType
|
||||
{
|
||||
@ -29,7 +30,7 @@ class CollectionType extends AbstractType
|
||||
$builder->setAttribute('prototype', $prototype->getForm());
|
||||
}
|
||||
|
||||
$listener = new ResizeFormListener(
|
||||
$resizeListener = new ResizeFormListener(
|
||||
$builder->getFormFactory(),
|
||||
$options['type'],
|
||||
$options['options'],
|
||||
@ -37,8 +38,14 @@ class CollectionType extends AbstractType
|
||||
$options['allow_delete']
|
||||
);
|
||||
|
||||
$mergeListener = new MergeCollectionListener(
|
||||
$options['allow_add'],
|
||||
$options['allow_delete']
|
||||
);
|
||||
|
||||
$builder
|
||||
->addEventSubscriber($listener)
|
||||
->addEventSubscriber($resizeListener)
|
||||
->addEventSubscriber($mergeListener)
|
||||
->setAttribute('allow_add', $options['allow_add'])
|
||||
->setAttribute('allow_delete', $options['allow_delete'])
|
||||
;
|
||||
|
@ -83,8 +83,8 @@ abstract class FormUtil
|
||||
// hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
|
||||
array('sev', 3, true, true, 'f'),
|
||||
|
||||
// axes (axis)
|
||||
array('sexa', 4, false, false, 'axis'),
|
||||
// axes (axis), axes (ax), axes (axe)
|
||||
array('sexa', 4, false, false, array('ax', 'axe', 'axis')),
|
||||
|
||||
// indexes (index), matrixes (matrix)
|
||||
array('sex', 3, true, false, 'x'),
|
||||
|
@ -344,13 +344,13 @@ class EntityTypeTest extends TypeTestCase
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$existing = new ArrayCollection(array($entity2));
|
||||
$existing = new ArrayCollection(array(0 => $entity2));
|
||||
|
||||
$field->setData($existing);
|
||||
$field->bind(array('1', '3'));
|
||||
|
||||
// entry with index 0 was removed
|
||||
$expected = new ArrayCollection(array(1 => $entity1, 2 => $entity3));
|
||||
// entry with index 0 ($entity2) was replaced
|
||||
$expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3));
|
||||
|
||||
$this->assertTrue($field->isSynchronized());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
@ -406,8 +406,8 @@ class EntityTypeTest extends TypeTestCase
|
||||
$field->setData($existing);
|
||||
$field->bind(array('0', '2'));
|
||||
|
||||
// entry with index 0 was removed
|
||||
$expected = new ArrayCollection(array(1 => $entity1, 2 => $entity3));
|
||||
// entry with index 0 ($entity2) was replaced
|
||||
$expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3));
|
||||
|
||||
$this->assertTrue($field->isSynchronized());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
class MergeCollectionListenerArrayObjectTest extends MergeCollectionListenerTest
|
||||
{
|
||||
protected function getData(array $data)
|
||||
{
|
||||
return new \ArrayObject($data);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
class MergeCollectionListenerArrayTest extends MergeCollectionListenerTest
|
||||
{
|
||||
protected function getData(array $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
/**
|
||||
* This class is a hand written simplified version of PHP native `ArrayObject`
|
||||
* class, to show that it behaves differently than the PHP native implementation.
|
||||
*/
|
||||
class MergeCollectionListenerCustomArrayObjectTest_CustomArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable
|
||||
{
|
||||
private $array;
|
||||
|
||||
public function __construct(array $array = null)
|
||||
{
|
||||
$this->array = (array) ($array ?: array());
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->array);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->array[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (null === $offset) {
|
||||
$this->array[] = $value;
|
||||
} else {
|
||||
$this->array[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if (array_key_exists($offset, $this->array)) {
|
||||
unset($this->array[$offset]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->array);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->array);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->array);
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->array = (array) unserialize((string) $serialized);
|
||||
}
|
||||
}
|
||||
|
||||
class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListenerTest
|
||||
{
|
||||
protected function getData(array $data)
|
||||
{
|
||||
$class = __CLASS__ . '_CustomArrayObject';
|
||||
|
||||
return new $class($data);
|
||||
}
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Core\EventListener;
|
||||
|
||||
use Symfony\Component\Form\Event\DataEvent;
|
||||
use Symfony\Component\Form\Event\FilterDataEvent;
|
||||
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
|
||||
class MergeCollectionListenerTest_Car
|
||||
{
|
||||
// In the test, use a name that FormUtil can't uniquely singularify
|
||||
public function addAxis($axis) {}
|
||||
|
||||
public function removeAxis($axis) {}
|
||||
}
|
||||
|
||||
abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $dispatcher;
|
||||
private $factory;
|
||||
private $form;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||
$this->form = $this->getForm('axes');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->dispatcher = null;
|
||||
$this->factory = null;
|
||||
$this->form = null;
|
||||
}
|
||||
|
||||
protected function getBuilder($name = 'name')
|
||||
{
|
||||
return new FormBuilder($name, $this->factory, $this->dispatcher);
|
||||
}
|
||||
|
||||
protected function getForm($name = 'name')
|
||||
{
|
||||
return $this->getBuilder($name)->getForm();
|
||||
}
|
||||
|
||||
protected function getMockForm()
|
||||
{
|
||||
return $this->getMock('Symfony\Tests\Component\Form\FormInterface');
|
||||
}
|
||||
|
||||
abstract protected function getData(array $data);
|
||||
|
||||
public function testAddExtraEntriesIfAllowAdd()
|
||||
{
|
||||
$originalData = $this->getData(array(1 => 'second'));
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(true, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
// The original object matches the new object
|
||||
$this->assertEquals($newData, $event->getData());
|
||||
}
|
||||
|
||||
public function testAddExtraEntriesIfAllowAddDontOverwriteExistingIndices()
|
||||
{
|
||||
$originalData = $this->getData(array(1 => 'first'));
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(true, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
// The original object matches the new object
|
||||
$this->assertEquals($this->getData(array(1 => 'first', 2 => 'second')), $event->getData());
|
||||
}
|
||||
|
||||
public function testDoNothingIfNotAllowAdd()
|
||||
{
|
||||
$originalDataArray = array(1 => 'second');
|
||||
$originalData = $this->getData($originalDataArray);
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// We still have the original object
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
// Nothing was removed
|
||||
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||
}
|
||||
|
||||
public function testRemoveMissingEntriesIfAllowDelete()
|
||||
{
|
||||
$originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
$newData = $this->getData(array(1 => 'second'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, true);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
// The original object matches the new object
|
||||
$this->assertEquals($newData, $event->getData());
|
||||
}
|
||||
|
||||
public function testDoNothingIfNotAllowDelete()
|
||||
{
|
||||
$originalDataArray = array(0 => 'first', 1 => 'second', 2 => 'third');
|
||||
$originalData = $this->getData($originalDataArray);
|
||||
$newData = $this->getData(array(1 => 'second'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// We still have the original object
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
// Nothing was removed
|
||||
$this->assertEquals($this->getData($originalDataArray), $event->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
|
||||
*/
|
||||
public function testRequireArrayOrTraversable()
|
||||
{
|
||||
$newData = 'no array or traversable';
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, false);
|
||||
$listener->onBindNormData($event);
|
||||
}
|
||||
|
||||
public function testDealWithNullData()
|
||||
{
|
||||
$originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
$newData = null;
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
|
||||
public function testDealWithNullOriginalDataIfAllowAdd()
|
||||
{
|
||||
$originalData = null;
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(true, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
$this->assertSame($newData, $event->getData());
|
||||
}
|
||||
|
||||
public function testDontDealWithNullOriginalDataIfNotAllowAdd()
|
||||
{
|
||||
$originalData = null;
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
$this->assertNull($event->getData());
|
||||
}
|
||||
|
||||
public function testCallAdderIfAllowAdd()
|
||||
{
|
||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||
$parentForm = $this->getForm('article');
|
||||
$parentForm->setData($parentData);
|
||||
$parentForm->add($this->form);
|
||||
|
||||
$originalData = $this->getData(array(1 => 'second'));
|
||||
$newData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$parentData->expects($this->at(0))
|
||||
->method('addAxis')
|
||||
->with('first');
|
||||
$parentData->expects($this->at(1))
|
||||
->method('addAxis')
|
||||
->with('third');
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(true, false);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
}
|
||||
|
||||
public function testDontCallAdderIfNotAllowAdd()
|
||||
{
|
||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||
$parentForm = $this->getForm('article');
|
||||
$parentForm->setData($parentData);
|
||||
$parentForm->add($this->form);
|
||||
|
||||
$originalData = $this->getData(array(1 => 'second'));
|
||||
$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);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
}
|
||||
|
||||
public function testCallRemoverIfAllowDelete()
|
||||
{
|
||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||
$parentForm = $this->getForm('article');
|
||||
$parentForm->setData($parentData);
|
||||
$parentForm->add($this->form);
|
||||
|
||||
$originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
$newData = $this->getData(array(1 => 'second'));
|
||||
|
||||
$this->form->setData($originalData);
|
||||
|
||||
$parentData->expects($this->at(0))
|
||||
->method('removeAxis')
|
||||
->with('first');
|
||||
$parentData->expects($this->at(1))
|
||||
->method('removeAxis')
|
||||
->with('third');
|
||||
|
||||
$event = new FilterDataEvent($this->form, $newData);
|
||||
$listener = new MergeCollectionListener(false, true);
|
||||
$listener->onBindNormData($event);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
}
|
||||
|
||||
public function testDontCallRemoverIfNotAllowDelete()
|
||||
{
|
||||
$parentData = $this->getMock(__CLASS__ . '_Car');
|
||||
$parentForm = $this->getForm('article');
|
||||
$parentForm->setData($parentData);
|
||||
$parentForm->add($this->form);
|
||||
|
||||
$originalData = $this->getData(array(0 => 'first', 1 => 'second', 2 => 'third'));
|
||||
$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);
|
||||
|
||||
// The original object was modified
|
||||
if (is_object($originalData)) {
|
||||
$this->assertSame($originalData, $event->getData());
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ namespace Symfony\Tests\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\Form;
|
||||
|
||||
class CollectionFormTest extends TypeTestCase
|
||||
class CollectionTypeTest extends TypeTestCase
|
||||
{
|
||||
public function testContainsNoFieldByDefault()
|
||||
{
|
||||
@ -80,12 +80,12 @@ class CollectionFormTest extends TypeTestCase
|
||||
'allow_delete' => true,
|
||||
));
|
||||
$form->setData(array('foo@foo.com', 'bar@bar.com'));
|
||||
$form->bind(array('foo@bar.com'));
|
||||
$form->bind(array('foo@foo.com'));
|
||||
|
||||
$this->assertTrue($form->has('0'));
|
||||
$this->assertFalse($form->has('1'));
|
||||
$this->assertEquals('foo@bar.com', $form[0]->getData());
|
||||
$this->assertEquals(array('foo@bar.com'), $form->getData());
|
||||
$this->assertEquals('foo@foo.com', $form[0]->getData());
|
||||
$this->assertEquals(array('foo@foo.com'), $form->getData());
|
||||
}
|
||||
|
||||
public function testNotResizedIfBoundWithExtraData()
|
||||
@ -108,13 +108,13 @@ class CollectionFormTest extends TypeTestCase
|
||||
'allow_add' => true,
|
||||
));
|
||||
$form->setData(array('foo@bar.com'));
|
||||
$form->bind(array('foo@foo.com', 'bar@bar.com'));
|
||||
$form->bind(array('foo@bar.com', 'bar@bar.com'));
|
||||
|
||||
$this->assertTrue($form->has('0'));
|
||||
$this->assertTrue($form->has('1'));
|
||||
$this->assertEquals('foo@foo.com', $form[0]->getData());
|
||||
$this->assertEquals('foo@bar.com', $form[0]->getData());
|
||||
$this->assertEquals('bar@bar.com', $form[1]->getData());
|
||||
$this->assertEquals(array('foo@foo.com', 'bar@bar.com'), $form->getData());
|
||||
$this->assertEquals(array('foo@bar.com', 'bar@bar.com'), $form->getData());
|
||||
}
|
||||
|
||||
public function testAllowAddButNoPrototype()
|
||||
|
@ -87,7 +87,7 @@ class FormUtilTest extends \PHPUnit_Framework_TestCase
|
||||
array('alumni', 'alumnus'),
|
||||
array('funguses', array('fungus', 'funguse', 'fungusis')),
|
||||
array('fungi', 'fungus'),
|
||||
array('axes', 'axis'),
|
||||
array('axes', array('ax', 'axe', 'axis')),
|
||||
array('appendices', array('appendex', 'appendix')),
|
||||
array('indices', array('index', 'indix')),
|
||||
array('indexes', 'index'),
|
||||
@ -123,7 +123,6 @@ class FormUtilTest extends \PHPUnit_Framework_TestCase
|
||||
array('houses', array('hous', 'house', 'housis')),
|
||||
array('arches', array('arch', 'arche')),
|
||||
array('atlases', array('atlas', 'atlase', 'atlasis')),
|
||||
// array('axes', 'axe'),
|
||||
array('batches', array('batch', 'batche')),
|
||||
array('bushes', array('bush', 'bushe')),
|
||||
array('buses', array('bus', 'buse', 'busis')),
|
||||
|
Reference in New Issue
Block a user