/** * {@inheritdoc} */ public function submit($submittedData, $clearMissing = true) { if ($this->submitted) { throw new AlreadySubmittedException('A form can only be submitted once'); } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); // Obviously, a disabled form should not change its data upon submission. if ($this->isDisabled()) { $this->submitted = true; return $this; } // The data must be initialized if it was not initialized yet. // This is necessary to guarantee that the *_SET_DATA listeners // are always invoked before submit() takes place. if (!$this->defaultDataSet) { $this->setData($this->config->getData()); } // Treat false as NULL to support binding false to checkboxes. // Don't convert NULL to a string here in order to determine later // whether an empty value has been submitted or whether no value has // been submitted at all. This is important for processing checkboxes // and radio buttons with empty values. if (false === $submittedData) { $submittedData = null; } elseif (is_scalar($submittedData)) { $submittedData = (string) $submittedData; } $dispatcher = $this->config->getEventDispatcher(); $modelData = null; $normData = null; $viewData = null; try { // Hook to change content of the data submitted by the browser if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) { $event = new FormEvent($this, $submittedData); $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event); $submittedData = $event->getData(); } // Check whether the form is compound. // This check is preferable over checking the number of children, // since forms without children may also be compound. // (think of empty collection forms) if ($this->config->getCompound()) { if (null === $submittedData) { $submittedData = array(); } if (!is_array($submittedData)) { throw new TransformationFailedException('Compound forms expect an array or NULL on submission.'); } foreach ($this->children as $name => $child) { $isSubmitted = array_key_exists($name, $submittedData); if ($isSubmitted || $clearMissing) { $child->submit($isSubmitted ? $submittedData[$name] : null, $clearMissing); unset($submittedData[$name]); if (null !== $this->clickedButton) { continue; } if ($child instanceof ClickableInterface && $child->isClicked()) { $this->clickedButton = $child; continue; } if (method_exists($child, 'getClickedButton') && null !== $child->getClickedButton()) { $this->clickedButton = $child->getClickedButton(); } } } $this->extraData = $submittedData; } // Forms that inherit their parents' data also are not processed, // because then it would be too difficult to merge the changes in // the child and the parent form. Instead, the parent form also takes // changes in the grandchildren (i.e. children of the form that inherits // its parent's data) into account. // (see InheritDataAwareIterator below) if (!$this->config->getInheritData()) { // If the form is compound, the default data in view format // is reused. The data of the children is merged into this // default data using the data mapper. // If the form is not compound, the submitted data is also the data in view format. $viewData = $this->config->getCompound() ? $this->viewData : $submittedData; if (FormUtil::isEmpty($viewData)) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { /* @var \Closure $emptyData */ $emptyData = $emptyData($this, $viewData); } $viewData = $emptyData; } // Merge form data from children into existing view data // It is not necessary to invoke this method if the form has no children, // even if it is compound. if (count($this->children) > 0) { // Use InheritDataAwareIterator to process children of // descendants that inherit this form's data. // These descendants will not be submitted normally (see the check // for $this->config->getInheritData() above) $childrenIterator = new InheritDataAwareIterator($this->children); $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); $this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData); } // Normalize data to unified representation $normData = $this->viewToNorm($viewData); // Hook to change content of the data in the normalized // representation if ($dispatcher->hasListeners(FormEvents::SUBMIT)) { $event = new FormEvent($this, $normData); $dispatcher->dispatch(FormEvents::SUBMIT, $event); $normData = $event->getData(); } // Synchronize representations - must not change the content! $modelData = $this->normToModel($normData); $viewData = $this->normToView($normData); } } catch (TransformationFailedException $e) { $this->transformationFailure = $e; // If $viewData was not yet set, set it to $submittedData so that // the erroneous data is accessible on the form. // Forms that inherit data never set any data, because the getters // forward to the parent form's getters anyway. if (null === $viewData && !$this->config->getInheritData()) { $viewData = $submittedData; } } $this->submitted = true; $this->modelData = $modelData; $this->normData = $normData; $this->viewData = $viewData; if ($dispatcher->hasListeners(FormEvents::POST_SUBMIT)) { $event = new FormEvent($this, $viewData); $dispatcher->dispatch(FormEvents::POST_SUBMIT, $event); } return $this; }
/** * {@inheritdoc} */ public function bind($submittedData) { if ($this->bound) { throw new AlreadyBoundException('A form can only be bound once'); } if ($this->isDisabled()) { $this->bound = true; return $this; } // The data must be initialized if it was not initialized yet. // This is necessary to guarantee that the *_SET_DATA listeners // are always invoked before bind() takes place. if (!$this->initialized) { $this->setData($this->config->getData()); } // Don't convert NULL to a string here in order to determine later // whether an empty value has been submitted or whether no value has // been submitted at all. This is important for processing checkboxes // and radio buttons with empty values. if (is_scalar($submittedData)) { $submittedData = (string) $submittedData; } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); $dispatcher = $this->config->getEventDispatcher(); $modelData = null; $normData = null; $viewData = null; try { // Hook to change content of the data bound by the browser if ($dispatcher->hasListeners(FormEvents::PRE_BIND) || $dispatcher->hasListeners(FormEvents::BIND_CLIENT_DATA)) { $event = new FormEvent($this, $submittedData); $dispatcher->dispatch(FormEvents::PRE_BIND, $event); // BC until 2.3 if ($dispatcher->hasListeners(FormEvents::BIND_CLIENT_DATA)) { trigger_error('The FormEvents::BIND_CLIENT_DATA event is deprecated since 2.1 and will be removed in 2.3. Use the FormEvents::PRE_BIND event instead.', E_USER_DEPRECATED); } $dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event); $submittedData = $event->getData(); } // Check whether the form is compound. // This check is preferable over checking the number of children, // since forms without children may also be compound. // (think of empty collection forms) if ($this->config->getCompound()) { if (null === $submittedData) { $submittedData = array(); } if (!is_array($submittedData)) { throw new TransformationFailedException('Compound forms expect an array or NULL on submission.'); } for (reset($this->children); false !== current($this->children); next($this->children)) { $child = current($this->children); $name = key($this->children); $child->bind(isset($submittedData[$name]) ? $submittedData[$name] : null); unset($submittedData[$name]); } $this->extraData = $submittedData; // If the form is compound, the default data in view format // is reused. The data of the children is merged into this // default data using the data mapper. $viewData = $this->viewData; } else { // If the form is not compound, the submitted data is also the data in view format. $viewData = $submittedData; } if (FormUtil::isEmpty($viewData)) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { /* @var \Closure $emptyData */ $emptyData = $emptyData($this, $viewData); } $viewData = $emptyData; } // Merge form data from children into existing view data // It is not necessary to invoke this method if the form has no children, // even if it is compound. if (count($this->children) > 0) { $this->config->getDataMapper()->mapFormsToData($this->children, $viewData); } // Normalize data to unified representation $normData = $this->viewToNorm($viewData); // Hook to change content of the data into the normalized // representation if ($dispatcher->hasListeners(FormEvents::BIND) || $dispatcher->hasListeners(FormEvents::BIND_NORM_DATA)) { $event = new FormEvent($this, $normData); $dispatcher->dispatch(FormEvents::BIND, $event); // BC until 2.3 if ($dispatcher->hasListeners(FormEvents::BIND_NORM_DATA)) { trigger_error('The FormEvents::BIND_NORM_DATA event is deprecated since 2.1 and will be removed in 2.3. Use the FormEvents::BIND event instead.', E_USER_DEPRECATED); } $dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event); $normData = $event->getData(); } // Synchronize representations - must not change the content! $modelData = $this->normToModel($normData); $viewData = $this->normToView($normData); } catch (TransformationFailedException $e) { $this->synchronized = false; // If $viewData was not yet set, set it to $submittedData so that // the erroneous data is accessible on the form. if (null === $viewData) { $viewData = $submittedData; } } $this->bound = true; $this->modelData = $modelData; $this->normData = $normData; $this->viewData = $viewData; if ($dispatcher->hasListeners(FormEvents::POST_BIND)) { $event = new FormEvent($this, $viewData); $dispatcher->dispatch(FormEvents::POST_BIND, $event); } set_error_handler(array('Symfony\\Component\\Form\\Test\\DeprecationErrorHandler', 'handleBC')); $validators = $this->config->getValidators(); restore_error_handler(); foreach ($validators as $validator) { trigger_error(sprintf('FormConfigInterface::getValidators() is deprecated since 2.1 and will be removed in 2.3. Convert your %s class to a listener on the FormEvents::POST_BIND event.', get_class($validator)), E_USER_DEPRECATED); $validator->validate($this); } return $this; }
/** * {@inheritdoc} */ public function bind($submittedData) { if ($this->bound) { throw new AlreadyBoundException('A form can only be bound once'); } if ($this->isDisabled()) { $this->bound = true; return $this; } // The data must be initialized if it was not initialized yet. // This is necessary to guarantee that the *_SET_DATA listeners // are always invoked before bind() takes place. if (!$this->initialized) { $this->setData($this->config->getData()); } // Don't convert NULL to a string here in order to determine later // whether an empty value has been submitted or whether no value has // been submitted at all. This is important for processing checkboxes // and radio buttons with empty values. if (is_scalar($submittedData)) { $submittedData = (string) $submittedData; } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); $dispatcher = $this->config->getEventDispatcher(); // Hook to change content of the data bound by the browser if ($dispatcher->hasListeners(FormEvents::PRE_BIND) || $dispatcher->hasListeners(FormEvents::BIND_CLIENT_DATA)) { $event = new FormEvent($this, $submittedData); $dispatcher->dispatch(FormEvents::PRE_BIND, $event); // BC until 2.3 $dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event); $submittedData = $event->getData(); } // Check whether the form is compound. // This check is preferrable over checking the number of children, // since forms without children may also be compound. // (think of empty collection forms) if ($this->config->getCompound()) { if (!is_array($submittedData)) { $submittedData = array(); } foreach ($this->children as $name => $child) { $child->bind(isset($submittedData[$name]) ? $submittedData[$name] : null); unset($submittedData[$name]); } $this->extraData = $submittedData; // If the form is compound, the default data in view format // is reused. The data of the children is merged into this // default data using the data mapper. $viewData = $this->viewData; } else { // If the form is not compound, the submitted data is also the data in view format. $viewData = $submittedData; } if (FormUtil::isEmpty($viewData)) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { /* @var \Closure $emptyData */ $emptyData = $emptyData($this, $viewData); } $viewData = $emptyData; } // Merge form data from children into existing view data // It is not necessary to invoke this method if the form has no children, // even if it is compound. if (count($this->children) > 0) { $this->config->getDataMapper()->mapFormsToData($this->children, $viewData); } $modelData = null; $normData = null; try { // Normalize data to unified representation $normData = $this->viewToNorm($viewData); // Hook to change content of the data into the normalized // representation if ($dispatcher->hasListeners(FormEvents::BIND) || $dispatcher->hasListeners(FormEvents::BIND_NORM_DATA)) { $event = new FormEvent($this, $normData); $dispatcher->dispatch(FormEvents::BIND, $event); // BC until 2.3 $dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event); $normData = $event->getData(); } // Synchronize representations - must not change the content! $modelData = $this->normToModel($normData); $viewData = $this->normToView($normData); } catch (TransformationFailedException $e) { $this->synchronized = false; } $this->bound = true; $this->modelData = $modelData; $this->normData = $normData; $this->viewData = $viewData; if ($dispatcher->hasListeners(FormEvents::POST_BIND)) { $event = new FormEvent($this, $viewData); $dispatcher->dispatch(FormEvents::POST_BIND, $event); } foreach ($this->config->getValidators() as $validator) { $validator->validate($this); } return $this; }
/** * Creates an unmodifiable copy of a given configuration. * * @param FormConfigInterface $config The configuration to copy. */ public function __construct(FormConfigInterface $config) { $dispatcher = $config->getEventDispatcher(); if (!$dispatcher instanceof UnmodifiableEventDispatcher) { $dispatcher = new UnmodifiableEventDispatcher($dispatcher); } $this->dispatcher = $dispatcher; $this->name = $config->getName(); $this->propertyPath = $config->getPropertyPath(); $this->mapped = $config->getMapped(); $this->byReference = $config->getByReference(); $this->virtual = $config->getVirtual(); $this->compound = $config->getCompound(); $this->types = $config->getTypes(); $this->viewTransformers = $config->getViewTransformers(); $this->modelTransformers = $config->getModelTransformers(); $this->dataMapper = $config->getDataMapper(); $this->validators = $config->getValidators(); $this->required = $config->getRequired(); $this->disabled = $config->getDisabled(); $this->errorBubbling = $config->getErrorBubbling(); $this->emptyData = $config->getEmptyData(); $this->attributes = $config->getAttributes(); $this->data = $config->getData(); $this->dataClass = $config->getDataClass(); $this->options = $config->getOptions(); }
/** * Binds data to the form, transforms and validates it. * * @param string|array $submittedData The data * * @return Form The current form * * @throws UnexpectedTypeException */ public function bind($submittedData) { if ($this->bound) { throw new AlreadyBoundException('A form can only be bound once'); } if ($this->isDisabled()) { $this->bound = true; return $this; } // Don't convert NULL to a string here in order to determine later // whether an empty value has been submitted or whether no value has // been submitted at all. This is important for processing checkboxes // and radio buttons with empty values. if (is_scalar($submittedData)) { $submittedData = (string) $submittedData; } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); $modelData = null; $normData = null; $extraData = array(); $synchronized = false; // Hook to change content of the data bound by the browser $event = new FormEvent($this, $submittedData); $this->config->getEventDispatcher()->dispatch(FormEvents::PRE_BIND, $event); // BC until 2.3 $this->config->getEventDispatcher()->dispatch(FormEvents::BIND_CLIENT_DATA, $event); $submittedData = $event->getData(); // Check whether the form is compound. // This check is preferrable over checking the number of children, // since forms without children may also be compound. // (think of empty collection forms) if ($this->config->getCompound()) { if (null === $submittedData || '' === $submittedData) { $submittedData = array(); } if (!is_array($submittedData)) { throw new UnexpectedTypeException($submittedData, 'array'); } foreach ($this->children as $name => $child) { if (!isset($submittedData[$name])) { $submittedData[$name] = null; } } foreach ($submittedData as $name => $value) { if ($this->has($name)) { $this->children[$name]->bind($value); } else { $extraData[$name] = $value; } } } // By default, the submitted data is also the data in view format $viewData = $submittedData; // If the form is compound, the default data in view format // is reused. The data of the children is merged into this // default data using the data mapper. if ($this->config->getCompound() && $this->config->getDataMapper()) { $viewData = $this->getViewData(); } if (null === $viewData || '' === $viewData) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { /* @var \Closure $emptyData */ $emptyData = $emptyData($this, $viewData); } $viewData = $emptyData; } // Merge form data from children into existing view data if ($this->config->getCompound() && $this->config->getDataMapper() && null !== $viewData) { $this->config->getDataMapper()->mapFormsToData($this->children, $viewData); } try { // Normalize data to unified representation $normData = $this->viewToNorm($viewData); $synchronized = true; } catch (TransformationFailedException $e) { } if ($synchronized) { // Hook to change content of the data into the normalized // representation $event = new FormEvent($this, $normData); $this->config->getEventDispatcher()->dispatch(FormEvents::BIND, $event); // BC until 2.3 $this->config->getEventDispatcher()->dispatch(FormEvents::BIND_NORM_DATA, $event); $normData = $event->getData(); // Synchronize representations - must not change the content! $modelData = $this->normToModel($normData); $viewData = $this->normToView($normData); } $this->bound = true; $this->modelData = $modelData; $this->normData = $normData; $this->viewData = $viewData; $this->extraData = $extraData; $this->synchronized = $synchronized; $event = new FormEvent($this, $viewData); $this->config->getEventDispatcher()->dispatch(FormEvents::POST_BIND, $event); foreach ($this->config->getValidators() as $validator) { $validator->validate($this); } return $this; }