4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Form\Extension\Core\EventListener
;
14 use Symfony\Component\EventDispatcher\EventSubscriberInterface
;
15 use Symfony\Component\Form\FormEvents
;
16 use Symfony\Component\Form\FormEvent
;
17 use Symfony\Component\Form\Exception\UnexpectedTypeException
;
20 * @author Bernhard Schussek <bschussek@gmail.com>
22 class MergeCollectionListener
implements EventSubscriberInterface
25 * Whether elements may be added to the collection
31 * Whether elements may be removed from the collection
37 * Creates a new listener.
39 * @param Boolean $allowAdd Whether values might be added to the
41 * @param Boolean $allowDelete Whether values might be removed from the
44 public function __construct($allowAdd = false, $allowDelete = false)
46 $this->allowAdd
= $allowAdd;
47 $this->allowDelete
= $allowDelete;
50 public static function getSubscribedEvents()
53 FormEvents
::SUBMIT
=> 'onSubmit',
57 public function onSubmit(FormEvent
$event)
59 $dataToMergeInto = $event->getForm()->getNormData();
60 $data = $event->getData();
66 if (!is_array($data) && !($data instanceof \Traversable
&& $data instanceof \ArrayAccess
)) {
67 throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
70 if (null !== $dataToMergeInto && !is_array($dataToMergeInto) && !($dataToMergeInto instanceof \Traversable
&& $dataToMergeInto instanceof \ArrayAccess
)) {
71 throw new UnexpectedTypeException($dataToMergeInto, 'array or (\Traversable and \ArrayAccess)');
74 // If we are not allowed to change anything, return immediately
75 if ((!$this->allowAdd
&& !$this->allowDelete
) || $data === $dataToMergeInto) {
76 $event->setData($dataToMergeInto);
81 if (!$dataToMergeInto) {
82 // No original data was set. Set it if allowed
83 if ($this->allowAdd
) {
84 $dataToMergeInto = $data;
88 $itemsToAdd = is_object($data) ? clone $data : $data;
89 $itemsToDelete = array();
91 foreach ($dataToMergeInto as $beforeKey => $beforeItem) {
92 foreach ($data as $afterKey => $afterItem) {
93 if ($afterItem === $beforeItem) {
94 // Item found, next original item
95 unset($itemsToAdd[$afterKey]);
100 // Item not found, remember for deletion
101 $itemsToDelete[] = $beforeKey;
104 // Remove deleted items before adding to free keys that are to be
106 if ($this->allowDelete
) {
107 foreach ($itemsToDelete as $key) {
108 unset($dataToMergeInto[$key]);
112 // Add remaining items
113 if ($this->allowAdd
) {
114 foreach ($itemsToAdd as $key => $item) {
115 if (!isset($dataToMergeInto[$key])) {
116 $dataToMergeInto[$key] = $item;
118 $dataToMergeInto[] = $item;
124 $event->setData($dataToMergeInto);
128 * Alias of {@link onSubmit()}.
130 * @deprecated Deprecated since version 2.3, to be removed in 3.0. Use
131 * {@link onSubmit()} instead.
133 public function onBind(FormEvent
$event)
135 $this->onSubmit($event);