/** * Binds data to the field, transforms and validates it. * * @param string|array $clientData The data * * @return Form The current form * * @throws UnexpectedTypeException */ public function bind($clientData) { if ($this->readOnly) { $this->bound = true; return $this; } if (is_scalar($clientData) || null === $clientData) { $clientData = (string) $clientData; } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); $event = new DataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::PRE_BIND, $event); $appData = null; $normData = null; $extraData = array(); $synchronized = false; // Hook to change content of the data bound by the browser $event = new FilterDataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event); $clientData = $event->getData(); if (count($this->children) > 0) { if (null === $clientData || '' === $clientData) { $clientData = array(); } if (!is_array($clientData)) { throw new UnexpectedTypeException($clientData, 'array'); } foreach ($this->children as $name => $child) { if (!isset($clientData[$name])) { $clientData[$name] = null; } } foreach ($clientData as $name => $value) { if ($this->has($name)) { $this->children[$name]->bind($value); } else { $extraData[$name] = $value; } } // If we have a data mapper, use old client data and merge // data from the children into it later if ($this->dataMapper) { $clientData = $this->getClientData(); } } if (null === $clientData || '' === $clientData) { $clientData = $this->emptyData; if ($clientData instanceof \Closure) { $clientData = $clientData($this); } } // Merge form data from children into existing client data if (count($this->children) > 0 && $this->dataMapper) { $this->dataMapper->mapFormsToData($this->children, $clientData); } try { // Normalize data to unified representation $normData = $this->clientToNorm($clientData); $synchronized = true; } catch (TransformationFailedException $e) { } if ($synchronized) { // Hook to change content of the data in the normalized // representation $event = new FilterDataEvent($this, $normData); $this->dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event); $normData = $event->getData(); // Synchronize representations - must not change the content! $appData = $this->normToApp($normData); $clientData = $this->normToClient($normData); } $this->bound = true; $this->appData = $appData; $this->normData = $normData; $this->clientData = $clientData; $this->extraData = $extraData; $this->synchronized = $synchronized; $event = new DataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::POST_BIND, $event); foreach ($this->validators as $validator) { $validator->validate($this); } return $this; }
/** * Binds data to the field, transforms and validates it. * * @param string|array $clientData The data * * @return Form The current form * * @throws UnexpectedTypeException */ public function bind($clientData) { 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($clientData)) { $clientData = (string) $clientData; } // Initialize errors in the very beginning so that we don't lose any // errors added during listeners $this->errors = array(); $event = new DataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::PRE_BIND, $event); $appData = null; $normData = null; $extraData = array(); $synchronized = false; // Hook to change content of the data bound by the browser $event = new FilterDataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event); $clientData = $event->getData(); if (count($this->children) > 0) { if (null === $clientData || '' === $clientData) { $clientData = array(); } if (!is_array($clientData)) { throw new UnexpectedTypeException($clientData, 'array'); } foreach ($this->children as $name => $child) { if (!isset($clientData[$name])) { $clientData[$name] = null; } } foreach ($clientData as $name => $value) { if ($this->has($name)) { $this->children[$name]->bind($value); } else { $extraData[$name] = $value; } } // If we have a data mapper, use old client data and merge // data from the children into it later if ($this->dataMapper) { $clientData = $this->getClientData(); } } if (null === $clientData || '' === $clientData) { $emptyData = $this->emptyData; if ($emptyData instanceof \Closure) { $emptyData = $emptyData($this, $clientData); } $clientData = $emptyData; } // Merge form data from children into existing client data if (count($this->children) > 0 && $this->dataMapper && null !== $clientData) { $this->dataMapper->mapFormsToData($this->children, $clientData); } try { // Normalize data to unified representation $normData = $this->clientToNorm($clientData); $synchronized = true; } catch (TransformationFailedException $e) { } if ($synchronized) { // Hook to change content of the data into the normalized // representation $event = new FilterDataEvent($this, $normData); $this->dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event); $normData = $event->getData(); // Synchronize representations - must not change the content! $appData = $this->normToApp($normData); $clientData = $this->normToClient($normData); } $this->bound = true; $this->appData = $appData; $this->normData = $normData; $this->clientData = $clientData; $this->extraData = $extraData; $this->synchronized = $synchronized; $event = new DataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::POST_BIND, $event); foreach ($this->validators as $validator) { $validator->validate($this); } $event = new DataEvent($this, $clientData); $this->dispatcher->dispatch(FormEvents::POST_VALIDATE, $event); return $this; }