[Form] Extracted FormConfig class to simplify the Form's constructor

This commit is contained in:
Bernhard Schussek 2012-05-16 19:25:27 +02:00
parent e4e3ce6cc2
commit 29963400e8
9 changed files with 1118 additions and 838 deletions

View File

@ -11,7 +11,7 @@
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\InvalidConfigurationException;
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
@ -335,7 +335,7 @@ class ChoiceList implements ChoiceListInterface
{
$index = $this->createIndex($choice);
if ('' === $index || null === $index || !Form::isValidName((string)$index)) {
if ('' === $index || null === $index || !FormConfig::isValidName((string)$index)) {
throw new InvalidConfigurationException('The index "' . $index . '" created by the choice list is invalid. It should be a valid, non-empty Form name.');
}

View File

@ -58,10 +58,10 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Form implements \IteratorAggregate, FormInterface
{
/**
* The name of this form
* @var string
* The form's configuration
* @var FormConfigInterface
*/
private $name;
private $config;
/**
* The parent of this form
@ -75,36 +75,18 @@ class Form implements \IteratorAggregate, FormInterface
*/
private $children = array();
/**
* The mapper for mapping data to children and back
* @var DataMapperInterface
*/
private $dataMapper;
/**
* The errors of this form
* @var array An array of FormError instances
*/
private $errors = array();
/**
* Whether added errors should bubble up to the parent
* @var Boolean
*/
private $errorBubbling;
/**
* Whether this form is bound
* @var Boolean
*/
private $bound = false;
/**
* Whether this form may or may not be empty
* @var Boolean
*/
private $required;
/**
* The form data in application format
* @var mixed
@ -123,32 +105,12 @@ class Form implements \IteratorAggregate, FormInterface
*/
private $clientData;
/**
* Data used for the client data when no value is bound
* @var mixed
*/
private $emptyData = '';
/**
* The bound values that don't belong to any children
* @var array
*/
private $extraData = array();
/**
* The transformers for transforming from application to normalized format
* and back
* @var array An array of DataTransformerInterface
*/
private $normTransformers;
/**
* The transformers for transforming from normalized to client format and
* back
* @var array An array of DataTransformerInterface
*/
private $clientTransformers;
/**
* Whether the data in application, normalized and client format is
* synchronized. Data may not be synchronized if transformation errors
@ -158,78 +120,19 @@ class Form implements \IteratorAggregate, FormInterface
private $synchronized = true;
/**
* The validators attached to this form
* @var array An array of FormValidatorInterface instances
* Creates a new form based on the given configuration.
*
* @param FormConfigInterface $config The form configuration.
*/
private $validators;
/**
* Whether this form may only be read, but not bound
* @var Boolean
*/
private $disabled = false;
/**
* The dispatcher for distributing events of this form
* @var Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $dispatcher;
/**
* Key-value store for arbitrary attributes attached to this form
* @var array
*/
private $attributes;
/**
* The FormTypeInterface instances used to create this form
* @var array An array of FormTypeInterface
*/
private $types;
public function __construct($name, EventDispatcherInterface $dispatcher,
array $types = array(), array $clientTransformers = array(),
array $normTransformers = array(),
DataMapperInterface $dataMapper = null, array $validators = array(),
$required = false, $disabled = false, $errorBubbling = null,
$emptyData = null, array $attributes = array())
public function __construct(FormConfigInterface $config)
{
$name = (string) $name;
self::validateName($name);
foreach ($clientTransformers as $transformer) {
if (!$transformer instanceof DataTransformerInterface) {
throw new UnexpectedTypeException($transformer, 'Symfony\Component\Form\DataTransformerInterface');
}
if (!$config instanceof ImmutableFormConfig) {
$config = new ImmutableFormConfig($config);
}
foreach ($normTransformers as $transformer) {
if (!$transformer instanceof DataTransformerInterface) {
throw new UnexpectedTypeException($transformer, 'Symfony\Component\Form\DataTransformerInterface');
}
}
$this->config = $config;
foreach ($validators as $validator) {
if (!$validator instanceof FormValidatorInterface) {
throw new UnexpectedTypeException($validator, 'Symfony\Component\Form\FormValidatorInterface');
}
}
$this->name = $name;
$this->dispatcher = $dispatcher;
$this->types = $types;
$this->clientTransformers = $clientTransformers;
$this->normTransformers = $normTransformers;
$this->dataMapper = $dataMapper;
$this->validators = $validators;
$this->required = (Boolean) $required;
$this->disabled = (Boolean) $disabled;
$this->errorBubbling = (Boolean) $errorBubbling;
$this->emptyData = $emptyData;
$this->attributes = $attributes;
$this->setData(null);
$this->setData($config->getData());
}
public function __clone()
@ -239,6 +142,16 @@ class Form implements \IteratorAggregate, FormInterface
}
}
/**
* Returns the configuration of the form.
*
* @return ImmutableFormConfig The form's immutable configuration.
*/
public function getConfig()
{
return $this->config;
}
/**
* Returns the name by which the form is identified in forms.
*
@ -246,7 +159,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getName()
{
return $this->name;
return $this->config->getName();
}
/**
@ -256,22 +169,16 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getTypes()
{
return $this->types;
return $this->config->getTypes();
}
/**
* Returns whether the form is required to be filled out.
*
* If the form has a parent and the parent is not required, this method
* will always return false. Otherwise the value set with setRequired()
* is returned.
*
* @return Boolean
* {@inheritdoc}
*/
public function isRequired()
{
if (null === $this->parent || $this->parent->isRequired()) {
return $this->required;
return $this->config->getRequired();
}
return false;
@ -283,7 +190,7 @@ class Form implements \IteratorAggregate, FormInterface
public function isDisabled()
{
if (null === $this->parent || !$this->parent->isDisabled()) {
return $this->disabled;
return $this->config->getDisabled();
}
return true;
@ -302,8 +209,8 @@ class Form implements \IteratorAggregate, FormInterface
throw new AlreadyBoundException('You cannot set the parent of a bound form');
}
if ('' === $this->getName()) {
throw new FormException('Form with empty name can not have parent form.');
if ('' === $this->config->getName()) {
throw new FormException('A form with an empty name cannot have a parent form.');
}
$this->parent = $parent;
@ -360,7 +267,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function hasAttribute($name)
{
return isset($this->attributes[$name]);
return $this->config->hasAttribute($name);
}
/**
@ -370,7 +277,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getAttribute($name)
{
return $this->attributes[$name];
return $this->config->getAttribute($name);
}
/**
@ -387,15 +294,15 @@ class Form implements \IteratorAggregate, FormInterface
}
$event = new DataEvent($this, $appData);
$this->dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::PRE_SET_DATA, $event);
// Hook to change content of the data
$event = new FilterDataEvent($this, $appData);
$this->dispatcher->dispatch(FormEvents::SET_DATA, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::SET_DATA, $event);
$appData = $event->getData();
// Treat data as strings unless a value transformer exists
if (!$this->clientTransformers && !$this->normTransformers && is_scalar($appData)) {
if (!$this->config->getClientTransformers() && !$this->config->getNormTransformers() && is_scalar($appData)) {
$appData = (string) $appData;
}
@ -408,13 +315,13 @@ class Form implements \IteratorAggregate, FormInterface
$this->clientData = $clientData;
$this->synchronized = true;
if (count($this->children) > 0 && $this->dataMapper) {
if (count($this->children) > 0 && $this->config->getDataMapper()) {
// Update child forms from the data
$this->dataMapper->mapDataToForms($clientData, $this->children);
$this->config->getDataMapper()->mapDataToForms($clientData, $this->children);
}
$event = new DataEvent($this, $appData);
$this->dispatcher->dispatch(FormEvents::POST_SET_DATA, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::POST_SET_DATA, $event);
return $this;
}
@ -483,7 +390,7 @@ class Form implements \IteratorAggregate, FormInterface
$this->errors = array();
$event = new DataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::PRE_BIND, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::PRE_BIND, $event);
$appData = null;
$normData = null;
@ -492,7 +399,7 @@ class Form implements \IteratorAggregate, FormInterface
// Hook to change content of the data bound by the browser
$event = new FilterDataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$clientData = $event->getData();
if (count($this->children) > 0) {
@ -520,13 +427,13 @@ class Form implements \IteratorAggregate, FormInterface
// If we have a data mapper, use old client data and merge
// data from the children into it later
if ($this->dataMapper) {
if ($this->config->getDataMapper()) {
$clientData = $this->getClientData();
}
}
if (null === $clientData || '' === $clientData) {
$emptyData = $this->emptyData;
$emptyData = $this->config->getEmptyData();
if ($emptyData instanceof \Closure) {
$emptyData = $emptyData($this, $clientData);
@ -536,8 +443,8 @@ class Form implements \IteratorAggregate, FormInterface
}
// Merge form data from children into existing client data
if (count($this->children) > 0 && $this->dataMapper && null !== $clientData) {
$this->dataMapper->mapFormsToData($this->children, $clientData);
if (count($this->children) > 0 && $this->config->getDataMapper() && null !== $clientData) {
$this->config->getDataMapper()->mapFormsToData($this->children, $clientData);
}
try {
@ -551,7 +458,7 @@ class Form implements \IteratorAggregate, FormInterface
// Hook to change content of the data into the normalized
// representation
$event = new FilterDataEvent($this, $normData);
$this->dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::BIND_NORM_DATA, $event);
$normData = $event->getData();
// Synchronize representations - must not change the content!
@ -567,9 +474,9 @@ class Form implements \IteratorAggregate, FormInterface
$this->synchronized = $synchronized;
$event = new DataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::POST_BIND, $event);
$this->config->getEventDispatcher()->dispatch(FormEvents::POST_BIND, $event);
foreach ($this->validators as $validator) {
foreach ($this->config->getValidators() as $validator) {
$validator->validate($this);
}
@ -590,24 +497,26 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function bindRequest(Request $request)
{
$name = $this->config->getName();
// Store the bound data in case of a post request
switch ($request->getMethod()) {
case 'POST':
case 'PUT':
case 'DELETE':
case 'PATCH':
if ('' === $this->getName()) {
if ('' === $name) {
// Form bound without name
$params = $request->request->all();
$files = $request->files->all();
} elseif ($this->hasChildren()) {
// Form bound with name and children
$params = $request->request->get($this->getName(), array());
$files = $request->files->get($this->getName(), array());
$params = $request->request->get($name, array());
$files = $request->files->get($name, array());
} else {
// Form bound with name, but without children
$params = $request->request->get($this->getName(), null);
$files = $request->files->get($this->getName(), null);
$params = $request->request->get($name, null);
$files = $request->files->get($name, null);
}
if (is_array($params) && is_array($files)) {
$data = array_replace_recursive($params, $files);
@ -616,7 +525,7 @@ class Form implements \IteratorAggregate, FormInterface
}
break;
case 'GET':
$data = '' === $this->getName() ? $request->query->all() : $request->query->get($this->getName(), array());
$data = '' === $name ? $request->query->all() : $request->query->get($name, array());
break;
default:
throw new FormException(sprintf('The request method "%s" is not supported', $request->getMethod()));
@ -662,7 +571,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getErrorBubbling()
{
return $this->errorBubbling;
return $this->config->getErrorBubbling();
}
/**
@ -787,7 +696,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getNormTransformers()
{
return $this->normTransformers;
return $this->config->getNormTransformers();
}
/**
@ -797,7 +706,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
public function getClientTransformers()
{
return $this->clientTransformers;
return $this->config->getClientTransformers();
}
/**
@ -821,11 +730,7 @@ class Form implements \IteratorAggregate, FormInterface
}
/**
* Adds a child to the form.
*
* @param FormInterface $child The FormInterface to add as a child
*
* @return Form the current form
* {@inheritdoc}
*/
public function add(FormInterface $child)
{
@ -837,19 +742,15 @@ class Form implements \IteratorAggregate, FormInterface
$child->setParent($this);
if ($this->dataMapper) {
$this->dataMapper->mapDataToForm($this->getClientData(), $child);
if ($this->config->getDataMapper()) {
$this->config->getDataMapper()->mapDataToForm($this->getClientData(), $child);
}
return $this;
}
/**
* Removes a child from the form.
*
* @param string $name The name of the child to remove
*
* @return Form the current form
* {@inheritdoc}
*/
public function remove($name)
{
@ -867,11 +768,7 @@ class Form implements \IteratorAggregate, FormInterface
}
/**
* Returns whether a child with the given name exists.
*
* @param string $name
*
* @return Boolean
* {@inheritdoc}
*/
public function has($name)
{
@ -879,13 +776,7 @@ class Form implements \IteratorAggregate, FormInterface
}
/**
* Returns the child with the given name.
*
* @param string $name
*
* @return FormInterface
*
* @throws \InvalidArgumentException if the child does not exist
* {@inheritdoc}
*/
public function get($name)
{
@ -974,11 +865,11 @@ class Form implements \IteratorAggregate, FormInterface
$parent = $this->parent->createView();
}
$view = new FormView($this->name);
$view = new FormView($this->config->getName());
$view->setParent($parent);
$types = (array) $this->types;
$types = (array) $this->config->getTypes();
foreach ($types as $type) {
$type->buildView($view, $this);
@ -1012,7 +903,7 @@ class Form implements \IteratorAggregate, FormInterface
*/
private function appToNorm($value)
{
foreach ($this->normTransformers as $transformer) {
foreach ($this->config->getNormTransformers() as $transformer) {
$value = $transformer->transform($value);
}
@ -1028,8 +919,10 @@ class Form implements \IteratorAggregate, FormInterface
*/
private function normToApp($value)
{
for ($i = count($this->normTransformers) - 1; $i >= 0; --$i) {
$value = $this->normTransformers[$i]->reverseTransform($value);
$transformers = $this->config->getNormTransformers();
for ($i = count($transformers) - 1; $i >= 0; --$i) {
$value = $transformers[$i]->reverseTransform($value);
}
return $value;
@ -1044,13 +937,13 @@ class Form implements \IteratorAggregate, FormInterface
*/
private function normToClient($value)
{
if (!$this->clientTransformers) {
if (!$this->config->getClientTransformers()) {
// Scalar values should always be converted to strings to
// facilitate differentiation between empty ("") and zero (0).
return null === $value || is_scalar($value) ? (string) $value : $value;
}
foreach ($this->clientTransformers as $transformer) {
foreach ($this->config->getClientTransformers() as $transformer) {
$value = $transformer->transform($value);
}
@ -1066,55 +959,16 @@ class Form implements \IteratorAggregate, FormInterface
*/
private function clientToNorm($value)
{
if (!$this->clientTransformers) {
$transformers = $this->config->getClientTransformers();
if (!$transformers) {
return '' === $value ? null : $value;
}
for ($i = count($this->clientTransformers) - 1; $i >= 0; --$i) {
$value = $this->clientTransformers[$i]->reverseTransform($value);
for ($i = count($transformers) - 1; $i >= 0; --$i) {
$value = $transformers[$i]->reverseTransform($value);
}
return $value;
}
/**
* Validates whether the given variable is a valid form name.
*
* @param string $name The tested form name.
*
* @throws UnexpectedTypeException If the name is not a string.
* @throws \InvalidArgumentException If the name contains invalid characters.
*/
static public function validateName($name)
{
if (!is_string($name)) {
throw new UnexpectedTypeException($name, 'string');
}
if (!self::isValidName($name)) {
throw new \InvalidArgumentException(sprintf(
'The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").',
$name
));
}
}
/**
* Returns whether the given variable contains a valid form name.
*
* A name is accepted if it
*
* * is empty
* * starts with a letter, digit or underscore
* * contains only letters, digits, numbers, underscores ("_"),
* hyphens ("-") and colons (":")
*
* @param string $name The tested form name.
*
* @return Boolean Whether the name is valid.
*/
static public function isValidName($name)
{
return '' === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name);
}
}

View File

@ -18,101 +18,37 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* A builder for creating {@link Form} instances.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormBuilder
class FormBuilder extends FormConfig
{
/**
* @var string
*/
private $name;
/**
* The form data in application format
* @var mixed
*/
private $appData;
/**
* The event dispatcher
* The form factory.
*
* @var EventDispatcherInterface
*/
private $dispatcher;
/**
* The form factory
* @var FormFactoryInterface
*/
private $factory;
/**
* @var Boolean
*/
private $disabled;
/**
* @var Boolean
*/
private $required;
/**
* The transformers for transforming from normalized to client format and
* back
* @var array An array of DataTransformerInterface
*/
private $clientTransformers = array();
/**
* The transformers for transforming from application to normalized format
* and back
* @var array An array of DataTransformerInterface
*/
private $normTransformers = array();
/**
* @var array An array of FormValidatorInterface
*/
private $validators = array();
/**
* Key-value store for arbitrary attributes attached to the form
* @var array
*/
private $attributes = array();
/**
* @var array An array of FormTypeInterface
*/
private $types = array();
/**
* @var string
*/
private $dataClass;
/**
* The children of the form
* The children of the form builder.
*
* @var array
*/
private $children = array();
/**
* @var DataMapperInterface
* The data of children who haven't been converted to form builders yet.
*
* @var array
*/
private $dataMapper;
/**
* Whether added errors should bubble up to the parent
* @var Boolean
*/
private $errorBubbling;
/**
* Data used for the client data when no value is bound
* @var mixed
*/
private $emptyData = '';
private $unresolvedChildren = array();
private $currentLoadingType;
@ -132,13 +68,9 @@ class FormBuilder
*/
public function __construct($name, FormFactoryInterface $factory, EventDispatcherInterface $dispatcher, $dataClass = null)
{
$name = (string) $name;
parent::__construct($name, $dispatcher);
Form::validateName($name);
$this->name = $name;
$this->factory = $factory;
$this->dispatcher = $dispatcher;
$this->dataClass = $dataClass;
}
@ -152,383 +84,6 @@ class FormBuilder
return $this->factory;
}
/**
* Returns the name of the form.
*
* @return string The form name
*/
public function getName()
{
return $this->name;
}
/**
* Updates the field with default data.
*
* @param array $appData The data formatted as expected for the underlying object
*
* @return FormBuilder The current builder
*/
public function setData($appData)
{
$this->appData = $appData;
return $this;
}
/**
* Returns the data in the format needed for the underlying object.
*
* @return mixed
*/
public function getData()
{
return $this->appData;
}
/**
* Set whether the form is disabled.
*
* @param Boolean $disabled Whether the form is disabled
*
* @return FormBuilder The current builder
*/
public function setDisabled($disabled)
{
$this->disabled = (Boolean) $disabled;
return $this;
}
/**
* Returns whether the form is disabled.
*
* @return Boolean Whether the form is disabled
*/
public function getDisabled()
{
return $this->disabled;
}
/**
* Sets whether this field is required to be filled out when bound.
*
* @param Boolean $required
*
* @return FormBuilder The current builder
*/
public function setRequired($required)
{
$this->required = (Boolean) $required;
return $this;
}
/**
* Returns whether this field is required to be filled out when bound.
*
* @return Boolean Whether this field is required
*/
public function getRequired()
{
return $this->required;
}
/**
* Sets whether errors bubble up to the parent.
*
* @param type $errorBubbling
*
* @return FormBuilder The current builder
*/
public function setErrorBubbling($errorBubbling)
{
$this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling;
return $this;
}
/**
* Returns whether errors bubble up to the parent.
*
* @return Boolean
*/
public function getErrorBubbling()
{
return $this->errorBubbling;
}
/**
* Adds a validator to the form.
*
* @param FormValidatorInterface $validator The validator
*
* @return FormBuilder The current builder
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
public function addValidator(FormValidatorInterface $validator)
{
$this->validators[] = $validator;
return $this;
}
/**
* Returns the validators used by the form.
*
* @return array An array of FormValidatorInterface
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
public function getValidators()
{
return $this->validators;
}
/**
* Adds an event listener for events on this field
*
* @see Symfony\Component\EventDispatcher\EventDispatcherInterface::addListener
*
* @return FormBuilder The current builder
*/
public function addEventListener($eventName, $listener, $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
return $this;
}
/**
* Adds an event subscriber for events on this field
*
* @see Symfony\Component\EventDispatcher\EventDispatcherInterface::addSubscriber
*
* @return FormBuilder The current builder
*/
public function addEventSubscriber(EventSubscriberInterface $subscriber)
{
$this->dispatcher->addSubscriber($subscriber);
return $this;
}
/**
* Appends a transformer to the normalization transformer chain
*
* @param DataTransformerInterface $normTransformer
*
* @return FormBuilder The current builder
*/
public function appendNormTransformer(DataTransformerInterface $normTransformer)
{
$this->normTransformers[] = $normTransformer;
return $this;
}
/**
* Prepends a transformer to the normalization transformer chain
*
* @param DataTransformerInterface $normTransformer
*
* @return FormBuilder The current builder
*/
public function prependNormTransformer(DataTransformerInterface $normTransformer)
{
array_unshift($this->normTransformers, $normTransformer);
return $this;
}
/**
* Clears the normalization transformers.
*
* @return FormBuilder The current builder
*/
public function resetNormTransformers()
{
$this->normTransformers = array();
return $this;
}
/**
* Returns all the normalization transformers.
*
* @return array An array of DataTransformerInterface
*/
public function getNormTransformers()
{
return $this->normTransformers;
}
/**
* Appends a transformer to the client transformer chain
*
* @param DataTransformerInterface $clientTransformer
*
* @return FormBuilder The current builder
*/
public function appendClientTransformer(DataTransformerInterface $clientTransformer)
{
$this->clientTransformers[] = $clientTransformer;
return $this;
}
/**
* Prepends a transformer to the client transformer chain
*
* @param DataTransformerInterface $clientTransformer
*
* @return FormBuilder The current builder
*/
public function prependClientTransformer(DataTransformerInterface $clientTransformer)
{
array_unshift($this->clientTransformers, $clientTransformer);
return $this;
}
/**
* Clears the client transformers.
*
* @return FormBuilder The current builder
*/
public function resetClientTransformers()
{
$this->clientTransformers = array();
return $this;
}
/**
* Returns all the client transformers.
*
* @return array An array of DataTransformerInterface
*/
public function getClientTransformers()
{
return $this->clientTransformers;
}
/**
* Sets the value for an attribute.
*
* @param string $name The name of the attribute
* @param string $value The value of the attribute
*
* @return FormBuilder The current builder
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* Returns the value of the attributes with the given name.
*
* @param string $name The name of the attribute
*/
public function getAttribute($name)
{
return $this->attributes[$name];
}
/**
* Returns whether the form has an attribute with the given name.
*
* @param string $name The name of the attribute
*/
public function hasAttribute($name)
{
return isset($this->attributes[$name]);
}
/**
* Returns all the attributes.
*
* @return array An array of attributes
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Sets the data mapper used by the form.
*
* @param DataMapperInterface $dataMapper
*
* @return FormBuilder The current builder
*/
public function setDataMapper(DataMapperInterface $dataMapper = null)
{
$this->dataMapper = $dataMapper;
return $this;
}
/**
* Returns the data mapper used by the form.
*
* @return array An array of DataMapperInterface
*/
public function getDataMapper()
{
return $this->dataMapper;
}
/**
* Set the types.
*
* @param array $types An array FormTypeInterface
*
* @return FormBuilder The current builder
*/
public function setTypes(array $types)
{
$this->types = $types;
return $this;
}
/**
* Return the types.
*
* @return array An array of FormTypeInterface
*/
public function getTypes()
{
return $this->types;
}
/**
* Sets the data used for the client data when no value is bound.
*
* @param mixed $emptyData
*/
public function setEmptyData($emptyData)
{
$this->emptyData = $emptyData;
return $this;
}
/**
* Returns the data used for the client data when no value is bound.
*
* @return mixed
*/
public function getEmptyData()
{
return $this->emptyData;
}
/**
* Adds a new field to this group. A field must have a unique name within
* the group. Otherwise the existing field is overwritten.
@ -540,7 +95,7 @@ class FormBuilder
* @param string|FormTypeInterface $type
* @param array $options
*
* @return FormBuilder The current builder
* @return FormBuilder The builder object.
*/
public function add($child, $type = null, array $options = array())
{
@ -548,6 +103,9 @@ class FormBuilder
$child->setParent($this);
$this->children[$child->getName()] = $child;
// In case an unresolved child with the same name exists
unset($this->unresolvedChildren[$child->getName()]);
return $this;
}
@ -563,9 +121,9 @@ class FormBuilder
throw new CircularReferenceException(is_string($type) ? $this->getFormFactory()->getType($type) : $type);
}
$this->children[$child] = array(
'type' => $type,
'options' => $options,
$this->unresolvedChildren[$child] = array(
'type' => $type,
'options' => $options,
);
return $this;
@ -578,7 +136,7 @@ class FormBuilder
* @param string|FormTypeInterface $type The type of the form or null if name is a property
* @param array $options The options
*
* @return FormBuilder The builder
* @return FormBuilder The created builder.
*/
public function create($name, $type = null, array $options = array())
{
@ -604,19 +162,15 @@ class FormBuilder
*/
public function get($name)
{
if (!isset($this->children[$name])) {
throw new FormException(sprintf('The field "%s" does not exist', $name));
if (isset($this->unresolvedChildren[$name])) {
return $this->resolveChild($name);
}
if (!$this->children[$name] instanceof FormBuilder) {
$this->children[$name] = $this->create(
$name,
$this->children[$name]['type'],
$this->children[$name]['options']
);
if (isset($this->children[$name])) {
return $this->children[$name];
}
return $this->children[$name];
throw new FormException(sprintf('The child with the name "%s" does not exist.', $name));
}
/**
@ -624,10 +178,14 @@ class FormBuilder
*
* @param string $name
*
* @return FormBuilder The current builder
* @return FormBuilder The builder object.
*/
public function remove($name)
{
if (isset($this->unresolvedChildren[$name])) {
unset($this->unresolvedChildren[$name]);
}
if (isset($this->children[$name])) {
if ($this->children[$name] instanceof self) {
$this->children[$name]->setParent(null);
@ -647,7 +205,15 @@ class FormBuilder
*/
public function has($name)
{
return isset($this->children[$name]);
if (isset($this->unresolvedChildren[$name])) {
return true;
}
if (isset($this->children[$name])) {
return true;
}
return false;
}
/**
@ -657,6 +223,8 @@ class FormBuilder
*/
public function all()
{
$this->resolveChildren();
return $this->children;
}
@ -667,30 +235,15 @@ class FormBuilder
*/
public function getForm()
{
$instance = new Form(
$this->getName(),
$this->buildDispatcher(),
$this->getTypes(),
$this->getClientTransformers(),
$this->getNormTransformers(),
$this->getDataMapper(),
$this->getValidators(),
$this->getRequired(),
$this->getDisabled(),
$this->getErrorBubbling(),
$this->getEmptyData(),
$this->getAttributes()
);
$this->resolveChildren();
foreach ($this->buildChildren() as $child) {
$instance->add($child);
$form = new Form($this);
foreach ($this->children as $child) {
$form->add($child->getForm());
}
if (null !== $this->getData()) {
$instance->setData($this->getData());
}
return $instance;
return $form;
}
public function setCurrentLoadingType($type)
@ -713,7 +266,7 @@ class FormBuilder
*
* @param FormBuilder $parent The parent builder
*
* @return FormBuilder The current builder
* @return FormBuilder The builder object.
*/
public function setParent(FormBuilder $parent = null)
{
@ -723,32 +276,31 @@ class FormBuilder
}
/**
* Returns the event dispatcher.
* Converts an unresolved child into a {@link FormBuilder} instance.
*
* @return type
* @param string $name The name of the unresolved child.
*
* @return FormBuilder The created instance.
*/
protected function buildDispatcher()
private function resolveChild($name)
{
return $this->dispatcher;
$info = $this->unresolvedChildren[$name];
$child = $this->create($name, $info['type'], $info['options']);
$this->children[$name] = $child;
unset($this->unresolvedChildren[$name]);
return $child;
}
/**
* Creates the children.
*
* @return array An array of Form
* Converts all unresolved children into {@link FormBuilder} instances.
*/
protected function buildChildren()
private function resolveChildren()
{
$children = array();
foreach ($this->children as $name => $builder) {
if (!$builder instanceof FormBuilder) {
$builder = $this->create($name, $builder['type'], $builder['options']);
}
$children[$builder->getName()] = $builder->getForm();
foreach ($this->unresolvedChildren as $name => $info) {
$this->children[$name] = $this->create($name, $info['type'], $info['options']);
}
return $children;
$this->unresolvedChildren = array();
}
}

View File

@ -0,0 +1,524 @@
<?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;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* A basic form configuration.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormConfig implements FormConfigInterface
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $dispatcher;
/**
* @var string
*/
private $name;
/**
* @var array
*/
private $types = array();
/**
* @var array
*/
private $clientTransformers = array();
/**
* @var array
*/
private $normTransformers = array();
/**
* @var DataMapperInterface
*/
private $dataMapper;
/**
* @var FormValidatorInterface
*/
private $validators = array();
/**
* @var Boolean
*/
private $required;
/**
* @var Boolean
*/
private $disabled;
/**
* @var Boolean
*/
private $errorBubbling;
/**
* @var mixed
*/
private $emptyData;
/**
* @var array
*/
private $attributes = array();
/**
* @var mixed
*/
private $data;
/**
* Creates an empty form configuration.
*
* @param string $name The form name.
* @param EventDispatcherInterface $dispatcher The event dispatcher.
*/
public function __construct($name, EventDispatcherInterface $dispatcher)
{
$name = (string) $name;
self::validateName($name);
$this->name = $name;
$this->dispatcher = $dispatcher;
}
/**
* Adds an event listener to an event on this form.
*
* @param string $eventName The name of the event to listen to.
* @param callable $listener The listener to execute.
* @param integer $priority The priority of the listener. Listeners
* with a higher priority are called before
* listeners with a lower priority.
*
* @return self The configuration object.
*/
public function addEventListener($eventName, $listener, $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
return $this;
}
/**
* Adds an event subscriber for events on this form.
*
* @param EventSubscriberInterface $subscriber The subscriber to attach.
*
* @return self The configuration object.
*/
public function addEventSubscriber(EventSubscriberInterface $subscriber)
{
$this->dispatcher->addSubscriber($subscriber);
return $this;
}
/**
* Adds a validator to the form.
*
* @param FormValidatorInterface $validator The validator.
*
* @return self The configuration object.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
public function addValidator(FormValidatorInterface $validator)
{
$this->validators[] = $validator;
return $this;
}
/**
* Appends a transformer to the client transformer chain
*
* @param DataTransformerInterface $clientTransformer
*
* @return self The configuration object.
*/
public function appendClientTransformer(DataTransformerInterface $clientTransformer)
{
$this->clientTransformers[] = $clientTransformer;
return $this;
}
/**
* Prepends a transformer to the client transformer chain.
*
* @param DataTransformerInterface $clientTransformer
*
* @return self The configuration object.
*/
public function prependClientTransformer(DataTransformerInterface $clientTransformer)
{
array_unshift($this->clientTransformers, $clientTransformer);
return $this;
}
/**
* Clears the client transformers.
*
* @return self The configuration object.
*/
public function resetClientTransformers()
{
$this->clientTransformers = array();
return $this;
}
/**
* Appends a transformer to the normalization transformer chain
*
* @param DataTransformerInterface $normTransformer
*
* @return self The configuration object.
*/
public function appendNormTransformer(DataTransformerInterface $normTransformer)
{
$this->normTransformers[] = $normTransformer;
return $this;
}
/**
* Prepends a transformer to the normalization transformer chain
*
* @param DataTransformerInterface $normTransformer
*
* @return self The configuration object.
*/
public function prependNormTransformer(DataTransformerInterface $normTransformer)
{
array_unshift($this->normTransformers, $normTransformer);
return $this;
}
/**
* Clears the normalization transformers.
*
* @return self The configuration object.
*/
public function resetNormTransformers()
{
$this->normTransformers = array();
return $this;
}
/**
* {@inheritdoc}
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getTypes()
{
return $this->types;
}
/**
* {@inheritdoc}
*/
public function getClientTransformers()
{
return $this->clientTransformers;
}
/**
* {@inheritdoc}
*/
public function getNormTransformers()
{
return $this->normTransformers;
}
/**
* Returns the data mapper of the form.
*
* @return DataMapperInterface The data mapper.
*/
public function getDataMapper()
{
return $this->dataMapper;
}
/**
* {@inheritdoc}
*/
public function getValidators()
{
return $this->validators;
}
/**
* {@inheritdoc}
*/
public function getRequired()
{
return $this->required;
}
/**
* {@inheritdoc}
*/
public function getDisabled()
{
return $this->disabled;
}
/**
* {@inheritdoc}
*/
public function getErrorBubbling()
{
return $this->errorBubbling;
}
/**
* {@inheritdoc}
*/
public function getEmptyData()
{
return $this->emptyData;
}
/**
* {@inheritdoc}
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* {@inheritdoc}
*/
function hasAttribute($name)
{
return isset($this->attributes[$name]);
}
/**
* {@inheritdoc}
*/
function getAttribute($name)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
}
/**
* {@inheritdoc}
*/
public function getData()
{
return $this->data;
}
/**
* Sets the value for an attribute.
*
* @param string $name The name of the attribute
* @param string $value The value of the attribute
*
* @return self The configuration object.
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* Sets the attributes.
*
* @param array $attributes The attributes.
*
* @return self The configuration object.
*/
public function setAttributes(array $attributes)
{
$this->attributes = $attributes;
return $this;
}
/**
* Sets the data mapper used by the form.
*
* @param DataMapperInterface $dataMapper
*
* @return self The configuration object.
*/
public function setDataMapper(DataMapperInterface $dataMapper = null)
{
$this->dataMapper = $dataMapper;
return $this;
}
/**
* Set whether the form is disabled.
*
* @param Boolean $disabled Whether the form is disabled
*
* @return self The configuration object.
*/
public function setDisabled($disabled)
{
$this->disabled = (Boolean) $disabled;
return $this;
}
/**
* Sets the data used for the client data when no value is bound.
*
* @param mixed $emptyData The empty data.
*
* @return self The configuration object.
*/
public function setEmptyData($emptyData)
{
$this->emptyData = $emptyData;
return $this;
}
/**
* Sets whether errors bubble up to the parent.
*
* @param Boolean $errorBubbling
*
* @return self The configuration object.
*/
public function setErrorBubbling($errorBubbling)
{
$this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling;
return $this;
}
/**
* Sets whether this field is required to be filled out when bound.
*
* @param Boolean $required
*
* @return self The configuration object.
*/
public function setRequired($required)
{
$this->required = (Boolean) $required;
return $this;
}
/**
* Set the types.
*
* @param array $types An array FormTypeInterface
*
* @return self The configuration object.
*/
public function setTypes(array $types)
{
$this->types = $types;
return $this;
}
/**
* Sets the initial data of the form.
*
* @param array $data The data of the form in application format.
*
* @return self The configuration object.
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Validates whether the given variable is a valid form name.
*
* @param string $name The tested form name.
*
* @throws UnexpectedTypeException If the name is not a string.
* @throws \InvalidArgumentException If the name contains invalid characters.
*/
static public function validateName($name)
{
if (!is_string($name)) {
throw new UnexpectedTypeException($name, 'string');
}
if (!self::isValidName($name)) {
throw new \InvalidArgumentException(sprintf(
'The name "%s" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").',
$name
));
}
}
/**
* Returns whether the given variable contains a valid form name.
*
* A name is accepted if it
*
* * is empty
* * starts with a letter, digit or underscore
* * contains only letters, digits, numbers, underscores ("_"),
* hyphens ("-") and colons (":")
*
* @param string $name The tested form name.
*
* @return Boolean Whether the name is valid.
*/
static public function isValidName($name)
{
return '' === $name || preg_match('/^[a-zA-Z0-9_][a-zA-Z0-9_\-:]*$/D', $name);
}
}

View File

@ -0,0 +1,131 @@
<?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;
/**
* The configuration of a {@link Form} object.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface FormConfigInterface
{
/**
* Returns the event dispatcher used to dispatch form events.
*
* @return \Symfony\Component\EventDispatcher\EventDispatcherInterface The dispatcher.
*/
function getEventDispatcher();
/**
* Returns the name of the form used as HTTP parameter.
*
* @return string The form name.
*/
function getName();
/**
* Returns the form types used to construct the form.
*
* @return array An array of {@link FormTypeInterface} instances.
*/
function getTypes();
/**
* Returns the client transformers of the form.
*
* @return array An array of {@link DataTransformerInterface} instances.
*/
function getClientTransformers();
/**
* Returns the view transformers of the form.
*
* @return array An array of {@link DataTransformerInterface} instances.
*/
function getNormTransformers();
/**
* Returns the data mapper of the form.
*
* @return DataMapperInterface The data mapper.
*/
function getDataMapper();
/**
* Returns the validators of the form.
*
* @return FormValidatorInterface The form validator.
*
* @deprecated Deprecated since version 2.1, to be removed in 2.3.
*/
function getValidators();
/**
* Returns whether the form is required.
*
* @return Boolean Whether the form is required.
*/
function getRequired();
/**
* Returns whether the form is disabled.
*
* @return Boolean Whether the form is disabled.
*/
function getDisabled();
/**
* Returns whether errors attached to the form will bubble to its parent.
*
* @return Boolean Whether errors will bubble up.
*/
function getErrorBubbling();
/**
* Returns the data that should be returned when the form is empty.
*
* @return mixed|\Closure The data returned if the form is empty.
*/
function getEmptyData();
/**
* Returns additional attributes of the form.
*
* @return array An array of key-value combinations.
*/
function getAttributes();
/**
* Returns whether the attribute with the given name exists.
*
* @param string $name The attribute name.
*
* @return Boolean Whether the attribute exists.
*/
function hasAttribute($name);
/**
* Returns the value of the given attribute.
*
* @param string $name The attribute name.
*
* @return mixed The attribute value.
*/
function getAttribute($name);
/**
* Returns the initial data of the form.
*
* @return mixed The initial form data.
*/
function getData();
}

View File

@ -0,0 +1,232 @@
<?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;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Util\PropertyPath;
/**
* An immutable form configuration.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ImmutableFormConfig implements FormConfigInterface
{
/**
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $dispatcher;
/**
* @var string
*/
private $name;
/**
* @var array
*/
private $types;
/**
* @var array
*/
private $clientTransformers;
/**
* @var array
*/
private $normTransformers;
/**
* @var DataMapperInterface
*/
private $dataMapper;
/**
* @var FormValidatorInterface
*/
private $validators;
/**
* @var Boolean
*/
private $required;
/**
* @var Boolean
*/
private $disabled;
/**
* @var Boolean
*/
private $errorBubbling;
/**
* @var mixed
*/
private $emptyData;
/**
* @var array
*/
private $attributes;
/**
* @var mixed
*/
private $data;
/**
* Creates an immutable copy of a given configuration.
*
* @param FormConfigInterface $config The configuration to copy.
*/
public function __construct(FormConfigInterface $config)
{
$this->dispatcher = $config->getEventDispatcher();
$this->name = $config->getName();
$this->types = $config->getTypes();
$this->clientTransformers = $config->getClientTransformers();
$this->normTransformers = $config->getNormTransformers();
$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->data = $config->getData();
$this->attributes = $config->getAttributes();
}
/**
* {@inheritdoc}
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getTypes()
{
return $this->types;
}
/**
* {@inheritdoc}
*/
public function getClientTransformers()
{
return $this->clientTransformers;
}
/**
* {@inheritdoc}
*/
public function getNormTransformers()
{
return $this->normTransformers;
}
/**
* Returns the data mapper of the form.
*
* @return DataMapperInterface The data mapper.
*/
public function getDataMapper()
{
return $this->dataMapper;
}
/**
* {@inheritdoc}
*/
public function getValidators()
{
return $this->validators;
}
/**
* {@inheritdoc}
*/
public function getRequired()
{
return $this->required;
}
/**
* {@inheritdoc}
*/
public function getDisabled()
{
return $this->disabled;
}
/**
* {@inheritdoc}
*/
public function getErrorBubbling()
{
return $this->errorBubbling;
}
/**
* {@inheritdoc}
*/
public function getEmptyData()
{
return $this->emptyData;
}
/**
* {@inheritdoc}
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* {@inheritdoc}
*/
function hasAttribute($name)
{
return isset($this->attributes[$name]);
}
/**
* {@inheritdoc}
*/
function getAttribute($name)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
}
/**
* {@inheritdoc}
*/
public function getData()
{
return $this->data;
}
}

View File

@ -35,37 +35,6 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase
$this->builder = null;
}
public function getHtml4Ids()
{
// The full list is tested in FormTest, since both Form and FormBuilder
// use the same implementation internally
return array(
array('#', false),
array('a ', false),
array("a\t", false),
array("a\n", false),
array('a.', false),
);
}
/**
* @dataProvider getHtml4Ids
*/
public function testConstructAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted)
{
try {
new FormBuilder($name, $this->factory, $this->dispatcher);
if (!$accepted) {
$this->fail(sprintf('The value "%s" should not be accepted', $name));
}
} catch (\InvalidArgumentException $e) {
// if the value was not accepted, but should be, rethrow exception
if ($accepted) {
throw $e;
}
}
}
/**
* Changing the name is not allowed, otherwise the name and property path
* are not synchronized anymore
@ -111,6 +80,11 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase
public function testAll()
{
$this->factory->expects($this->once())
->method('createNamedBuilder')
->with('text', 'foo')
->will($this->returnValue(new FormBuilder('foo', $this->factory, $this->dispatcher)));
$this->assertCount(0, $this->builder->all());
$this->assertFalse($this->builder->has('foo'));
@ -120,9 +94,6 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($this->builder->has('foo'));
$this->assertCount(1, $children);
$this->assertArrayHasKey('foo', $children);
$foo = $children['foo'];
$this->assertEquals('text', $foo['type']);
}
public function testAddFormType()
@ -157,7 +128,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase
public function testGetUnknown()
{
$this->setExpectedException('Symfony\Component\Form\Exception\FormException', 'The field "foo" does not exist');
$this->setExpectedException('Symfony\Component\Form\Exception\FormException', 'The child with the name "foo" does not exist.');
$this->builder->get('foo');
}

View File

@ -0,0 +1,78 @@
<?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\Tests;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
use Symfony\Component\Form\FormConfig;
class FormConfigTest extends \PHPUnit_Framework_TestCase
{
public function getHtml4Ids()
{
return array(
array('a0', true),
array('a9', true),
array('z0', true),
array('A0', true),
array('A9', true),
array('Z0', true),
array('#', false),
array('a#', false),
array('a$', false),
array('a%', false),
array('a ', false),
array("a\t", false),
array("a\n", false),
array('a-', true),
array('a_', true),
array('a:', true),
// Periods are allowed by the HTML4 spec, but disallowed by us
// because they break the generated property paths
array('a.', false),
// Contrary to the HTML4 spec, we allow names starting with a
// number, otherwise naming fields by collection indices is not
// possible.
// For root forms, leading digits will be stripped from the
// "id" attribute to produce valid HTML4.
array('0', true),
array('9', true),
// Contrary to the HTML4 spec, we allow names starting with an
// underscore, since this is already a widely used practice in
// Symfony2.
// For root forms, leading underscores will be stripped from the
// "id" attribute to produce valid HTML4.
array('_', true),
);
}
/**
* @dataProvider getHtml4Ids
*/
public function testSetNameAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted)
{
$dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
try {
new FormConfig($name, $dispatcher);
if (!$accepted) {
$this->fail(sprintf('The value "%s" should not be accepted', $name));
}
} catch (\InvalidArgumentException $e) {
// if the value was not accepted, but should be, rethrow exception
if ($accepted) {
throw $e;
}
}
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfig;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormError;
@ -51,72 +52,6 @@ class FormTest extends \PHPUnit_Framework_TestCase
$this->form = null;
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testConstructExpectsValidValidators()
{
$validators = array(new \stdClass());
new Form('name', $this->dispatcher, array(), array(), array(), null, $validators);
}
public function getHtml4Ids()
{
return array(
array('a0', true),
array('a9', true),
array('z0', true),
array('A0', true),
array('A9', true),
array('Z0', true),
array('#', false),
array('a#', false),
array('a$', false),
array('a%', false),
array('a ', false),
array("a\t", false),
array("a\n", false),
array('a-', true),
array('a_', true),
array('a:', true),
// Periods are allowed by the HTML4 spec, but disallowed by us
// because they break the generated property paths
array('a.', false),
// Contrary to the HTML4 spec, we allow names starting with a
// number, otherwise naming fields by collection indices is not
// possible.
// For root forms, leading digits will be stripped from the
// "id" attribute to produce valid HTML4.
array('0', true),
array('9', true),
// Contrary to the HTML4 spec, we allow names starting with an
// underscore, since this is already a widely used practice in
// Symfony2.
// For root forms, leading underscores will be stripped from the
// "id" attribute to produce valid HTML4.
array('_', true),
);
}
/**
* @dataProvider getHtml4Ids
*/
public function testConstructAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted)
{
try {
new Form($name, $this->dispatcher);
if (!$accepted) {
$this->fail(sprintf('The value "%s" should not be accepted', $name));
}
} catch (\InvalidArgumentException $e) {
// if the value was not accepted, but should be, rethrow exception
if ($accepted) {
throw $e;
}
}
}
public function testDataIsInitializedEmpty()
{
$norm = new FixedDataTransformer(array(
@ -126,7 +61,10 @@ class FormTest extends \PHPUnit_Framework_TestCase
'foo' => 'bar',
));
$form = new Form('name', $this->dispatcher, array(), array($client), array($norm));
$config = new FormConfig('name', $this->dispatcher);
$config->appendClientTransformer($client);
$config->appendNormTransformer($norm);
$form = new Form($config);
$this->assertNull($form->getData());
$this->assertSame('foo', $form->getNormData());
@ -1259,7 +1197,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException Symfony\Component\Form\Exception\FormException
* @expectedExceptionMessage Form with empty name can not have parent form.
* @expectedExceptionMessage A form with an empty name cannot have a parent form.
*/
public function testFormCannotHaveEmptyNameNotInRootLevel()
{