diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php index 96078ce20b58..5f1ae2d2e836 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php @@ -23,22 +23,24 @@ class PropertyPathMapper implements DataMapperInterface */ public function mapDataToForms($data, array $forms) { - if (!empty($data) && !is_array($data) && !is_object($data)) { + if (null === $data || array() === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { throw new UnexpectedTypeException($data, 'object, array or empty'); } - if (!empty($data)) { - $iterator = new VirtualFormAwareIterator($forms); - $iterator = new \RecursiveIteratorIterator($iterator); + $iterator = new VirtualFormAwareIterator($forms); + $iterator = new \RecursiveIteratorIterator($iterator); - foreach ($iterator as $form) { - /* @var FormInterface $form */ - $propertyPath = $form->getPropertyPath(); - $config = $form->getConfig(); + foreach ($iterator as $form) { + /* @var FormInterface $form */ + $propertyPath = $form->getPropertyPath(); + $config = $form->getConfig(); - if (null !== $propertyPath && $config->getMapped()) { - $form->setData($propertyPath->getValue($data)); - } + if (null !== $propertyPath && $config->getMapped()) { + $form->setData($propertyPath->getValue($data)); } } } @@ -48,6 +50,14 @@ public function mapDataToForms($data, array $forms) */ public function mapFormsToData(array $forms, &$data) { + if (null === $data) { + return; + } + + if (!is_array($data) && !is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + $iterator = new VirtualFormAwareIterator($forms); $iterator = new \RecursiveIteratorIterator($iterator); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index ee566feff91b..58135fed5015 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -15,6 +15,7 @@ use Symfony\Component\Form\Exception\AlreadyBoundException; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Util\FormUtil; use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -352,7 +353,7 @@ public function setData($modelData) $viewData = $this->normToView($normData); // Validate if view data matches data class (unless empty) - if ('' !== $viewData && null !== $viewData) { + if (!FormUtil::isEmpty($viewData)) { $dataClass = $this->config->getDataClass(); $actualType = is_object($viewData) ? 'an instance of class ' . get_class($viewData) : ' a(n) ' . gettype($viewData); @@ -492,12 +493,12 @@ public function bind($submittedData) // 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'); + if (!FormUtil::isEmpty($submittedData)) { + throw new UnexpectedTypeException($submittedData, 'array'); + } + + $submittedData = array(); } foreach ($this->children as $name => $child) { @@ -520,7 +521,7 @@ public function bind($submittedData) $viewData = $this->getViewData(); } - if (null === $viewData || '' === $viewData) { + if (FormUtil::isEmpty($viewData)) { $emptyData = $this->config->getEmptyData(); if ($emptyData instanceof \Closure) { @@ -532,7 +533,7 @@ public function bind($submittedData) } // Merge form data from children into existing view data - if ($this->config->getCompound() && null !== $viewData) { + if ($this->config->getCompound()) { $this->config->getDataMapper()->mapFormsToData($this->children, $viewData); } @@ -701,7 +702,7 @@ public function isEmpty() } } - return array() === $this->modelData || null === $this->modelData || '' === $this->modelData; + return FormUtil::isEmpty($this->modelData) || array() === $this->modelData; } /** @@ -1048,9 +1049,12 @@ private function normToModel($value) */ private function normToView($value) { - if (!$this->config->getViewTransformers()) { - // Scalar values should always be converted to strings to - // facilitate differentiation between empty ("") and zero (0). + // Scalar values should be converted to strings to + // facilitate differentiation between empty ("") and zero (0). + // Only do this for simple forms, as the resulting value in + // compound forms is passed to the data mapper and thus should + // not be converted to a string before. + if (!$this->config->getViewTransformers() && !$this->config->getCompound()) { return null === $value || is_scalar($value) ? (string) $value : $value; } diff --git a/src/Symfony/Component/Form/Util/FormUtil.php b/src/Symfony/Component/Form/Util/FormUtil.php index 251ebd040e77..868dab9610a5 100644 --- a/src/Symfony/Component/Form/Util/FormUtil.php +++ b/src/Symfony/Component/Form/Util/FormUtil.php @@ -219,4 +219,23 @@ static public function isChoiceSelected($choice, $value) return $choice === $value; } + + /** + * Returns whether the given data is empty. + * + * This logic is reused multiple times throughout the processing of + * a form and needs to be consistent. PHP's keyword `empty` cannot + * be used as it also considers 0 and "0" to be empty. + * + * @param mixed $data + * + * @return Boolean + */ + static public function isEmpty($data) + { + // Should not do a check for array() === $data!!! + // This method is used in occurrences where arrays are + // not considered to be empty, ever. + return null === $data || '' === $data; + } }