diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md index 240078cf2f..38183feec3 100644 --- a/UPGRADE-2.1.md +++ b/UPGRADE-2.1.md @@ -488,6 +488,8 @@ * `getErrorBubbling` * `getNormTransformers` * `getClientTransformers` + * `getAttribute` + * `hasAttribute` You can access these methods on the `FormConfigInterface` object instead. @@ -609,6 +611,24 @@ The second argument `$value` contains the current default value and does not have to be specified if not needed. + * A third argument $options was added to the methods `buildView()` and + `buildViewBottomUp()` in `FormTypeInterface` and `FormTypeExtensionInterface`. + You should adapt your implementing classes. + + Before: + + ``` + public function buildView(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form) + ``` + + After: + + ``` + public function buildView(FormView $view, FormInterface $form, array $options) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) + ``` + ### Validator * The methods `setMessage()`, `getMessageTemplate()` and diff --git a/src/Symfony/Component/Form/AbstractType.php b/src/Symfony/Component/Form/AbstractType.php index d700f9827c..823350c8d9 100644 --- a/src/Symfony/Component/Form/AbstractType.php +++ b/src/Symfony/Component/Form/AbstractType.php @@ -35,14 +35,14 @@ abstract class AbstractType implements FormTypeInterface /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { } /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { } diff --git a/src/Symfony/Component/Form/AbstractTypeExtension.php b/src/Symfony/Component/Form/AbstractTypeExtension.php index a53deb77cb..c1c0423361 100644 --- a/src/Symfony/Component/Form/AbstractTypeExtension.php +++ b/src/Symfony/Component/Form/AbstractTypeExtension.php @@ -28,14 +28,14 @@ abstract class AbstractTypeExtension implements FormTypeExtensionInterface /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { } /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index c59f65523c..24ccd19bc6 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -81,6 +81,8 @@ CHANGELOG * `getErrorBubbling` * `getNormTransformers` * `getClientTransformers` + * `getAttribute` + * `hasAttribute` * deprecated the option "validation_constraint" in favor of the new option "constraints" * removed superfluous methods from DataMapperInterface @@ -92,3 +94,9 @@ CHANGELOG which accepts an OptionsResolver instance * deprecated the methods `getDefaultOptions` and `getAllowedOptionValues` in FormTypeInterface and FormTypeExtensionInterface + * options passed during construction can now be accessed from FormConfigInterface + * [BC BREAK] the options array is now passed as last argument of the + methods + * `buildView` + * `buildViewBottomUp` + in FormTypeInterface and FormTypeExtensionInterface diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php index 470f6e4bc0..114054a34e 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php @@ -27,17 +27,16 @@ class CheckboxType extends AbstractType { $builder ->appendClientTransformer(new BooleanToStringTransformer($options['value'])) - ->setAttribute('value', $options['value']) ; } /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $view - ->set('value', $form->getAttribute('value')) + ->set('value', $options['value']) ->set('checked', null !== $form->getClientData()) ; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 914053b4f8..90a87af9c4 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -36,10 +36,6 @@ class ChoiceType extends AbstractType */ public function buildForm(FormBuilder $builder, array $options) { - if ($options['choice_list'] && !$options['choice_list'] instanceof ChoiceListInterface) { - throw new FormException('The "choice_list" must be an instance of "Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface".'); - } - if (!$options['choice_list'] && !is_array($options['choices']) && !$options['choices'] instanceof \Traversable) { throw new FormException('Either the option "choices" or "choice_list" must be set.'); } @@ -47,30 +43,7 @@ class ChoiceType extends AbstractType if ($options['expanded']) { $this->addSubForms($builder, $options['choice_list']->getPreferredViews(), $options); $this->addSubForms($builder, $options['choice_list']->getRemainingViews(), $options); - } - // empty value - if ($options['multiple'] || $options['expanded']) { - // never use and empty value for these cases - $emptyValue = null; - } elseif (false === $options['empty_value']) { - // an empty value should be added but the user decided otherwise - $emptyValue = null; - } else { - // empty value has been set explicitly - $emptyValue = $options['empty_value']; - } - - $builder - ->setAttribute('choice_list', $options['choice_list']) - ->setAttribute('preferred_choices', $options['preferred_choices']) - ->setAttribute('multiple', $options['multiple']) - ->setAttribute('expanded', $options['expanded']) - ->setAttribute('required', $options['required']) - ->setAttribute('empty_value', $emptyValue) - ; - - if ($options['expanded']) { if ($options['multiple']) { $builder ->appendClientTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list'])) @@ -100,20 +73,18 @@ class ChoiceType extends AbstractType /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { - $choiceList = $form->getAttribute('choice_list'); - $view - ->set('multiple', $form->getAttribute('multiple')) - ->set('expanded', $form->getAttribute('expanded')) - ->set('preferred_choices', $choiceList->getPreferredViews()) - ->set('choices', $choiceList->getRemainingViews()) + ->set('multiple', $options['multiple']) + ->set('expanded', $options['expanded']) + ->set('preferred_choices', $options['choice_list']->getPreferredViews()) + ->set('choices', $options['choice_list']->getRemainingViews()) ->set('separator', '-------------------') - ->set('empty_value', $form->getAttribute('empty_value')) + ->set('empty_value', $options['empty_value']) ; - if ($view->get('multiple') && !$view->get('expanded')) { + if ($options['multiple'] && !$options['expanded']) { // Add "[]" to the name in case a select tag with multiple options is // displayed. Otherwise only one of the selected options is sent in the // POST request. @@ -124,14 +95,14 @@ class ChoiceType extends AbstractType /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { - if ($view->get('expanded')) { + if ($options['expanded']) { // Radio buttons should have the same name as the parent $childName = $view->get('full_name'); // Checkboxes should append "[]" to allow multiple selection - if ($view->get('multiple')) { + if ($options['multiple']) { $childName .= '[]'; } @@ -166,6 +137,19 @@ class ChoiceType extends AbstractType return $options['required'] ? null : ''; }; + $emptyValueFilter = function (Options $options, $emptyValue) { + if ($options['multiple'] || $options['expanded']) { + // never use an empty value for these cases + return null; + } elseif (false === $emptyValue) { + // an empty value should be added but the user decided otherwise + return null; + } + + // empty value has been set explicitly + return $emptyValue; + }; + $singleControl = function (Options $options) { return !$options['expanded']; }; @@ -179,7 +163,15 @@ class ChoiceType extends AbstractType 'empty_data' => $emptyData, 'empty_value' => $emptyValue, 'error_bubbling' => false, - 'single_control' => $singleControl, + 'single_control' => $singleControl, + )); + + $resolver->setFilters(array( + 'empty_value' => $emptyValueFilter, + )); + + $resolver->setAllowedTypes(array( + 'choice_list' => array('null', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface'), )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php index 86ff699c4a..b31c1d4e94 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -40,34 +40,30 @@ class CollectionType extends AbstractType $options['allow_delete'] ); - $builder - ->addEventSubscriber($resizeListener) - ->setAttribute('allow_add', $options['allow_add']) - ->setAttribute('allow_delete', $options['allow_delete']) - ; + $builder->addEventSubscriber($resizeListener); } /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $view - ->set('allow_add', $form->getAttribute('allow_add')) - ->set('allow_delete', $form->getAttribute('allow_delete')) + ->set('allow_add', $options['allow_add']) + ->set('allow_delete', $options['allow_delete']) ; - if ($form->hasAttribute('prototype')) { - $view->set('prototype', $form->getAttribute('prototype')->createView($view)); + if ($form->getConfig()->hasAttribute('prototype')) { + $view->set('prototype', $form->getConfig()->getAttribute('prototype')->createView($view)); } } /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { - if ($form->hasAttribute('prototype') && $view->get('prototype')->get('multipart')) { + if ($form->getConfig()->hasAttribute('prototype') && $view->get('prototype')->get('multipart')) { $view->set('multipart', true); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index 2952de3292..6926c51727 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -111,18 +111,16 @@ class DateTimeType extends AbstractType new DateTimeToArrayTransformer($options['data_timezone'], $options['data_timezone'], $parts) )); } - - $builder->setAttribute('widget', $options['widget']); } /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { - $view->set('widget', $form->getAttribute('widget')); + $view->set('widget', $options['widget']); - if ('single_text' === $form->getAttribute('widget')) { + if ('single_text' === $options['widget']) { $view->set('type', 'datetime'); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index a5e5a86c87..d0c6a3e0d0 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -130,24 +130,22 @@ class DateType extends AbstractType )); } - $builder - ->setAttribute('formatter', $formatter) - ->setAttribute('widget', $options['widget']); + $builder->setAttribute('formatter', $formatter); } /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { - $view->set('widget', $form->getAttribute('widget')); + $view->set('widget', $options['widget']); - if ('single_text' === $form->getAttribute('widget')) { + if ('single_text' === $options['widget']) { $view->set('type', 'date'); } if ($view->hasChildren()) { - $pattern = $form->getAttribute('formatter')->getPattern(); + $pattern = $form->getConfig()->getAttribute('formatter')->getPattern(); // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy) // lookup various formats at http://userguide.icu-project.org/formatparse/datetime diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 8ed0b6ffe1..8d76c4f3aa 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -21,7 +21,7 @@ class FileType extends AbstractType /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $view ->set('type', 'file') @@ -32,7 +32,7 @@ class FileType extends AbstractType /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { $view ->set('multipart', true) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index cbc88ace23..4a75c688be 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -31,14 +31,6 @@ class FormType extends AbstractType */ public function buildForm(FormBuilder $builder, array $options) { - if (!is_array($options['attr'])) { - throw new FormException('The "attr" option must be an "array".'); - } - - if (!is_array($options['label_attr'])) { - throw new FormException('The "label_attr" option must be an "array".'); - } - $builder ->setRequired($options['required']) ->setDisabled($options['disabled']) @@ -49,14 +41,6 @@ class FormType extends AbstractType ->setMapped($options['mapped']) ->setByReference($options['by_reference']) ->setVirtual($options['virtual']) - ->setAttribute('read_only', $options['read_only']) - ->setAttribute('max_length', $options['max_length']) - ->setAttribute('pattern', $options['pattern']) - ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) - ->setAttribute('attr', $options['attr']) - ->setAttribute('label_attr', $options['label_attr']) - ->setAttribute('translation_domain', $options['translation_domain']) - ->setAttribute('single_control', $options['single_control']) ->setData($options['data']) ->setDataMapper(new PropertyPathMapper()) ; @@ -69,10 +53,10 @@ class FormType extends AbstractType /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $name = $form->getName(); - $readOnly = $form->getAttribute('read_only'); + $readOnly = $options['read_only']; if ($view->hasParent()) { if ('' === $name) { @@ -115,23 +99,23 @@ class FormType extends AbstractType ->set('value', $form->getClientData()) ->set('disabled', $form->isDisabled()) ->set('required', $form->isRequired()) - ->set('max_length', $form->getAttribute('max_length')) - ->set('pattern', $form->getAttribute('pattern')) + ->set('max_length', $options['max_length']) + ->set('pattern', $options['pattern']) ->set('size', null) - ->set('label', $form->getAttribute('label')) + ->set('label', $options['label'] ?: $this->humanize($form->getName())) ->set('multipart', false) - ->set('attr', $form->getAttribute('attr')) - ->set('label_attr', $form->getAttribute('label_attr')) - ->set('single_control', $form->getAttribute('single_control')) + ->set('attr', $options['attr']) + ->set('label_attr', $options['label_attr']) + ->set('single_control', $options['single_control']) ->set('types', $types) - ->set('translation_domain', $form->getAttribute('translation_domain')) + ->set('translation_domain', $options['translation_domain']) ; } /** * {@inheritdoc} */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { $multipart = false; @@ -214,6 +198,11 @@ class FormType extends AbstractType 'single_control' => false, 'translation_domain' => 'messages', )); + + $resolver->setAllowedTypes(array( + 'attr' => 'array', + 'label_attr' => 'array', + )); } /** @@ -221,7 +210,7 @@ class FormType extends AbstractType */ public function createBuilder($name, FormFactoryInterface $factory, array $options) { - return new FormBuilder($name, $options['data_class'], new EventDispatcher(), $factory); + return new FormBuilder($name, $options['data_class'], new EventDispatcher(), $factory, $options); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php b/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php index bddbf87688..faa864eec5 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php @@ -34,16 +34,15 @@ class MoneyType extends AbstractType null, $options['divisor'] )) - ->setAttribute('currency', $options['currency']) ; } /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { - $view->set('money_pattern', self::getPattern($form->getAttribute('currency'))); + $view->set('money_pattern', self::getPattern($options['currency'])); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php b/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php index 229ab5e1f6..ffa94dab31 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/PasswordType.php @@ -22,17 +22,9 @@ class PasswordType extends AbstractType /** * {@inheritdoc} */ - public function buildForm(FormBuilder $builder, array $options) + public function buildView(FormView $view, FormInterface $form, array $options) { - $builder->setAttribute('always_empty', $options['always_empty']); - } - - /** - * {@inheritdoc} - */ - public function buildView(FormView $view, FormInterface $form) - { - if ($form->getAttribute('always_empty') || !$form->isBound()) { + if ($options['always_empty'] || !$form->isBound()) { $view->set('value', ''); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php b/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php index ce8618181b..d9b0e5c7b6 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TextareaType.php @@ -20,7 +20,7 @@ class TextareaType extends AbstractType /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $view->set('pattern', null); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index d7c6efc530..8c40a791a0 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -42,12 +42,6 @@ class TimeType extends AbstractType $hourOptions = $minuteOptions = $secondOptions = array(); if ('choice' === $options['widget']) { - if (is_array($options['empty_value'])) { - $options['empty_value'] = array_merge(array('hour' => null, 'minute' => null, 'second' => null), $options['empty_value']); - } else { - $options['empty_value'] = array('hour' => $options['empty_value'], 'minute' => $options['empty_value'], 'second' => $options['empty_value']); - } - $hours = $minutes = array(); foreach ($options['hours'] as $hour) { @@ -114,24 +108,19 @@ class TimeType extends AbstractType new DateTimeToArrayTransformer($options['data_timezone'], $options['data_timezone'], $parts) )); } - - $builder - ->setAttribute('widget', $options['widget']) - ->setAttribute('with_seconds', $options['with_seconds']) - ; } /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form, array $options) { $view - ->set('widget', $form->getAttribute('widget')) - ->set('with_seconds', $form->getAttribute('with_seconds')) + ->set('widget', $options['widget']) + ->set('with_seconds', $options['with_seconds']) ; - if ('single_text' === $form->getAttribute('widget')) { + if ('single_text' === $options['widget']) { $view->set('type', 'time'); } } @@ -145,6 +134,21 @@ class TimeType extends AbstractType return $options['widget'] === 'single_text'; }; + $emptyValueFilter = function (Options $options, $emptyValue) { + if (is_array($emptyValue)) { + return array_merge( + array('hour' => null, 'minute' => null, 'second' => null), + $emptyValue + ); + } + + return array( + 'hour' => $emptyValue, + 'minute' => $emptyValue, + 'second' => $emptyValue + ); + }; + $resolver->setDefaults(array( 'hours' => range(0, 23), 'minutes' => range(0, 59), @@ -164,7 +168,11 @@ class TimeType extends AbstractType // representation is not \DateTime, but an array, we need to unset // this option. 'data_class' => null, - 'single_control' => $singleControl, + 'single_control' => $singleControl, + )); + + $resolver->setFilters(array( + 'empty_value' => $emptyValueFilter, )); $resolver->setAllowedValues(array( diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index da8a07d1af..7177f99981 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -63,7 +63,7 @@ class CsrfValidationListener implements EventSubscriberInterface $form = $event->getForm(); $data = $event->getData(); - if ($form->isRoot() && !$form->getAttribute('single_control')) { + if ($form->isRoot() && !$form->getConfig()->getOption('single_control')) { if (!isset($data[$this->fieldName]) || !$this->csrfProvider->isCsrfTokenValid($this->intention, $data[$this->fieldName])) { $form->addError(new FormError('The CSRF token is invalid. Please try to resubmit the form')); } diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index 9e6ebda8a8..b37b1f353b 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -47,11 +47,7 @@ class FormTypeCsrfExtension extends AbstractTypeExtension return; } - // use a low priority so higher priority listeners don't remove the field $builder - ->setAttribute('csrf_field_name', $options['csrf_field_name']) - ->setAttribute('csrf_provider', $options['csrf_provider']) - ->setAttribute('csrf_intention', $options['intention']) ->setAttribute('csrf_factory', $builder->getFormFactory()) ->addEventSubscriber(new CsrfValidationListener($options['csrf_field_name'], $options['csrf_provider'], $options['intention'])) ; @@ -63,15 +59,13 @@ class FormTypeCsrfExtension extends AbstractTypeExtension * @param FormView $view The form view * @param FormInterface $form The form */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form, array $options) { - if (!$view->hasParent() && !$form->getAttribute('single_control') && $form->hasAttribute('csrf_field_name')) { - $name = $form->getAttribute('csrf_field_name'); - $csrfProvider = $form->getAttribute('csrf_provider'); - $intention = $form->getAttribute('csrf_intention'); - $factory = $form->getAttribute('csrf_factory'); - $data = $csrfProvider->generateCsrfToken($intention); - $csrfForm = $factory->createNamed('hidden', $name, $data, array( + if ($options['csrf_protection'] && !$view->hasParent() && !$options['single_control']) { + $factory = $form->getConfig()->getAttribute('csrf_factory'); + $data = $options['csrf_provider']->generateCsrfToken($options['intention']); + + $csrfForm = $factory->createNamed('hidden', $options['csrf_field_name'], $data, array( 'property_path' => false, )); diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 586bde759f..3c46271e84 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -55,6 +55,7 @@ class FormValidator extends ConstraintValidator $path = $this->context->getPropertyPath(); $graphWalker = $this->context->getGraphWalker(); $groups = $this->getValidationGroups($form); + $config = $form->getConfig(); if (!empty($path)) { $path .= '.'; @@ -72,7 +73,7 @@ class FormValidator extends ConstraintValidator // Validate the data against the constraints defined // in the form - $constraints = $form->getAttribute('constraints'); + $constraints = $config->getOption('constraints'); foreach ($constraints as $constraint) { foreach ($groups as $group) { $graphWalker->walkConstraint($constraint, $form->getData(), $group, $path . 'data'); @@ -85,7 +86,7 @@ class FormValidator extends ConstraintValidator // Mark the form with an error if it is not synchronized $this->context->addViolation( - $form->getAttribute('invalid_message'), + $config->getOption('invalid_message'), array('{{ value }}' => $clientDataAsString), $form->getClientData(), null, @@ -96,7 +97,7 @@ class FormValidator extends ConstraintValidator // Mark the form with an error if it contains extra fields if (count($form->getExtraData()) > 0) { $this->context->addViolation( - $form->getAttribute('extra_fields_message'), + $config->getOption('extra_fields_message'), array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))), $form->getExtraData() ); @@ -126,7 +127,7 @@ class FormValidator extends ConstraintValidator if ($length > $maxLength) { $this->context->addViolation( - $form->getAttribute('post_max_size_message'), + $config->getOption('post_max_size_message'), array('{{ max }}' => $max), $length ); @@ -161,7 +162,7 @@ class FormValidator extends ConstraintValidator $parent = $form->getParent(); while (null !== $parent) { - if (!$parent->getAttribute('cascade_validation')) { + if (!$parent->getConfig()->getOption('cascade_validation')) { return false; } @@ -182,29 +183,18 @@ class FormValidator extends ConstraintValidator { $groups = null; - if ($form->hasAttribute('validation_groups')) { - $groups = $form->getAttribute('validation_groups'); + while (null !== $form && null === $groups) { + $groups = $form->getConfig()->getOption('validation_groups'); if (is_callable($groups)) { $groups = (array) call_user_func($groups, $form); } - } - $currentForm = $form; - while (!$groups && $currentForm->hasParent()) { - $currentForm = $currentForm->getParent(); - - if ($currentForm->hasAttribute('validation_groups')) { - $groups = $currentForm->getAttribute('validation_groups'); - - if (is_callable($groups)) { - $groups = (array) call_user_func($groups, $currentForm); - } - } + $form = $form->getParent(); } if (null === $groups) { - $groups = array('Default'); + $groups = array(Constraint::DEFAULT_GROUP); } return (array) $groups; diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php index ad5efaf6ad..aed5ee6f87 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -45,29 +45,7 @@ class FormTypeValidatorExtension extends AbstractTypeExtension */ public function buildForm(FormBuilder $builder, array $options) { - if (empty($options['validation_groups'])) { - $options['validation_groups'] = null; - } else { - $options['validation_groups'] = is_callable($options['validation_groups']) - ? $options['validation_groups'] - : (array) $options['validation_groups']; - } - - // Objects, when casted to an array, are split into their properties - $constraints = is_object($options['constraints']) - ? array($options['constraints']) - : (array) $options['constraints']; - - $builder - ->setAttribute('error_mapping', $options['error_mapping']) - ->setAttribute('validation_groups', $options['validation_groups']) - ->setAttribute('constraints', $constraints) - ->setAttribute('cascade_validation', $options['cascade_validation']) - ->setAttribute('invalid_message', $options['invalid_message']) - ->setAttribute('extra_fields_message', $options['extra_fields_message']) - ->setAttribute('post_max_size_message', $options['post_max_size_message']) - ->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper)) - ; + $builder->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper)); } /** @@ -80,6 +58,22 @@ class FormTypeValidatorExtension extends AbstractTypeExtension return $options['validation_constraint']; }; + // Make sure that validation groups end up as null, closure or array + $validationGroupsFilter = function (Options $options, $groups) { + if (empty($groups)) { + return null; + } elseif (is_callable($groups)) { + return $groups; + } + + return (array) $groups; + }; + + // Constraint should always be converted to an array + $constraintsFilter = function (Options $options, $constraints) { + return is_object($constraints) ? array($constraints) : (array) $constraints; + }; + $resolver->setDefaults(array( 'error_mapping' => array(), 'validation_groups' => null, @@ -91,6 +85,11 @@ class FormTypeValidatorExtension extends AbstractTypeExtension 'extra_fields_message' => 'This form should not contain extra fields.', 'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.', )); + + $resolver->setFilters(array( + 'validation_groups' => $validationGroupsFilter, + 'constraints' => $constraintsFilter, + )); } /** diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php index 736f532184..8d8de9dabe 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -109,12 +109,12 @@ class ViolationMapper implements ViolationMapperInterface } // Follow dot rules until we have the final target - $mapping = $this->scope->getAttribute('error_mapping'); + $mapping = $this->scope->getConfig()->getAttribute('error_mapping'); while ($this->isValidScope() && isset($mapping['.'])) { $dotRule = new MappingRule($this->scope, '.', $mapping['.']); $this->scope = $dotRule->getTarget(); - $mapping = $this->scope->getAttribute('error_mapping'); + $mapping = $this->scope->getConfig()->getAttribute('error_mapping'); } // Only add the error if the form is synchronized @@ -281,7 +281,7 @@ class ViolationMapper implements ViolationMapperInterface $this->children = new \RecursiveIteratorIterator( new VirtualFormAwareIterator($form->getChildren()) ); - foreach ($form->getAttribute('error_mapping') as $propertyPath => $targetPath) { + foreach ($form->getConfig()->getAttribute('error_mapping') as $propertyPath => $targetPath) { // Dot rules are considered at the very end if ('.' !== $propertyPath) { $this->rules[] = new MappingRule($form, $propertyPath, $targetPath); diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index b9fe51376d..cda238e720 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -288,6 +288,9 @@ class Form implements \IteratorAggregate, FormInterface * @param string $name The name of the attribute. * * @return Boolean Whether the attribute exists. + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::hasAttribute()} instead. */ public function hasAttribute($name) { @@ -300,6 +303,9 @@ class Form implements \IteratorAggregate, FormInterface * @param string $name The name of the attribute * * @return mixed The attribute value. + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::getAttribute()} instead. */ public function getAttribute($name) { @@ -936,12 +942,13 @@ class Form implements \IteratorAggregate, FormInterface $view->setParent($parent); $types = (array) $this->config->getTypes(); + $options = $this->config->getOptions(); foreach ($types as $type) { - $type->buildView($view, $this); + $type->buildView($view, $this, $options); foreach ($type->getExtensions() as $typeExtension) { - $typeExtension->buildView($view, $this); + $typeExtension->buildView($view, $this, $options); } } @@ -950,10 +957,10 @@ class Form implements \IteratorAggregate, FormInterface } foreach ($types as $type) { - $type->buildViewBottomUp($view, $this); + $type->buildViewBottomUp($view, $this, $options); foreach ($type->getExtensions() as $typeExtension) { - $typeExtension->buildViewBottomUp($view, $this); + $typeExtension->buildViewBottomUp($view, $this, $options); } } diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 560b16e884..2929a58c54 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -61,9 +61,9 @@ class FormBuilder extends FormConfig * @param EventDispatcherInterface $dispatcher * @param FormFactoryInterface $factory */ - public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory) + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory, array $options = array()) { - parent::__construct($name, $dataClass, $dispatcher); + parent::__construct($name, $dataClass, $dispatcher, $options); $this->factory = $factory; } diff --git a/src/Symfony/Component/Form/FormConfig.php b/src/Symfony/Component/Form/FormConfig.php index eae045323c..f12111b9c4 100644 --- a/src/Symfony/Component/Form/FormConfig.php +++ b/src/Symfony/Component/Form/FormConfig.php @@ -113,6 +113,11 @@ class FormConfig implements FormConfigInterface */ private $dataClass; + /** + * @var array + */ + private $options; + /** * Creates an empty form configuration. * @@ -124,7 +129,7 @@ class FormConfig implements FormConfigInterface * @throws \InvalidArgumentException If the data class is not a valid class or if * the name contains invalid characters. */ - public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher) + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, array $options = array()) { $name = (string) $name; @@ -137,6 +142,7 @@ class FormConfig implements FormConfigInterface $this->name = $name; $this->dataClass = $dataClass; $this->dispatcher = $dispatcher; + $this->options = $options; } /** @@ -408,9 +414,9 @@ class FormConfig implements FormConfigInterface /** * {@inheritdoc} */ - public function getAttribute($name) + public function getAttribute($name, $default = null) { - return isset($this->attributes[$name]) ? $this->attributes[$name] : null; + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; } /** @@ -429,6 +435,30 @@ class FormConfig implements FormConfigInterface return $this->dataClass; } + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * {@inheritdoc} + */ + public function getOption($name, $default = null) + { + return isset($this->options[$name]) ? $this->options[$name] : $default; + } + /** * Sets the value for an attribute. * diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php index 554a104667..ca1951f001 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -149,11 +149,12 @@ interface FormConfigInterface /** * Returns the value of the given attribute. * - * @param string $name The attribute name. + * @param string $name The attribute name. + * @param mixed $default The value returned if the attribute does not exist. * * @return mixed The attribute value. */ - function getAttribute($name); + function getAttribute($name, $default = null); /** * Returns the initial data of the form. @@ -168,4 +169,30 @@ interface FormConfigInterface * @return string The data class or null. */ function getDataClass(); + + /** + * Returns all options passed during the construction of the form. + * + * @return array The passed options. + */ + function getOptions(); + + /** + * Returns whether a specific option exists. + * + * @param string $name The option name, + * + * @return Boolean Whether the option exists. + */ + function hasOption($name); + + /** + * Returns the value of a specific option. + * + * @param string $name The option name. + * @param mixed $default The value returned if the option does not exist. + * + * @return mixed The option value. + */ + function getOption($name, $default = null); } diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 66f119bf62..c90b41519d 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -228,20 +228,6 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable */ function bind($data); - /** - * Returns whether the form has an attribute with the given name. - * - * @param string $name The name of the attribute - */ - function hasAttribute($name); - - /** - * Returns the value of the attributes with the given name. - * - * @param string $name The name of the attribute - */ - function getAttribute($name); - /** * Returns the root of the form tree. * diff --git a/src/Symfony/Component/Form/FormTypeExtensionInterface.php b/src/Symfony/Component/Form/FormTypeExtensionInterface.php index 8662eade5b..686adddba2 100644 --- a/src/Symfony/Component/Form/FormTypeExtensionInterface.php +++ b/src/Symfony/Component/Form/FormTypeExtensionInterface.php @@ -39,10 +39,11 @@ interface FormTypeExtensionInterface * * @see FormTypeInterface::buildView() * - * @param FormView $view The view - * @param FormInterface $form The form + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options */ - function buildView(FormView $view, FormInterface $form); + function buildView(FormView $view, FormInterface $form, array $options); /** * Builds the view. @@ -52,10 +53,11 @@ interface FormTypeExtensionInterface * * @see FormTypeInterface::buildViewBottomUp() * - * @param FormView $view The view - * @param FormInterface $form The form + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options */ - function buildViewBottomUp(FormView $view, FormInterface $form); + function buildViewBottomUp(FormView $view, FormInterface $form, array $options); /** * Overrides the default options from the extended type. diff --git a/src/Symfony/Component/Form/FormTypeInterface.php b/src/Symfony/Component/Form/FormTypeInterface.php index ff4ed875bf..062fadd55f 100644 --- a/src/Symfony/Component/Form/FormTypeInterface.php +++ b/src/Symfony/Component/Form/FormTypeInterface.php @@ -41,10 +41,11 @@ interface FormTypeInterface * * @see FormTypeExtensionInterface::buildView() * - * @param FormView $view The view - * @param FormInterface $form The form + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options */ - function buildView(FormView $view, FormInterface $form); + function buildView(FormView $view, FormInterface $form, array $options); /** * Builds the form view. @@ -58,10 +59,11 @@ interface FormTypeInterface * * @see FormTypeExtensionInterface::buildViewBottomUp() * - * @param FormView $view The view - * @param FormInterface $form The form + * @param FormView $view The view + * @param FormInterface $form The form + * @param array $options The options */ - function buildViewBottomUp(FormView $view, FormInterface $form); + function buildViewBottomUp(FormView $view, FormInterface $form, array $options); /** * Returns a builder for the current type. diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php index 040e9899dc..c5b73538bc 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -18,33 +18,6 @@ use Symfony\Component\Form\FormConfigInterface; use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; -abstract class PropertyPathMapperTest_Form implements FormInterface -{ - private $attributes = array(); - - private $data; - - public function setAttribute($name, $value) - { - $this->attributes[$name] = $value; - } - - public function getAttribute($name) - { - return isset($this->attributes[$name]) ? $this->attributes[$name] : null; - } - - public function setData($data) - { - $this->data = $data; - } - - public function getData() - { - return $this->data; - } -} - class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase { /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index fbda65a3b6..a639cc79fc 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -74,17 +74,17 @@ class ChoiceTypeTest extends TypeTestCase */ public function testChoicesOptionExpectsArray() { - $form = $this->factory->create('choice', null, array( + $this->factory->create('choice', null, array( 'choices' => new \ArrayObject(), )); } /** - * @expectedException Symfony\Component\Form\Exception\FormException + * @expectedException Symfony\Component\OptionsResolver\Exception\InvalidOptionsException */ public function testChoiceListOptionExpectsChoiceListInterface() { - $form = $this->factory->create('choice', null, array( + $this->factory->create('choice', null, array( 'choice_list' => array('foo' => 'foo'), )); } @@ -94,7 +94,7 @@ class ChoiceTypeTest extends TypeTestCase */ public function testEitherChoiceListOrChoicesMustBeSet() { - $form = $this->factory->create('choice', null, array( + $this->factory->create('choice', null, array( )); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index 5461258e95..e97aa4dc3b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -39,15 +39,15 @@ class CollectionTypeTest extends TypeTestCase $this->assertCount(2, $form); $this->assertEquals('foo@foo.com', $form[0]->getData()); $this->assertEquals('foo@bar.com', $form[1]->getData()); - $this->assertEquals(20, $form[0]->getAttribute('max_length')); - $this->assertEquals(20, $form[1]->getAttribute('max_length')); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); + $this->assertEquals(20, $form[1]->getConfig()->getOption('max_length')); $form->setData(array('foo@baz.com')); $this->assertInstanceOf('Symfony\Component\Form\Form', $form[0]); $this->assertFalse(isset($form[1])); $this->assertCount(1, $form); $this->assertEquals('foo@baz.com', $form[0]->getData()); - $this->assertEquals(20, $form[0]->getAttribute('max_length')); + $this->assertEquals(20, $form[0]->getConfig()->getOption('max_length')); } public function testThrowsExceptionIfObjectIsNotTraversable() @@ -174,7 +174,7 @@ class CollectionTypeTest extends TypeTestCase 'allow_add' => true, )); - $this->assertSame('__name__', $form->getAttribute('prototype')->getName(), '__name__ is the default'); + $this->assertSame('__name__', $form->getConfig()->getAttribute('prototype')->getName(), '__name__ is the default'); $form = $this->factory->create('collection', null, array( 'type' => 'form', @@ -183,7 +183,7 @@ class CollectionTypeTest extends TypeTestCase 'prototype_name' => '__test__', )); - $this->assertSame('__test__', $form->getAttribute('prototype')->getName()); + $this->assertSame('__test__', $form->getConfig()->getAttribute('prototype')->getName()); } public function testPrototypeDefaultLabel() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php index 4c9be8af24..a4b9a01b74 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -341,13 +341,6 @@ class FormTypeTest extends TypeTestCase $this->assertEquals('Bernhard', $author->firstName); } - public function testGetAttributesIsEmpty() - { - $form = $this->factory->create('form', null, array('attr' => array())); - - $this->assertCount(0, $form->getAttribute('attr')); - } - /** * @see https://github.com/symfony/symfony/issues/1986 */ @@ -379,11 +372,11 @@ class FormTypeTest extends TypeTestCase } /** - * @expectedException Symfony\Component\Form\Exception\FormException + * @expectedException Symfony\Component\OptionsResolver\Exception\InvalidOptionsException */ public function testAttributesException() { - $form = $this->factory->create('form', null, array('attr' => '')); + $this->factory->create('form', null, array('attr' => '')); } public function testNameCanBeEmptyString() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php index 6d9139633e..eeb1c6b0ba 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -41,8 +41,8 @@ class RepeatedTypeTest extends TypeTestCase 'options' => array('label' => 'Global'), )); - $this->assertEquals('Global', $form['first']->getAttribute('label')); - $this->assertEquals('Global', $form['second']->getAttribute('label')); + $this->assertEquals('Global', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Global', $form['second']->getConfig()->getOption('label')); $this->assertTrue($form['first']->isRequired()); $this->assertTrue($form['second']->isRequired()); } @@ -56,8 +56,8 @@ class RepeatedTypeTest extends TypeTestCase 'second_options' => array('label' => 'Test2') )); - $this->assertEquals('Test', $form['first']->getAttribute('label')); - $this->assertEquals('Test2', $form['second']->getAttribute('label')); + $this->assertEquals('Test', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Test2', $form['second']->getConfig()->getOption('label')); $this->assertTrue($form['first']->isRequired()); $this->assertTrue($form['second']->isRequired()); } @@ -81,8 +81,8 @@ class RepeatedTypeTest extends TypeTestCase 'second_options' => array('label' => 'Second label') )); - $this->assertEquals('Label', $form['first']->getAttribute('label')); - $this->assertEquals('Second label', $form['second']->getAttribute('label')); + $this->assertEquals('Label', $form['first']->getConfig()->getOption('label')); + $this->assertEquals('Second label', $form['second']->getConfig()->getOption('label')); $this->assertTrue($form['first']->isRequired()); $this->assertTrue($form['second']->isRequired()); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 209ebdf66d..cd0e8d909d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -64,8 +64,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', array('group1', 'group2')) + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) ->getForm(); @@ -88,9 +88,11 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', array('group1', 'group2')) - ->setAttribute('constraints', array($constraint1, $constraint2)) + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) ->getForm(); @@ -126,12 +128,9 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder() - ->setAttribute('cascade_validation', false) - ->getForm(); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', array('group1', 'group2')) - ->getForm(); + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false))->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); $parent->add($form); $form->setData($object); @@ -151,12 +150,12 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); - $parent = $this->getBuilder() - ->setAttribute('cascade_validation', false) - ->getForm(); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', array('group1', 'group2')) - ->setAttribute('constraints', array($constraint1, $constraint2)) + $parent = $this->getBuilder('parent', null, array('cascade_validation' => false))->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) ->getForm(); $parent->add($form); @@ -184,9 +183,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder('name', '\stdClass') + $form = $this->getBuilder('name', '\stdClass', array('invalid_message' => 'Invalid!')) ->setData($object) - ->setAttribute('invalid_message', 'Invalid!') ->appendClientTransformer(new CallbackTransformer( function ($data) { return $data; }, function () { throw new TransformationFailedException(); } @@ -214,10 +212,12 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $constraint1 = $this->getMock('Symfony\Component\Validator\Constraint'); $constraint2 = $this->getMock('Symfony\Component\Validator\Constraint'); - $form = $this->getBuilder('name', '\stdClass') + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array($constraint1, $constraint2), + ); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) - ->setAttribute('validation_groups', array('group1', 'group2')) - ->setAttribute('constraints', array($constraint1, $constraint2)) ->appendClientTransformer(new CallbackTransformer( function ($data) { return $data; }, function () { throw new TransformationFailedException(); } @@ -239,8 +239,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', array($this, 'getValidationGroups')) + $options = array('validation_groups' => array($this, 'getValidationGroups')); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) ->getForm(); @@ -260,10 +260,10 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', function(FormInterface $form){ - return array('group1', 'group2'); - }) + $options = array('validation_groups' => function(FormInterface $form){ + return array('group1', 'group2'); + }); + $form = $this->getBuilder('name', '\stdClass', $options) ->setData($object) ->getForm(); @@ -284,13 +284,12 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder() - ->setAttribute('validation_groups', 'group') - ->setAttribute('cascade_validation', true) - ->getForm(); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', null) - ->getForm(); + $parentOptions = array( + 'validation_groups' => 'group', + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions)->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); $parent->add($form); $form->setData($object); @@ -309,13 +308,12 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder() - ->setAttribute('validation_groups', array($this, 'getValidationGroups')) - ->setAttribute('cascade_validation', true) - ->getForm(); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', null) - ->getForm(); + $parentOptions = array( + 'validation_groups' => array($this, 'getValidationGroups'), + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions)->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); $parent->add($form); $form->setData($object); @@ -337,15 +335,14 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder() - ->setAttribute('validation_groups', function(FormInterface $form){ + $parentOptions = array( + 'validation_groups' => function(FormInterface $form){ return array('group1', 'group2'); - }) - ->setAttribute('cascade_validation', true) - ->getForm(); - $form = $this->getBuilder('name', '\stdClass') - ->setAttribute('validation_groups', null) - ->getForm(); + }, + 'cascade_validation' => true, + ); + $parent = $this->getBuilder('parent', null, $parentOptions)->getForm(); + $form = $this->getBuilder('name', '\stdClass')->getForm(); $parent->add($form); $form->setData($object); @@ -398,9 +395,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase { $context = $this->getExecutionContext(); - $form = $this->getBuilder() + $form = $this->getBuilder('parent', null, array('extra_fields_message' => 'Extra!')) ->add($this->getBuilder('child')) - ->setAttribute('extra_fields_message', 'Extra!') ->getForm(); $form->bind(array('foo' => 'bar')); @@ -422,9 +418,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1G')); $context = $this->getExecutionContext(); - $form = $this->getBuilder() - ->setAttribute('post_max_size_message', 'Max {{ max }}!') - ->getForm(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); $this->validator->initialize($context); $this->validator->validate($form, new Form()); @@ -443,9 +438,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1g')); $context = $this->getExecutionContext(); - $form = $this->getBuilder() - ->setAttribute('post_max_size_message', 'Max {{ max }}!') - ->getForm(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); $this->validator->initialize($context); $this->validator->validate($form, new Form()); @@ -482,9 +476,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1M')); $context = $this->getExecutionContext(); - $form = $this->getBuilder() - ->setAttribute('post_max_size_message', 'Max {{ max }}!') - ->getForm(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); $this->validator->initialize($context); $this->validator->validate($form, new Form()); @@ -521,9 +514,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue('1K')); $context = $this->getExecutionContext(); - $form = $this->getBuilder() - ->setAttribute('post_max_size_message', 'Max {{ max }}!') - ->getForm(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); $this->validator->initialize($context); $this->validator->validate($form, new Form()); @@ -598,9 +590,8 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(' 1K ')); $context = $this->getExecutionContext(); - $form = $this->getBuilder() - ->setAttribute('post_max_size_message', 'Max {{ max }}!') - ->getForm(); + $options = array('post_max_size_message' => 'Max {{ max }}!'); + $form = $this->getBuilder('name', null, $options)->getForm(); $this->validator->initialize($context); $this->validator->validate($form, new Form()); @@ -679,12 +670,13 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase /** * @return FormBuilder */ - private function getBuilder($name = 'name', $dataClass = null) + private function getBuilder($name = 'name', $dataClass = null, array $options = array()) { - $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory); - $builder->setAttribute('constraints', array()); + $options = array_replace(array( + 'constraints' => array(), + ), $options); - return $builder; + return new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory, $options); } private function getForm($name = 'name', $dataClass = null) diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php index 5cd0ea753b..eca5b45a0c 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -19,7 +19,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase { $form = $this->factory->create('form'); - $this->assertNull($form->getAttribute('validation_groups')); + $this->assertNull($form->getConfig()->getOption('validation_groups')); } public function testValidationGroupsCanBeSetToString() @@ -28,7 +28,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase 'validation_groups' => 'group', )); - $this->assertEquals(array('group'), $form->getAttribute('validation_groups')); + $this->assertEquals(array('group'), $form->getConfig()->getOption('validation_groups')); } public function testValidationGroupsCanBeSetToArray() @@ -37,7 +37,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase 'validation_groups' => array('group1', 'group2'), )); - $this->assertEquals(array('group1', 'group2'), $form->getAttribute('validation_groups')); + $this->assertEquals(array('group1', 'group2'), $form->getConfig()->getOption('validation_groups')); } public function testValidationGroupsCanBeSetToCallback() @@ -46,7 +46,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), )); - $this->assertTrue(is_callable($form->getAttribute('validation_groups'))); + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); } public function testValidationGroupsCanBeSetToClosure() @@ -55,7 +55,7 @@ class FormTypeValidatorExtensionTest extends TypeTestCase 'validation_groups' => function(FormInterface $form){ return null; }, )); - $this->assertTrue(is_callable($form->getAttribute('validation_groups'))); + $this->assertTrue(is_callable($form->getConfig()->getOption('validation_groups'))); } public function testBindValidatesData() diff --git a/src/Symfony/Component/Form/UnmodifiableFormConfig.php b/src/Symfony/Component/Form/UnmodifiableFormConfig.php index 3fdb963923..39cd86d518 100644 --- a/src/Symfony/Component/Form/UnmodifiableFormConfig.php +++ b/src/Symfony/Component/Form/UnmodifiableFormConfig.php @@ -112,6 +112,11 @@ class UnmodifiableFormConfig implements FormConfigInterface */ private $dataClass; + /** + * @var array + */ + private $options; + /** * Creates an unmodifiable copy of a given configuration. * @@ -142,6 +147,7 @@ class UnmodifiableFormConfig implements FormConfigInterface $this->attributes = $config->getAttributes(); $this->data = $config->getData(); $this->dataClass = $config->getDataClass(); + $this->options = $config->getOptions(); } /** @@ -285,9 +291,9 @@ class UnmodifiableFormConfig implements FormConfigInterface /** * {@inheritdoc} */ - public function getAttribute($name) + public function getAttribute($name, $default = null) { - return isset($this->attributes[$name]) ? $this->attributes[$name] : null; + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; } /** @@ -305,4 +311,28 @@ class UnmodifiableFormConfig implements FormConfigInterface { return $this->dataClass; } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * {@inheritdoc} + */ + public function getOption($name, $default = null) + { + return isset($this->options[$name]) ? $this->options[$name] : $default; + } }