diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index 50236cedf8..744e483e69 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -267,8 +267,12 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c in their name anymore. Their names terminate with "[]" now. * [BC BREAK] FormType::getDefaultOptions() and FormType::getAllowedOptionValues() don't receive an options array anymore. - * Deprecated FormValidatorInterface and substituted its implementations + * deprecated FormValidatorInterface and substituted its implementations by event subscribers + * simplified CSRF protection and removed the csrf type + * deprecated FieldType and merged it into FormType + * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to + "input" ### HttpFoundation diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md index aef5f591ec..e564597215 100644 --- a/UPGRADE-2.1.md +++ b/UPGRADE-2.1.md @@ -358,6 +358,13 @@ (or any other of the BIND events). In case you used the CallbackValidator class, you should now pass the callback directly to `addEventListener`. + * simplified CSRF protection and removed the csrf type + + * deprecated FieldType and merged it into FormType + + * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to + "input" + ### Validator * The methods `setMessage()`, `getMessageTemplate()` and diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index cc3f3fd01d..46420a7122 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -2,10 +2,14 @@ {% block form_widget %} {% spaceless %} -
- {{ block('field_rows') }} - {{ form_rest(form) }} -
+ {% if form.children|length > 0 %} +
+ {{ block('form_rows') }} + {{ form_rest(form) }} +
+ {% else %} + {{ block('input') }} + {% endif %} {% endspaceless %} {% endblock form_widget %} @@ -83,7 +87,7 @@ {% block datetime_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('field_widget') }} + {{ block('input') }} {% else %}
{{ form_errors(form.date) }} @@ -98,7 +102,7 @@ {% block date_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('field_widget') }} + {{ block('input') }} {% else %}
{{ date_pattern|replace({ @@ -114,7 +118,7 @@ {% block time_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('field_widget') }} + {{ block('input') }} {% else %}
{{ form_widget(form.hour, { 'attr': { 'size': '1' } }) }}:{{ form_widget(form.minute, { 'attr': { 'size': '1' } }) }}{% if with_seconds %}:{{ form_widget(form.second, { 'attr': { 'size': '1' } }) }}{% endif %} @@ -127,67 +131,60 @@ {% spaceless %} {# type="number" doesn't work with floats #} {% set type = type|default('text') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock number_widget %} {% block integer_widget %} {% spaceless %} {% set type = type|default('number') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock integer_widget %} {% block money_widget %} {% spaceless %} - {{ money_pattern|replace({ '{{ widget }}': block('field_widget') })|raw }} + {{ money_pattern|replace({ '{{ widget }}': block('input') })|raw }} {% endspaceless %} {% endblock money_widget %} {% block url_widget %} {% spaceless %} {% set type = type|default('url') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock url_widget %} {% block search_widget %} {% spaceless %} {% set type = type|default('search') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock search_widget %} {% block percent_widget %} {% spaceless %} {% set type = type|default('text') %} - {{ block('field_widget') }} % + {{ block('input') }} % {% endspaceless %} {% endblock percent_widget %} -{% block field_widget %} -{% spaceless %} - {% set type = type|default('text') %} - -{% endspaceless %} -{% endblock field_widget %} - {% block password_widget %} {% spaceless %} {% set type = type|default('password') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock password_widget %} {% block hidden_widget %} {% set type = type|default('hidden') %} - {{ block('field_widget') }} + {{ block('input') }} {% endblock hidden_widget %} {% block email_widget %} {% spaceless %} {% set type = type|default('email') %} - {{ block('field_widget') }} + {{ block('input') }} {% endspaceless %} {% endblock email_widget %} @@ -202,15 +199,11 @@ {% endspaceless %} {% endblock %} -{% block field_label %} -{% spaceless %} - {% set attr = attr|merge({'for': id}) %} - {{ block('generic_label') }} -{% endspaceless %} -{% endblock field_label %} - {% block form_label %} {% spaceless %} + {% if form.children|length == 0 %} + {% set attr = attr|merge({'for': id}) %} + {% endif %} {{ block('generic_label') }} {% endspaceless %} {% endblock form_label %} @@ -219,24 +212,17 @@ {% block repeated_row %} {% spaceless %} - {{ block('field_rows') }} + {{ block('form_rows') }} {% endspaceless %} {% endblock repeated_row %} -{% block field_row %} -{% spaceless %} -
- {{ form_label(form, label|default(null)) }} - {{ form_errors(form) }} - {{ form_widget(form) }} -
-{% endspaceless %} -{% endblock field_row %} - {% block form_row %} {% spaceless %}
{{ form_label(form, label|default(null)) }} + {% if form.children|length == 0 %} + {{ form_errors(form) }} + {% endif %} {{ form_widget(form) }}
{% endspaceless %} @@ -248,13 +234,13 @@ {# Misc #} -{% block field_enctype %} +{% block form_enctype %} {% spaceless %} {% if multipart %}enctype="multipart/form-data"{% endif %} {% endspaceless %} -{% endblock field_enctype %} +{% endblock form_enctype %} -{% block field_errors %} +{% block form_errors %} {% spaceless %} {% if errors|length > 0 %}
    @@ -270,9 +256,9 @@
{% endif %} {% endspaceless %} -{% endblock field_errors %} +{% endblock form_errors %} -{% block field_rest %} +{% block form_rest %} {% spaceless %} {% for child in form %} {% if not child.rendered %} @@ -280,18 +266,25 @@ {% endif %} {% endfor %} {% endspaceless %} -{% endblock field_rest %} +{% endblock form_rest %} {# Support #} -{% block field_rows %} +{% block form_rows %} {% spaceless %} {{ form_errors(form) }} {% for child in form %} {{ form_row(child) }} {% endfor %} {% endspaceless %} -{% endblock field_rows %} +{% endblock form_rows %} + +{% block input %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock input %} {% block widget_attributes %} {% spaceless %} @@ -306,3 +299,47 @@ {% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %} {% endspaceless %} {% endblock widget_container_attributes %} + +{# Deprecated in Symfony 2.1, to be removed in 2.3 #} + +{% block field_widget %} +{% spaceless %} + {{ block('input') }} +{% endspaceless %} +{% endblock field_widget %} + +{% block field_label %} +{% spaceless %} + {{ block('form_label') }} +{% endspaceless %} +{% endblock field_label %} + +{% block field_row %} +{% spaceless %} + {{ block('form_row') }} +{% endspaceless %} +{% endblock field_row %} + +{% block field_enctype %} +{% spaceless %} + {{ block('form_enctype') }} +{% endspaceless %} +{% endblock field_enctype %} + +{% block field_errors %} +{% spaceless %} + {{ block('form_errors') }} +{% endspaceless %} +{% endblock field_errors %} + +{% block field_rest %} +{% spaceless %} + {{ block('form_rest') }} +{% endspaceless %} +{% endblock field_rest %} + +{% block field_rows %} +{% spaceless %} + {{ block('form_rows') }} +{% endspaceless %} +{% endblock field_rows %} \ No newline at end of file diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig index 1046c3de36..b053c59e6f 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -1,19 +1,5 @@ {% use "form_div_layout.html.twig" %} -{% block field_row %} -{% spaceless %} - - - {{ form_label(form, label|default(null)) }} - - - {{ form_errors(form) }} - {{ form_widget(form) }} - - -{% endspaceless %} -{% endblock field_row %} - {% block form_row %} {% spaceless %} @@ -21,6 +7,9 @@ {{ form_label(form, label|default(null)) }} + {% if form.children|length == 0 %} + {{ form_errors(form) }} + {% endif %} {{ form_widget(form) }} @@ -29,12 +18,16 @@ {% block form_errors %} {% spaceless %} - {% if errors|length > 0 %} - - - {{ block('field_errors') }} - - + {% if form.children|length > 0 %} + {% if errors|length > 0 %} + + + {{ parent() }} + + + {% endif %} + {% else %} + {{ parent() }} {% endif %} {% endspaceless %} {% endblock form_errors %} @@ -51,9 +44,13 @@ {% block form_widget %} {% spaceless %} - - {{ block('field_rows') }} - {{ form_rest(form) }} -
+ {% if form.children|length > 0 %} + + {{ block('form_rows') }} + {{ form_rest(form) }} +
+ {% else %} + {{ parent() }} + {% endif %} {% endspaceless %} {% endblock form_widget %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig index 16c137aaca..061ef428c2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig @@ -1,3 +1,3 @@ -{% block field_label %} +{% block form_label %} -{% endblock field_label %} +{% endblock form_label %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig index fc59d708be..e96278b8f6 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig @@ -1,3 +1,3 @@ -{% block field_label %} +{% block form_label %} -{% endblock field_label %} +{% endblock form_label %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig index d5016842ea..ee5b19e073 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig @@ -1,6 +1,6 @@ -{% block field_widget %} +{% block input %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock field_widget %} +{% endblock input %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig index 96bfea20dc..f58e589498 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig @@ -1,8 +1,8 @@ {% extends 'form_div_layout.html.twig' %} -{% block field_widget %} +{% block input %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock field_widget %} +{% endblock input %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig index 5aee4708a2..9304e9dcfa 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig @@ -1,8 +1,8 @@ {% use 'form_div_layout.html.twig' %} -{% block field_widget %} +{% block input %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock field_widget %} +{% endblock input %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 1422e792b8..bddb2a96e8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -52,7 +52,6 @@ - @@ -133,8 +132,8 @@ - - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index 188a099df2..72442bcbba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -14,12 +14,9 @@ %kernel.secret% - - - - + %form.type_extension.csrf.enabled% %form.type_extension.csrf.field_name% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php index 4a4fa106e3..bd2c2769af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php @@ -1,5 +1,5 @@ - renderBlock('field_widget'); ?> + renderBlock('input'); ?>
renderBlock('container_attributes') ?>> - renderBlock('field_widget'); ?> + renderBlock('input'); ?>
renderBlock('container_attributes') ?>> widget($form['date']).' '.$view['form']->widget($form['time']) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php index c30cfb35c3..a00dda278e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : 'email')) ?> +renderBlock('input', array('type' => isset($type) ? $type : 'email')) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php index 424d425969..aa4ff39bba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php @@ -1 +1 @@ -get('multipart')): ?>enctype="multipart/form-data" +renderBlock('form_enctype') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php index 339e3d0009..d7b87ea2bc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php @@ -1,21 +1 @@ - -
    - -
  • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
  • - -
- +renderBlock('form_errors') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php index 1434301d4c..7c0c1b7559 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php @@ -1,2 +1 @@ - - +renderBlock('form_label') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php index 89041c6ec6..a570b1045a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php @@ -1,5 +1 @@ - - isRendered()): ?> - row($child) ?> - - +renderBlock('form_rest') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php index 091807020d..c91dcb5543 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php @@ -1,5 +1 @@ -
- label($form, isset($label) ? $label : null) ?> - errors($form) ?> - widget($form) ?> -
+renderBlock('form_row') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php index a5f1dfbf5f..ef0bf384a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php @@ -1,4 +1 @@ -errors($form) ?> - - row($child) ?> - +renderBlock('form_rows') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php index 0c86483386..f1ca2edadb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php @@ -1,5 +1 @@ -" - value="escape($value) ?>" - renderBlock('attributes') ?> -/> +renderBlock('input') ?> \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php new file mode 100644 index 0000000000..424d425969 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php @@ -0,0 +1 @@ +get('multipart')): ?>enctype="multipart/form-data" diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php new file mode 100644 index 0000000000..339e3d0009 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php @@ -0,0 +1,21 @@ + +
    + +
  • getMessagePluralization()) { + echo $view['translator']->trans( + $error->getMessageTemplate(), + $error->getMessageParameters(), + 'validators' + ); + } else { + echo $view['translator']->transChoice( + $error->getMessageTemplate(), + $error->getMessagePluralization(), + $error->getMessageParameters(), + 'validators' + ); + }?>
  • + +
+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php index e5c5843c46..3ddc300fd4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -1,2 +1,3 @@ +hasChildren()) { $attr['for'] = $id; } ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php new file mode 100644 index 0000000000..89041c6ec6 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php @@ -0,0 +1,5 @@ + + isRendered()): ?> + row($child) ?> + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php index 0a79a0cc53..02fb9ae9b6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php @@ -1,4 +1,7 @@
label($form, isset($label) ? $label : null) ?> + hasChildren()): ?> + errors($form) ?> + widget($form) ?>
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php new file mode 100644 index 0000000000..a5f1dfbf5f --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php @@ -0,0 +1,4 @@ +errors($form) ?> + + row($child) ?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php index 77fa483c3f..1c9368693b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php @@ -1,5 +1,8 @@ +hasChildren()): ?>
renderBlock('container_attributes') ?>> - renderBlock('field_rows') ?> + renderBlock('form_rows') ?> rest($form) ?>
- + +renderBlock('input')?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php index 11942cfe2b..50a42451ae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "hidden")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "hidden")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php new file mode 100644 index 0000000000..0c86483386 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php @@ -0,0 +1,5 @@ +" + value="escape($value) ?>" + renderBlock('attributes') ?> +/> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php index 012211ab5a..1fc6ace34b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "number")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "number")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php index 3151ecbd84..a68ad5ddac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget'), $money_pattern) ?> +renderBlock('input'), $money_pattern) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php index 9a08222c91..7e1a2776a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "text")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "text")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php index 78319fcbae..7aff242ef4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "password")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "password")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php index 4245b52a0c..328321f21f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "text")) ?> % +renderBlock('input', array('type' => isset($type) ? $type : "text")) ?> % diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php index a84bad54df..b9a07bc466 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php @@ -1 +1 @@ -renderBlock('field_rows') ?> +renderBlock('form_rows') ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php index bbfd593dbc..d8a773544e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php @@ -1 +1 @@ -renderBlock('field_widget', array('type' => isset($type) ? $type : "search")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "search")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php index 599750d20a..2178974c74 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php @@ -1,5 +1,5 @@ - renderBlock('field_widget'); ?> + renderBlock('input'); ?>
renderBlock('container_attributes') ?>> renderBlock('field_widget', array('type' => isset($type) ? $type : "url")) ?> +renderBlock('input', array('type' => isset($type) ? $type : "url")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php deleted file mode 100644 index b9e5c5639c..0000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php +++ /dev/null @@ -1,9 +0,0 @@ - - - label($form, isset($label) ? $label : null) ?> - - - errors($form) ?> - widget($form) ?> - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php index ac4315f957..05d8c4aea8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php @@ -1,7 +1,51 @@ - - - - renderBlock('field_errors'); ?> - - - +hasChildren()): ?> + 0): ?> + + + +
    + +
  • getMessagePluralization()) { + echo $view['translator']->trans( + $error->getMessageTemplate(), + $error->getMessageParameters(), + 'validators' + ); + } else { + echo $view['translator']->transChoice( + $error->getMessageTemplate(), + $error->getMessagePluralization(), + $error->getMessageParameters(), + 'validators' + ); + }?>
  • + +
+ + + + + + +
    + +
  • getMessagePluralization()) { + echo $view['translator']->trans( + $error->getMessageTemplate(), + $error->getMessageParameters(), + 'validators' + ); + } else { + echo $view['translator']->transChoice( + $error->getMessageTemplate(), + $error->getMessagePluralization(), + $error->getMessageParameters(), + 'validators' + ); + }?>
  • + +
+ + \ No newline at end of file diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php index d1bbbb1b2a..9262a1bae1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php @@ -3,6 +3,9 @@ label($form, isset($label) ? $label : null) ?> + hasChildren()): ?> + errors($form) ?> + widget($form) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php index d802ccf050..171f1eb40c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php @@ -1,5 +1,8 @@ +hasChildren()): ?> renderBlock('container_attributes') ?>> - renderBlock('field_rows') ?> + renderBlock('form_rows') ?> rest($form) ?>
- + +renderBlock('input')?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php index 2004b4e67e..8fd4adef89 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @@ -119,7 +119,7 @@ class FormHelper extends Helper */ public function widget(FormView $view, array $variables = array()) { - return trim($this->renderSection($view, 'widget', $variables)); + return $this->renderSection($view, 'widget', $variables); } /** @@ -276,7 +276,7 @@ class FormHelper extends Helper $view->setRendered(); } - return $html; + return trim($html); } } while (--$typeIndex >= 0); @@ -311,7 +311,7 @@ class FormHelper extends Helper $variables = array_replace_recursive($context['variables'], $variables); - return $this->engine->render($template, $variables); + return trim($this->engine->render($template, $variables)); } public function getName() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 859ec489ab..2c7204e735 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -27,9 +27,9 @@ abstract class FrameworkExtensionTest extends TestCase $def = $container->getDefinition('form.type_extension.csrf'); $this->assertTrue($container->getParameter('form.type_extension.csrf.enabled')); - $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(0)); + $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1)); $this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name')); - $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(1)); + $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2)); $this->assertEquals('s3cr3t', $container->getParameterBag()->resolveValue($container->findDefinition('form.csrf_provider')->getArgument(1))); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/field_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/field_label.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_label.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/input.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_widget.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/input.html.php diff --git a/src/Symfony/Component/Form/DataTransformerInterface.php b/src/Symfony/Component/Form/DataTransformerInterface.php index add2ce3d9b..2f1543e91b 100644 --- a/src/Symfony/Component/Form/DataTransformerInterface.php +++ b/src/Symfony/Component/Form/DataTransformerInterface.php @@ -24,7 +24,7 @@ interface DataTransformerInterface * This method is called on two occasions inside a form field: * * 1. When the form field is initialized with the data attached from the datasource (object or array). - * 2. When data from a request is bound using {@link Field::bind()} to transform the new input data + * 2. When data from a request is bound using {@link Form::bind()} to transform the new input data * back into the renderable format. For example if you have a date field and bind '2009-10-10' onto * it you might accept this value because its easily parsed, but the transformer still writes back * "2009/10/10" onto the form field (for further displaying or other purposes). @@ -52,7 +52,7 @@ interface DataTransformerInterface * Transforms a value from the transformed representation to its original * representation. * - * This method is called when {@link Field::bind()} is called to transform the requests tainted data + * This method is called when {@link Form::bind()} is called to transform the requests tainted data * into an acceptable format for your data processing/model layer. * * This method must be able to deal with empty values. Usually this will diff --git a/src/Symfony/Component/Form/Exception/DanglingFieldException.php b/src/Symfony/Component/Form/Exception/DanglingFieldException.php deleted file mode 100644 index 29d10be36b..0000000000 --- a/src/Symfony/Component/Form/Exception/DanglingFieldException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Exception; - -/** - * Thrown when a field is expected to be added to a form but is not - * - * @author Bernhard Schussek - */ -class DanglingFieldException extends FormException -{ -} diff --git a/src/Symfony/Component/Form/Exception/FieldDefinitionException.php b/src/Symfony/Component/Form/Exception/FieldDefinitionException.php deleted file mode 100644 index 6cd904ad4a..0000000000 --- a/src/Symfony/Component/Form/Exception/FieldDefinitionException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Exception; - -class FieldDefinitionException extends FormException -{ -} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 1ed495248b..1a8ba2d1be 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -44,8 +44,8 @@ class ChoiceType extends AbstractType } if ($options['expanded']) { - $this->addSubFields($builder, $options['choice_list']->getPreferredViews(), $options); - $this->addSubFields($builder, $options['choice_list']->getRemainingViews(), $options); + $this->addSubForms($builder, $options['choice_list']->getPreferredViews(), $options); + $this->addSubForms($builder, $options['choice_list']->getRemainingViews(), $options); } // empty value @@ -182,7 +182,7 @@ class ChoiceType extends AbstractType */ public function getParent(array $options) { - return isset($options['expanded']) && $options['expanded'] ? 'form' : 'field'; + return 'field'; } /** @@ -200,12 +200,12 @@ class ChoiceType extends AbstractType * @param array $choiceViews The choice view objects. * @param array $options The build options. */ - private function addSubFields(FormBuilder $builder, array $choiceViews, array $options) + private function addSubForms(FormBuilder $builder, array $choiceViews, array $options) { foreach ($choiceViews as $i => $choiceView) { if (is_array($choiceView)) { // Flatten groups - $this->addSubFields($builder, $choiceView, $options); + $this->addSubForms($builder, $choiceView, $options); } else { $choiceOpts = array( 'value' => $choiceView->getValue(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index 6eba72d4f6..f428054ede 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -153,7 +153,7 @@ class DateTimeType extends AbstractType 'widget' => null, // This will overwrite "empty_value" child options 'empty_value' => null, - // If initialized with a \DateTime object, FieldType initializes + // If initialized with a \DateTime object, FormType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -200,7 +200,7 @@ class DateTimeType extends AbstractType */ public function getParent(array $options) { - return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; + return 'field'; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index e58d0d9340..c506714c7f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -177,7 +177,7 @@ class DateType extends AbstractType // them like immutable value objects 'by_reference' => false, 'error_bubbling' => false, - // If initialized with a \DateTime object, FieldType initializes + // If initialized with a \DateTime object, FormType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -210,7 +210,7 @@ class DateType extends AbstractType */ public function getParent(array $options) { - return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; + return 'field'; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php index b676d81984..c509abe6a5 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php @@ -23,179 +23,15 @@ use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Form\Exception\FormException; +/** + * Deprecated. You should extend FormType instead. + * + * @author Bernhard Schussek + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. + */ class FieldType extends AbstractType { - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilder $builder, array $options) - { - if (null === $options['property_path']) { - $options['property_path'] = $builder->getName(); - } - - if (false === $options['property_path'] || '' === $options['property_path']) { - $options['property_path'] = null; - } else { - $options['property_path'] = new PropertyPath($options['property_path']); - } - if (!is_array($options['attr'])) { - throw new FormException('The "attr" option must be "array".'); - } - - $builder - ->setRequired($options['required']) - ->setDisabled($options['disabled']) - ->setErrorBubbling($options['error_bubbling']) - ->setEmptyData($options['empty_data']) - ->setAttribute('read_only', $options['read_only']) - ->setAttribute('by_reference', $options['by_reference']) - ->setAttribute('property_path', $options['property_path']) - ->setAttribute('error_mapping', $options['error_mapping']) - ->setAttribute('max_length', $options['max_length']) - ->setAttribute('pattern', $options['pattern']) - ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) - ->setAttribute('attr', $options['attr'] ?: array()) - ->setAttribute('invalid_message', $options['invalid_message']) - ->setAttribute('invalid_message_parameters', $options['invalid_message_parameters']) - ->setAttribute('translation_domain', $options['translation_domain']) - ->setData($options['data']) - ->addEventSubscriber(new ValidationListener()) - ; - - if ($options['trim']) { - $builder->addEventSubscriber(new TrimListener()); - } - } - - /** - * {@inheritdoc} - */ - public function buildView(FormView $view, FormInterface $form) - { - $name = $form->getName(); - $readOnly = $form->getAttribute('read_only'); - - if ($view->hasParent()) { - if ('' === $name) { - throw new FormException('Form node with empty name can be used only as root form node.'); - } - - if ('' !== ($parentFullName = $view->getParent()->get('full_name'))) { - $id = sprintf('%s_%s', $view->getParent()->get('id'), $name); - $fullName = sprintf('%s[%s]', $parentFullName, $name); - } else { - $id = $name; - $fullName = $name; - } - - // Complex fields are read-only if themselves or their parent is. - $readOnly = $readOnly || $view->getParent()->get('read_only'); - } else { - $id = $name; - $fullName = $name; - - // Strip leading underscores and digits. These are allowed in - // form names, but not in HTML4 ID attributes. - // http://www.w3.org/TR/html401/struct/global.html#adef-id - $id = ltrim($id, '_0123456789'); - } - - $types = array(); - foreach ($form->getTypes() as $type) { - $types[] = $type->getName(); - } - - $view - ->set('form', $view) - ->set('id', $id) - ->set('name', $name) - ->set('full_name', $fullName) - ->set('read_only', $readOnly) - ->set('errors', $form->getErrors()) - ->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('size', null) - ->set('label', $form->getAttribute('label')) - ->set('multipart', false) - ->set('attr', $form->getAttribute('attr')) - ->set('types', $types) - ->set('translation_domain', $form->getAttribute('translation_domain')) - ; - } - - /** - * {@inheritdoc} - */ - public function getDefaultOptions() - { - // Derive "data_class" option from passed "data" object - $dataClass = function (Options $options) { - if (is_object($options['data'])) { - return get_class($options['data']); - } - - return null; - }; - - // Derive "empty_data" closure from "data_class" option - $emptyData = function (Options $options) { - $class = $options['data_class']; - - if (null !== $class) { - return function (FormInterface $form) use ($class) { - if ($form->isEmpty() && !$form->isRequired()) { - return null; - } - - return new $class(); - }; - } - - return ''; - }; - - return array( - 'data' => null, - 'data_class' => $dataClass, - 'empty_data' => $emptyData, - 'trim' => true, - 'required' => true, - 'read_only' => false, - 'disabled' => false, - 'max_length' => null, - 'pattern' => null, - 'property_path' => null, - 'by_reference' => true, - 'error_bubbling' => false, - 'error_mapping' => array(), - 'label' => null, - 'attr' => array(), - 'invalid_message' => 'This value is not valid', - 'invalid_message_parameters' => array(), - 'translation_domain' => 'messages', - ); - } - - /** - * {@inheritdoc} - */ - public function createBuilder($name, FormFactoryInterface $factory, array $options) - { - return new FormBuilder($name, $factory, new EventDispatcher(), $options['data_class']); - } - - /** - * {@inheritdoc} - */ - public function getParent(array $options) - { - return null; - } - /** * {@inheritdoc} */ @@ -203,9 +39,4 @@ class FieldType extends AbstractType { return 'field'; } - - private function humanize($text) - { - return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text)))); - } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 30f37203b2..468c65a0f3 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -23,12 +23,24 @@ class FileType extends AbstractType public function buildView(FormView $view, FormInterface $form) { $view - ->set('multipart', true) ->set('type', 'file') ->set('value', '') ; } + /** + * {@inheritdoc} + */ + public function buildViewBottomUp(FormView $view, FormInterface $form) + { + $view + ->set('multipart', true) + ; + } + + /** + * {@inheritdoc} + */ public function getParent(array $options) { return 'field'; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 36e0c0c63b..1738fb4f62 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -13,10 +13,16 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Options; +use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; +use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener; use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\Exception\FormException; class FormType extends AbstractType { @@ -25,9 +31,102 @@ class FormType extends AbstractType */ public function buildForm(FormBuilder $builder, array $options) { + if (null === $options['property_path']) { + $options['property_path'] = $builder->getName(); + } + + if (false === $options['property_path'] || '' === $options['property_path']) { + $options['property_path'] = null; + } else { + $options['property_path'] = new PropertyPath($options['property_path']); + } + if (!is_array($options['attr'])) { + throw new FormException('The "attr" option must be "array".'); + } + $builder + ->setRequired($options['required']) + ->setDisabled($options['disabled']) + ->setErrorBubbling($options['error_bubbling']) + ->setEmptyData($options['empty_data']) + ->setAttribute('read_only', $options['read_only']) + ->setAttribute('by_reference', $options['by_reference']) + ->setAttribute('property_path', $options['property_path']) + ->setAttribute('error_mapping', $options['error_mapping']) + ->setAttribute('max_length', $options['max_length']) + ->setAttribute('pattern', $options['pattern']) + ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) + ->setAttribute('attr', $options['attr'] ?: array()) + ->setAttribute('invalid_message', $options['invalid_message']) + ->setAttribute('invalid_message_parameters', $options['invalid_message_parameters']) + ->setAttribute('translation_domain', $options['translation_domain']) ->setAttribute('virtual', $options['virtual']) + ->setData($options['data']) ->setDataMapper(new PropertyPathMapper($options['data_class'])) + ->addEventSubscriber(new ValidationListener()) + ; + + if ($options['trim']) { + $builder->addEventSubscriber(new TrimListener()); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form) + { + $name = $form->getName(); + $readOnly = $form->getAttribute('read_only'); + + if ($view->hasParent()) { + if ('' === $name) { + throw new FormException('Form node with empty name can be used only as root form node.'); + } + + if ('' !== ($parentFullName = $view->getParent()->get('full_name'))) { + $id = sprintf('%s_%s', $view->getParent()->get('id'), $name); + $fullName = sprintf('%s[%s]', $parentFullName, $name); + } else { + $id = $name; + $fullName = $name; + } + + // Complex fields are read-only if themselves or their parent is. + $readOnly = $readOnly || $view->getParent()->get('read_only'); + } else { + $id = $name; + $fullName = $name; + + // Strip leading underscores and digits. These are allowed in + // form names, but not in HTML4 ID attributes. + // http://www.w3.org/TR/html401/struct/global.html#adef-id + $id = ltrim($id, '_0123456789'); + } + + $types = array(); + foreach ($form->getTypes() as $type) { + $types[] = $type->getName(); + } + + $view + ->set('form', $view) + ->set('id', $id) + ->set('name', $name) + ->set('full_name', $fullName) + ->set('read_only', $readOnly) + ->set('errors', $form->getErrors()) + ->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('size', null) + ->set('label', $form->getAttribute('label')) + ->set('multipart', false) + ->set('attr', $form->getAttribute('attr')) + ->set('types', $types) + ->set('translation_domain', $form->getAttribute('translation_domain')) ; } @@ -53,29 +152,75 @@ class FormType extends AbstractType */ public function getDefaultOptions() { - $emptyData = function (Options $options, $currentValue) { - if (empty($options['data_class'])) { - return array(); + // Derive "data_class" option from passed "data" object + $dataClass = function (Options $options) { + if (is_object($options['data'])) { + return get_class($options['data']); } - return $currentValue; + return null; }; + // Derive "empty_data" closure from "data_class" option + $emptyData = function (Options $options) { + $class = $options['data_class']; + + if (null !== $class) { + return function (FormInterface $form) use ($class) { + if ($form->isEmpty() && !$form->isRequired()) { + return null; + } + + return new $class(); + }; + } + + return function (FormInterface $form) { + if ($form->hasChildren()) { + return array(); + } + + return ''; + }; + }; + return array( + 'data' => null, + 'data_class' => $dataClass, 'empty_data' => $emptyData, + 'trim' => true, + 'required' => true, + 'read_only' => false, + 'disabled' => false, + 'max_length' => null, + 'pattern' => null, + 'property_path' => null, + 'by_reference' => true, + 'error_bubbling' => false, + 'error_mapping' => array(), + 'label' => null, + 'attr' => array(), 'virtual' => false, - // Errors in forms bubble by default, so that form errors will - // end up as global errors in the root form - 'error_bubbling' => true, + 'invalid_message' => 'This value is not valid', + 'invalid_message_parameters' => array(), + 'translation_domain' => 'messages', ); } + /** + * {@inheritdoc} + */ + public function createBuilder($name, FormFactoryInterface $factory, array $options) + { + return new FormBuilder($name, $factory, new EventDispatcher(), $options['data_class']); + } + /** * {@inheritdoc} */ public function getParent(array $options) { - return 'field'; + return null; } /** @@ -85,4 +230,9 @@ class FormType extends AbstractType { return 'form'; } + + private function humanize($text) + { + return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text)))); + } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 3f7258bb82..546220d0ab 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -150,7 +150,7 @@ class TimeType extends AbstractType // them like immutable value objects 'by_reference' => false, 'error_bubbling' => false, - // If initialized with a \DateTime object, FieldType initializes + // If initialized with a \DateTime object, FormType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -183,7 +183,7 @@ class TimeType extends AbstractType */ public function getParent(array $options) { - return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; + return 'field'; } /** diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php index 42505e2b76..5330bbda45 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php @@ -32,27 +32,13 @@ class CsrfExtension extends AbstractExtension $this->csrfProvider = $csrfProvider; } - /** - * {@inheritDoc} - */ - protected function loadTypes() - { - return array( - new Type\CsrfType($this->csrfProvider), - ); - } - /** * {@inheritDoc} */ protected function loadTypeExtensions() { return array( - new Type\ChoiceTypeCsrfExtension(), - new Type\DateTypeCsrfExtension(), - new Type\FormTypeCsrfExtension(), - new Type\RepeatedTypeCsrfExtension(), - new Type\TimeTypeCsrfExtension(), + new Type\FormTypeCsrfExtension($this->csrfProvider), ); } } diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index 3de52ab867..dd74d87b2e 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Extension\Csrf\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormError; -use Symfony\Component\Form\Event\DataEvent; +use Symfony\Component\Form\Event\FilterDataEvent; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; /** @@ -22,6 +22,12 @@ use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; */ class CsrfValidationListener implements EventSubscriberInterface { + /** + * The name of the CSRF field + * @var string + */ + private $fieldName; + /** * The provider for generating and validating CSRF tokens * @var CsrfProviderInterface @@ -45,24 +51,26 @@ class CsrfValidationListener implements EventSubscriberInterface ); } - public function __construct(CsrfProviderInterface $csrfProvider, $intention) + public function __construct($fieldName, CsrfProviderInterface $csrfProvider, $intention) { + $this->fieldName = $fieldName; $this->csrfProvider = $csrfProvider; $this->intention = $intention; } - public function onBindClientData(DataEvent $event) + public function onBindClientData(FilterDataEvent $event) { $form = $event->getForm(); $data = $event->getData(); - if ((!$form->hasParent() || $form->getParent()->isRoot()) - && !$this->csrfProvider->isCsrfTokenValid($this->intention, $data)) { - $form->addError(new FormError('The CSRF token is invalid. Please try to resubmit the form')); + if ($form->isRoot() && $form->hasChildren() && isset($data[$this->fieldName])) { + if (!$this->csrfProvider->isCsrfTokenValid($this->intention, $data[$this->fieldName])) { + $form->addError(new FormError('The CSRF token is invalid. Please try to resubmit the form')); + } - // If the session timed out, the token is invalid now. - // Regenerate the token so that a resubmission is possible. - $event->setData($this->csrfProvider->generateCsrfToken($this->intention)); + unset($data[$this->fieldName]); } + + $event->setData($data); } } diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php deleted file mode 100644 index 480d67c323..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\EventListener; - -use Symfony\Component\Form\Event\DataEvent; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; -use Symfony\Component\Form\FormFactoryInterface; - -/** - * Ensures the CSRF field. - * - * @author Bulat Shakirzyanov - * @author Kris Wallsmith - */ -class EnsureCsrfFieldListener -{ - private $factory; - private $name; - private $intention; - private $provider; - - /** - * Constructor. - * - * @param FormFactoryInterface $factory The form factory - * @param string $name A name for the CSRF field - * @param string $intention The intention string - * @param CsrfProviderInterface $provider The CSRF provider - */ - public function __construct(FormFactoryInterface $factory, $name, $intention = null, CsrfProviderInterface $provider = null) - { - $this->factory = $factory; - $this->name = $name; - $this->intention = $intention; - $this->provider = $provider; - } - - /** - * Ensures a root form has a CSRF field. - * - * This method should be connected to both form.pre_set_data and form.pre_bind. - */ - public function ensureCsrfField(DataEvent $event) - { - $form = $event->getForm(); - - $options = array(); - if ($this->intention) { - $options['intention'] = $this->intention; - } - if ($this->provider) { - $options['csrf_provider'] = $this->provider; - } - - $form->add($this->factory->createNamed('csrf', $this->name, null, $options)); - } -} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php deleted file mode 100644 index dc1e6db15b..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\Type; - -use Symfony\Component\Form\AbstractTypeExtension; - -class ChoiceTypeCsrfExtension extends AbstractTypeExtension -{ - public function getDefaultOptions() - { - return array('csrf_protection' => false); - } - - public function getExtendedType() - { - return 'choice'; - } -} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php b/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php deleted file mode 100644 index 97c69d8a5a..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\Type; - - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilder; -use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; - -class CsrfType extends AbstractType -{ - private $csrfProvider; - - /** - * Constructor. - * - * @param CsrfProviderInterface $csrfProvider The provider to use to generate the token - */ - public function __construct(CsrfProviderInterface $csrfProvider) - { - $this->csrfProvider = $csrfProvider; - } - - /** - * Builds the CSRF field. - * - * A validator is added to check the token value when the CSRF field is added to - * a root form - * - * @param FormBuilder $builder The form builder - * @param array $options The options - */ - public function buildForm(FormBuilder $builder, array $options) - { - $csrfProvider = $options['csrf_provider']; - $intention = $options['intention']; - - $builder - ->setData($csrfProvider->generateCsrfToken($intention)) - ->addEventSubscriber(new CsrfValidationListener($csrfProvider, $intention)) - ; - } - - /** - * {@inheritDoc} - */ - public function getDefaultOptions() - { - return array( - 'csrf_provider' => $this->csrfProvider, - 'intention' => null, - 'property_path' => false, - ); - } - - /** - * {@inheritDoc} - */ - public function getParent(array $options) - { - return 'hidden'; - } - - /** - * Returns the name of this form. - * - * @return string 'csrf' - */ - public function getName() - { - return 'csrf'; - } -} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php deleted file mode 100644 index dc54e94ddf..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\Type; - -use Symfony\Component\Form\AbstractTypeExtension; - -class DateTypeCsrfExtension extends AbstractTypeExtension -{ - public function getDefaultOptions() - { - return array('csrf_protection' => false); - } - - public function getExtendedType() - { - return 'date'; - } -} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index 8e2a57459a..c8687d4af4 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -12,21 +12,27 @@ namespace Symfony\Component\Form\Extension\Csrf\Type; use Symfony\Component\Form\AbstractTypeExtension; -use Symfony\Component\Form\Extension\Csrf\EventListener\EnsureCsrfFieldListener; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; +/** + * @author Bernhard Schussek + */ class FormTypeCsrfExtension extends AbstractTypeExtension { - private $enabled; - private $fieldName; + private $defaultCsrfProvider; + private $defaultEnabled; + private $defaultFieldName; - public function __construct($enabled = true, $fieldName = '_token') + public function __construct(CsrfProviderInterface $defaultCsrfProvider, $defaultEnabled = true, $defaultFieldName = '_token') { - $this->enabled = $enabled; - $this->fieldName = $fieldName; + $this->defaultCsrfProvider = $defaultCsrfProvider; + $this->defaultEnabled = $defaultEnabled; + $this->defaultFieldName = $defaultFieldName; } /** @@ -41,35 +47,35 @@ class FormTypeCsrfExtension extends AbstractTypeExtension return; } - $listener = new EnsureCsrfFieldListener( - $builder->getFormFactory(), - $options['csrf_field_name'], - $options['intention'], - $options['csrf_provider'] - ); - // use a low priority so higher priority listeners don't remove the field $builder ->setAttribute('csrf_field_name', $options['csrf_field_name']) - ->addEventListener(FormEvents::PRE_SET_DATA, array($listener, 'ensureCsrfField'), -10) - ->addEventListener(FormEvents::PRE_BIND, array($listener, 'ensureCsrfField'), -10) + ->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'])) ; } /** - * Removes CSRF fields from all the form views except the root one. + * Adds a CSRF field to the root form view. * * @param FormView $view The form view * @param FormInterface $form The form */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form) { - if ($view->hasParent() && $form->hasAttribute('csrf_field_name')) { + if ($form->isRoot() && $form->hasChildren() && $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( + 'property_path' => false, + )); - if (isset($view[$name])) { - unset($view[$name]); - } + $view->addChild($csrfForm->createView($view)); } } @@ -79,9 +85,9 @@ class FormTypeCsrfExtension extends AbstractTypeExtension public function getDefaultOptions() { return array( - 'csrf_protection' => $this->enabled, - 'csrf_field_name' => $this->fieldName, - 'csrf_provider' => null, + 'csrf_protection' => $this->defaultEnabled, + 'csrf_field_name' => $this->defaultFieldName, + 'csrf_provider' => $this->defaultCsrfProvider, 'intention' => 'unknown', ); } diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php deleted file mode 100644 index 1115ea4d69..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\Type; - -use Symfony\Component\Form\AbstractTypeExtension; - -class RepeatedTypeCsrfExtension extends AbstractTypeExtension -{ - public function getDefaultOptions() - { - return array('csrf_protection' => false); - } - - public function getExtendedType() - { - return 'repeated'; - } -} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php deleted file mode 100644 index dbd7c0d2df..0000000000 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Csrf\Type; - -use Symfony\Component\Form\AbstractTypeExtension; - -class TimeTypeCsrfExtension extends AbstractTypeExtension -{ - public function getDefaultOptions() - { - return array('csrf_protection' => false); - } - - public function getExtendedType() - { - return 'time'; - } -} diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php similarity index 95% rename from src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php rename to src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php index 8d0db3f4f6..77754669f3 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -19,7 +19,7 @@ use Symfony\Component\Validator\ValidatorInterface; /** * @author Bernhard Schussek */ -class FieldTypeValidatorExtension extends AbstractTypeExtension +class FormTypeValidatorExtension extends AbstractTypeExtension { private $validator; @@ -57,6 +57,6 @@ class FieldTypeValidatorExtension extends AbstractTypeExtension public function getExtendedType() { - return 'field'; + return 'form'; } } diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php index 94b6ea699d..b4efa1679a 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php @@ -38,7 +38,7 @@ class ValidatorExtension extends AbstractExtension protected function loadTypeExtensions() { return array( - new Type\FieldTypeValidatorExtension($this->validator), + new Type\FormTypeValidatorExtension($this->validator), ); } } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 8f9e7b6de2..5669e0ead4 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -191,7 +191,7 @@ class Form implements \IteratorAggregate, FormInterface array $types = array(), array $clientTransformers = array(), array $normTransformers = array(), DataMapperInterface $dataMapper = null, array $validators = array(), - $required = false, $disabled = false, $errorBubbling = false, + $required = false, $disabled = false, $errorBubbling = null, $emptyData = null, array $attributes = array()) { $name = (string) $name; @@ -225,7 +225,10 @@ class Form implements \IteratorAggregate, FormInterface $this->validators = $validators; $this->required = (Boolean) $required; $this->disabled = (Boolean) $disabled; - $this->errorBubbling = (Boolean) $errorBubbling; + // NULL is the default meaning: + // bubble up if the form has children (complex forms) + // don't bubble up if the form has no children (primitive fields) + $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; $this->emptyData = $emptyData; $this->attributes = $attributes; @@ -312,9 +315,9 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Returns the parent field. + * Returns the parent form. * - * @return FormInterface The parent field + * @return FormInterface The parent form */ public function getParent() { @@ -342,7 +345,7 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Returns whether the field is the root of the form tree. + * Returns whether the form is the root of the form tree. * * @return Boolean */ @@ -374,7 +377,7 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Updates the field with default data. + * Updates the form with default data. * * @param array $appData The data formatted as expected for the underlying object * @@ -408,7 +411,7 @@ class Form implements \IteratorAggregate, FormInterface $this->clientData = $clientData; $this->synchronized = true; - if ($this->dataMapper) { + if (count($this->children) > 0 && $this->dataMapper) { // Update child forms from the data $this->dataMapper->mapDataToForms($clientData, $this->children); } @@ -450,7 +453,7 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Binds data to the field, transforms and validates it. + * Binds data to the form, transforms and validates it. * * @param string|array $clientData The data * @@ -626,11 +629,11 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Returns the normalized data of the field. + * Returns the normalized data of the form. * - * @return mixed When the field is not bound, the default data is returned. - * When the field is bound, the normalized bound data is - * returned if the field is valid, null otherwise. + * @return mixed When the form is not bound, the default data is returned. + * When the form is bound, the normalized bound data is + * returned if the form is valid, null otherwise. */ public function getNormData() { @@ -646,7 +649,7 @@ class Form implements \IteratorAggregate, FormInterface */ public function addError(FormError $error) { - if ($this->parent && $this->errorBubbling) { + if ($this->parent && $this->getErrorBubbling()) { $this->parent->addError($error); } else { $this->errors[] = $error; @@ -662,11 +665,11 @@ class Form implements \IteratorAggregate, FormInterface */ public function getErrorBubbling() { - return $this->errorBubbling; + return null === $this->errorBubbling ? $this->hasChildren() : $this->errorBubbling; } /** - * Returns whether the field is bound. + * Returns whether the form is bound. * * @return Boolean true if the form is bound to input values, false otherwise */ @@ -702,7 +705,7 @@ class Form implements \IteratorAggregate, FormInterface } /** - * Returns whether the field is valid. + * Returns whether the form is valid. * * @return Boolean */ @@ -735,9 +738,8 @@ class Form implements \IteratorAggregate, FormInterface public function hasErrors() { // Don't call isValid() here, as its semantics are slightly different - // Field groups are not valid if their children are invalid, but - // hasErrors() returns only true if a field/field group itself has - // errors + // Forms are not valid if their children are invalid, but + // hasErrors() returns only true if a form itself has errors return count($this->errors) > 0; } @@ -894,7 +896,7 @@ class Form implements \IteratorAggregate, FormInterface return $this->children[$name]; } - throw new \InvalidArgumentException(sprintf('Field "%s" does not exist.', $name)); + throw new \InvalidArgumentException(sprintf('Child "%s" does not exist.', $name)); } /** @@ -975,7 +977,7 @@ class Form implements \IteratorAggregate, FormInterface $parent = $this->parent->createView(); } - $view = new FormView(); + $view = new FormView($this->name); $view->setParent($parent); @@ -989,14 +991,10 @@ class Form implements \IteratorAggregate, FormInterface } } - $childViews = array(); - - foreach ($this->children as $key => $child) { - $childViews[$key] = $child->createView($view); + foreach ($this->children as $child) { + $view->addChild($child->createView($view)); } - $view->setChildren($childViews); - foreach ($types as $type) { $type->buildViewBottomUp($view, $this); diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 208a518518..b8f7da16c6 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -106,7 +106,7 @@ class FormBuilder * Whether added errors should bubble up to the parent * @var Boolean */ - private $errorBubbling = false; + private $errorBubbling; /** * Data used for the client data when no value is bound @@ -243,7 +243,7 @@ class FormBuilder */ public function setErrorBubbling($errorBubbling) { - $this->errorBubbling = (Boolean) $errorBubbling; + $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; return $this; } diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index d0d8a80a8d..f3ca837356 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -183,7 +183,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * The content of a disabled form is displayed, but not allowed to be * modified. The validation of modified disabled forms should fail. * - * Fields whose parents are disabled are considered disabled regardless of + * Forms whose parents are disabled are considered disabled regardless of * their own state. * * @return Boolean diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php index b3ca91a460..9d915698ca 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserChain.php +++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php @@ -75,7 +75,7 @@ class FormTypeGuesserChain implements FormTypeGuesserInterface * @param \Closure $closure The closure to execute. Accepts a guesser * as argument and should return a Guess instance * - * @return FieldFactoryGuess The guess with the highest confidence + * @return Guess The guess with the highest confidence */ private function guess(\Closure $closure) { diff --git a/src/Symfony/Component/Form/FormView.php b/src/Symfony/Component/Form/FormView.php index 5c8ab13da1..5d88346edc 100644 --- a/src/Symfony/Component/Form/FormView.php +++ b/src/Symfony/Component/Form/FormView.php @@ -11,8 +11,17 @@ namespace Symfony\Component\Form; -class FormView implements \ArrayAccess, \IteratorAggregate, \Countable +use ArrayAccess; +use IteratorAggregate; +use Countable; + +/** + * @author Bernhard Schussek + */ +class FormView implements ArrayAccess, IteratorAggregate, Countable { + private $name; + private $vars = array( 'value' => null, 'attr' => array(), @@ -33,6 +42,16 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable */ private $rendered = false; + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + /** * @param string $name * @param mixed $value @@ -177,15 +196,29 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable } /** - * Sets the children view. + * Adds a child view. * - * @param array $children The children as instances of FormView + * @param FormView $child The child view to add. * * @return FormView The current view */ - public function setChildren(array $children) + public function addChild(FormView $child) { - $this->children = $children; + $this->children[$child->getName()] = $child; + + return $this; + } + + /** + * Removes a child view. + * + * @param string $name The name of the removed child view. + * + * @return FormView The current view + */ + public function removeChild($name) + { + unset($this->children[$name]); return $this; } @@ -222,6 +255,18 @@ class FormView implements \ArrayAccess, \IteratorAggregate, \Countable return count($this->children) > 0; } + /** + * Returns whether this view has a given child. + * + * @param string $name The name of the child + * + * @return Boolean Whether the child with the given name exists + */ + public function hasChild($name) + { + return isset($this->children[$name]); + } + /** * Returns a child by name (implements \ArrayAccess). * diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index d22113245a..7d1512f003 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -202,7 +202,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest ); } - public function testRestAndRepeatedWithRowPerField() + public function testRestAndRepeatedWithRowPerChild() { $view = $this->factory->createNamedBuilder('form', 'name') ->add('first', 'text') @@ -230,7 +230,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest ); } - public function testRestAndRepeatedWithWidgetPerField() + public function testRestAndRepeatedWithWidgetPerChild() { $view = $this->factory->createNamedBuilder('form', 'name') ->add('first', 'text') @@ -348,7 +348,10 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest public function testNestedFormError() { $form = $this->factory->createNamedBuilder('form', 'name') - ->add('child', 'form', array('error_bubbling' => false)) + ->add($this->factory + ->createNamedBuilder('form', 'child', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) ->getForm(); $form->get('child')->addError(new FormError('Error!')); @@ -363,6 +366,31 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest ); } + public function testCsrf() + { + $this->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('form', 'name') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('form', 'child') + ->add($this->factory->createNamedBuilder('text', 'grandchild')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="hidden"][@id="name__token"][@value="foo&bar"] + /following-sibling::div + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + public function testRepeated() { $form = $this->factory->createNamed('repeated', 'name', 'foobar', array( @@ -372,7 +400,8 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="name__token"] + /following-sibling::div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] @@ -383,7 +412,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest /following-sibling::input[@type="text"][@id="name_second"] ] ] - [count(.//input)=2] + [count(.//input)=3] ' ); } @@ -399,7 +428,8 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="name__token"] + /following-sibling::div [ ./label[@for="name_first"][.="[trans]Test[/trans]"] /following-sibling::input[@type="text"][@id="name_first"][@required="required"] @@ -410,7 +440,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest /following-sibling::input[@type="text"][@id="name_second"][@required="required"] ] ] - [count(.//input)=2] + [count(.//input)=3] ' ); } diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 32aabb86a6..5d48b9921c 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -646,12 +646,13 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=2] + [count(./input)=3] ' ); } @@ -668,12 +669,13 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=2] + [count(./input)=3] ' ); } @@ -689,12 +691,13 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=2] + [count(./input)=3] ' ); } @@ -711,14 +714,15 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] ] - [count(./input)=3] + [count(./input)=4] ' ); } @@ -753,22 +757,6 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase ); } - public function testCsrf() - { - $this->csrfProvider->expects($this->any()) - ->method('generateCsrfToken') - ->will($this->returnValue('foo&bar')); - - $form = $this->factory->createNamed('csrf', 'name'); - - $this->assertWidgetMatchesXpath($form->createView(), array(), -'/input - [@type="hidden"] - [@value="foo&bar"] -' - ); - } - public function testDateTime() { $form = $this->factory->createNamed('datetime', 'name', '2011-02-03 04:05:06', array( diff --git a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php index d47b15564f..0943b06c41 100644 --- a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -45,36 +45,10 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest $html = $this->renderRow($form->createView()); $this->assertMatchesXpath($html, -'/tr - [ - ./td - [./label[@for="name_first"]] - /following-sibling::td - [./input[@id="name_first"]] - ] -/following-sibling::tr - [ - ./td - [./label[@for="name_second"]] - /following-sibling::td - [./input[@id="name_second"]] - ] - [count(../tr)=2] -' - ); - } - - public function testRepeatedRowWithErrors() - { - $form = $this->factory->createNamed('repeated', 'name'); - $form->addError(new FormError('Error!')); - $view = $form->createView(); - $html = $this->renderRow($view); - - $this->assertMatchesXpath($html, -'/tr - [./td[@colspan="2"]/ul - [./li[.="[trans]Error![/trans]"]] +'/tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] ] /following-sibling::tr [ @@ -95,6 +69,42 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest ); } + public function testRepeatedRowWithErrors() + { + $form = $this->factory->createNamed('repeated', 'name'); + $form->addError(new FormError('Error!')); + $view = $form->createView(); + $html = $this->renderRow($view); + + $this->assertMatchesXpath($html, +'/tr + [./td[@colspan="2"]/ul + [./li[.="[trans]Error![/trans]"]] + ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_first"]] + /following-sibling::td + [./input[@id="name_first"]] + ] +/following-sibling::tr + [ + ./td + [./label[@for="name_second"]] + /following-sibling::td + [./input[@id="name_second"]] + ] + [count(../tr)=4] +' + ); + } + public function testRest() { $view = $this->factory->createNamedBuilder('form', 'name') @@ -151,9 +161,9 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr[./td/input[@type="text"][@value="a"]] + ./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] + /following-sibling::tr[./td/input[@type="text"][@value="a"]] /following-sibling::tr[./td/input[@type="text"][@value="b"]] - /following-sibling::tr[./td/input[@type="hidden"][@id="name__token"]] ] [count(./tr[./td/input])=3] ' @@ -200,7 +210,10 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest public function testNestedFormError() { $form = $this->factory->createNamedBuilder('form', 'name') - ->add('child', 'form', array('error_bubbling' => false)) + ->add($this->factory + ->createNamedBuilder('form', 'child', null, array('error_bubbling' => false)) + ->add('grandChild', 'form') + ) ->getForm(); $form->get('child')->addError(new FormError('Error!')); @@ -217,6 +230,34 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest ); } + public function testCsrf() + { + $this->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamedBuilder('form', 'name') + ->add($this->factory + // No CSRF protection on nested forms + ->createNamedBuilder('form', 'child') + ->add($this->factory->createNamedBuilder('text', 'grandchild')) + ) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + ] + [count(.//input[@type="hidden"])=1] +' + ); + } + public function testRepeated() { $form = $this->factory->createNamed('repeated', 'name', 'foobar', array( @@ -226,7 +267,12 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + /following-sibling::tr [ ./td [./label[@for="name_first"]] @@ -241,7 +287,7 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest [./input[@type="text"][@id="name_second"]] ] ] - [count(.//input)=2] + [count(.//input)=3] ' ); } @@ -257,7 +303,12 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + /following-sibling::tr [ ./td [./label[@for="name_first"][.="[trans]Test[/trans]"]] @@ -272,7 +323,7 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest [./input[@type="password"][@id="name_second"][@required="required"]] ] ] - [count(.//input)=2] + [count(.//input)=3] ' ); } 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 9cf54e25ed..fbda65a3b6 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -98,7 +98,7 @@ class ChoiceTypeTest extends TypeTestCase )); } - public function testExpandedChoicesOptionsTurnIntoFields() + public function testExpandedChoicesOptionsTurnIntoChildren() { $form = $this->factory->create('choice', null, array( 'expanded' => true, @@ -141,7 +141,7 @@ class ChoiceTypeTest extends TypeTestCase } } - public function testExpandedRadiosAreRequiredIfChoiceFieldIsRequired() + public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -155,7 +155,7 @@ class ChoiceTypeTest extends TypeTestCase } } - public function testExpandedRadiosAreNotRequiredIfChoiceFieldIsNotRequired() + public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -288,7 +288,7 @@ class ChoiceTypeTest extends TypeTestCase $this->assertNull($form[4]->getClientData()); } - public function testBindSingleExpandedWithFalseDoesNotHaveExtraFields() + public function testBindSingleExpandedWithFalseDoesNotHaveExtraChildren() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -302,7 +302,7 @@ class ChoiceTypeTest extends TypeTestCase $this->assertNull($form->getData()); } - public function testBindSingleExpandedWithEmptyField() + public function testBindSingleExpandedWithEmptyChild() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -422,7 +422,7 @@ class ChoiceTypeTest extends TypeTestCase $this->assertNull($form[4]->getClientData()); } - public function testBindMultipleExpandedWithEmptyField() + public function testBindMultipleExpandedWithEmptyChild() { $form = $this->factory->create('choice', null, array( 'multiple' => true, 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 b0bb62508f..5461258e95 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -15,10 +15,10 @@ use Symfony\Component\Form\Form; class CollectionTypeTest extends TypeTestCase { - public function testContainsNoFieldByDefault() + public function testContainsNoChildByDefault() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', )); $this->assertCount(0, $form); @@ -27,7 +27,7 @@ class CollectionTypeTest extends TypeTestCase public function testSetDataAdjustsSize() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'options' => array( 'max_length' => 20, ), @@ -53,7 +53,7 @@ class CollectionTypeTest extends TypeTestCase public function testThrowsExceptionIfObjectIsNotTraversable() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', )); $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); $form->setData(new \stdClass()); @@ -62,7 +62,7 @@ class CollectionTypeTest extends TypeTestCase public function testNotResizedIfBoundWithMissingData() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', )); $form->setData(array('foo@foo.com', 'bar@bar.com')); $form->bind(array('foo@bar.com')); @@ -76,7 +76,7 @@ class CollectionTypeTest extends TypeTestCase public function testResizedDownIfBoundWithMissingDataAndAllowDelete() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'allow_delete' => true, )); $form->setData(array('foo@foo.com', 'bar@bar.com')); @@ -91,7 +91,7 @@ class CollectionTypeTest extends TypeTestCase public function testNotResizedIfBoundWithExtraData() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', )); $form->setData(array('foo@bar.com')); $form->bind(array('foo@foo.com', 'bar@bar.com')); @@ -104,7 +104,7 @@ class CollectionTypeTest extends TypeTestCase public function testResizedUpIfBoundWithExtraDataAndAllowAdd() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'allow_add' => true, )); $form->setData(array('foo@bar.com')); @@ -120,7 +120,7 @@ class CollectionTypeTest extends TypeTestCase public function testAllowAddButNoPrototype() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'allow_add' => true, 'prototype' => false, )); @@ -169,7 +169,7 @@ class CollectionTypeTest extends TypeTestCase public function testPrototypeNameOption() { $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'prototype' => true, 'allow_add' => true, )); @@ -177,7 +177,7 @@ class CollectionTypeTest extends TypeTestCase $this->assertSame('__name__', $form->getAttribute('prototype')->getName(), '__name__ is the default'); $form = $this->factory->create('collection', null, array( - 'type' => 'field', + 'type' => 'form', 'prototype' => true, 'allow_add' => true, 'prototype_name' => '__test__', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php deleted file mode 100644 index b588d4433f..0000000000 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php +++ /dev/null @@ -1,380 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Tests\Extension\Core\Type; - -use Symfony\Component\Form\Util\PropertyPath; -use Symfony\Component\Form\Form; -use Symfony\Component\Form\Tests\Fixtures\Author; -use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; - -class FieldTypeTest extends TypeTestCase -{ - public function testGetPropertyPathDefaultPath() - { - $form = $this->factory->createNamed('field', 'title'); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsZero() - { - $form = $this->factory->create('field', null, array('property_path' => '0')); - - $this->assertEquals(new PropertyPath('0'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsEmpty() - { - $form = $this->factory->create('field', null, array('property_path' => '')); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsFalse() - { - $form = $this->factory->create('field', null, array('property_path' => false)); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsNull() - { - $form = $this->factory->createNamed('field', 'title', null, array('property_path' => null)); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - - public function testPassRequiredAsOption() - { - $form = $this->factory->create('field', null, array('required' => false)); - - $this->assertFalse($form->isRequired()); - - $form = $this->factory->create('field', null, array('required' => true)); - - $this->assertTrue($form->isRequired()); - } - - public function testPassDisabledAsOption() - { - $form = $this->factory->create('field', null, array('disabled' => true)); - - $this->assertTrue($form->isDisabled()); - } - - public function testBoundDataIsTrimmedBeforeTransforming() - { - $form = $this->factory->createBuilder('field') - ->appendClientTransformer(new FixedDataTransformer(array( - null => '', - 'reverse[a]' => 'a', - ))) - ->getForm(); - - $form->bind(' a '); - - $this->assertEquals('a', $form->getClientData()); - $this->assertEquals('reverse[a]', $form->getData()); - } - - public function testBoundDataIsNotTrimmedBeforeTransformingIfNoTrimming() - { - $form = $this->factory->createBuilder('field', null, array('trim' => false)) - ->appendClientTransformer(new FixedDataTransformer(array( - null => '', - 'reverse[ a ]' => ' a ', - ))) - ->getForm(); - - $form->bind(' a '); - - $this->assertEquals(' a ', $form->getClientData()); - $this->assertEquals('reverse[ a ]', $form->getData()); - } - - public function testPassIdAndNameToView() - { - $form = $this->factory->createNamed('field', 'name'); - $view = $form->createView(); - - $this->assertEquals('name', $view->get('id')); - $this->assertEquals('name', $view->get('name')); - $this->assertEquals('name', $view->get('full_name')); - } - - public function testStripLeadingUnderscoresAndDigitsFromId() - { - $form = $this->factory->createNamed('field', '_09name'); - $view = $form->createView(); - - $this->assertEquals('name', $view->get('id')); - $this->assertEquals('_09name', $view->get('name')); - $this->assertEquals('_09name', $view->get('full_name')); - } - - public function testPassIdAndNameToViewWithParent() - { - $parent = $this->factory->createNamed('field', 'parent'); - $parent->add($this->factory->createNamed('field', 'child')); - $view = $parent->createView(); - - $this->assertEquals('parent_child', $view['child']->get('id')); - $this->assertEquals('child', $view['child']->get('name')); - $this->assertEquals('parent[child]', $view['child']->get('full_name')); - } - - public function testPassIdAndNameToViewWithGrandParent() - { - $parent = $this->factory->createNamed('field', 'parent'); - $parent->add($this->factory->createNamed('field', 'child')); - $parent['child']->add($this->factory->createNamed('field', 'grand_child')); - $view = $parent->createView(); - - $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id')); - $this->assertEquals('grand_child', $view['child']['grand_child']->get('name')); - $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name')); - } - - public function testNonReadOnlyFieldWithReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent', null, array('read_only' => true)); - $child = $this->factory->createNamed('field', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testReadOnlyFieldWithNonReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent'); - $child = $this->factory->createNamed('field', 'child', null, array('read_only' => true)); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testNonReadOnlyFieldWithNonReadOnlyParentBeingNonReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent'); - $child = $this->factory->createNamed('field', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertFalse($view['child']->get('read_only')); - } - - public function testPassMaxLengthToView() - { - $form = $this->factory->create('field', null, array('max_length' => 10)); - $view = $form->createView(); - - $this->assertSame(10, $view->get('max_length')); - } - - public function testPassTranslationDomainToView() - { - $form = $this->factory->create('field', null, array('translation_domain' => 'test')); - $view = $form->createView(); - - $this->assertSame('test', $view->get('translation_domain')); - } - - public function testPassDefaultLabelToView() - { - $form = $this->factory->createNamed('field', '__test___field'); - $view = $form->createView(); - - $this->assertSame('Test field', $view->get('label')); - } - - public function testPassLabelToView() - { - $form = $this->factory->createNamed('field', '__test___field', null, array('label' => 'My label')); - $view = $form->createView(); - - $this->assertSame('My label', $view->get('label')); - } - - public function testDefaultTranslationDomain() - { - $form = $this->factory->create('field'); - $view = $form->createView(); - - $this->assertSame('messages', $view->get('translation_domain')); - } - - public function testBindWithEmptyDataCreatesObjectIfClassAvailable() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => false, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - $form->add($this->factory->createNamed('field', 'lastName')); - - $form->setData(null); - // partially empty, still an object is created - $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); - - $author = new Author(); - $author->firstName = 'Bernhard'; - $author->setLastName(''); - - $this->assertEquals($author, $form->getData()); - } - - public function testBindWithEmptyDataCreatesObjectIfInitiallyBoundWithObject() - { - $form = $this->factory->create('form', null, array( - // data class is inferred from the passed object - 'data' => new Author(), - 'required' => false, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - $form->add($this->factory->createNamed('field', 'lastName')); - - $form->setData(null); - // partially empty, still an object is created - $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); - - $author = new Author(); - $author->firstName = 'Bernhard'; - $author->setLastName(''); - - $this->assertEquals($author, $form->getData()); - } - - public function testBindWithEmptyDataDoesNotCreateObjectIfDataClassIsNull() - { - $form = $this->factory->create('form', null, array( - 'data' => new Author(), - 'data_class' => null, - 'required' => false, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - - $form->setData(null); - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); - } - - public function testBindEmptyWithEmptyDataCreatesNoObjectIfNotRequired() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => false, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - $form->add($this->factory->createNamed('field', 'lastName')); - - $form->setData(null); - $form->bind(array('firstName' => '', 'lastName' => '')); - - $this->assertNull($form->getData()); - } - - public function testBindEmptyWithEmptyDataCreatesObjectIfRequired() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => true, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - $form->add($this->factory->createNamed('field', 'lastName')); - - $form->setData(null); - $form->bind(array('firstName' => '', 'lastName' => '')); - - $this->assertEquals(new Author(), $form->getData()); - } - - /* - * We need something to write the field values into - */ - public function testBindWithEmptyDataStoresArrayIfNoClassAvailable() - { - $form = $this->factory->create('form'); - $form->add($this->factory->createNamed('field', 'firstName')); - - $form->setData(null); - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); - } - - public function testBindWithEmptyDataUsesEmptyDataOption() - { - $author = new Author(); - - $form = $this->factory->create('form', null, array( - 'empty_data' => $author, - )); - $form->add($this->factory->createNamed('field', 'firstName')); - - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame($author, $form->getData()); - $this->assertEquals('Bernhard', $author->firstName); - } - - public function testGetAttributesIsEmpty() - { - $form = $this->factory->create('field', null, array('attr' => array())); - - $this->assertCount(0, $form->getAttribute('attr')); - } - - /** - * @see https://github.com/symfony/symfony/issues/1986 - */ - public function testSetDataThroughParamsWithZero() - { - $form = $this->factory->create('field', null, array('data' => 0)); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('0', $view->get('value')); - $this->assertSame('0', $form->getData()); - - $form = $this->factory->create('field', null, array('data' => '0')); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('0', $view->get('value')); - $this->assertSame('0', $form->getData()); - - $form = $this->factory->create('field', null, array('data' => '00000')); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('00000', $view->get('value')); - $this->assertSame('00000', $form->getData()); - } - - /** - * @expectedException Symfony\Component\Form\Exception\FormException - */ - public function testAttributesException() - { - $form = $this->factory->create('field', null, array('attr' => '')); - } - - public function testNameCanBeEmptyString() - { - $form = $this->factory->createNamed('field', ''); - - $this->assertEquals('', $form->getName()); - } -} 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 eff0719692..8275c61455 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\Form; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; class FormTest_AuthorWithoutRefSetter { @@ -49,13 +51,388 @@ class FormTest_AuthorWithoutRefSetter class FormTypeTest extends TypeTestCase { + public function testGetPropertyPathDefaultPath() + { + $form = $this->factory->createNamed('form', 'title'); + + $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsZero() + { + $form = $this->factory->create('form', null, array('property_path' => '0')); + + $this->assertEquals(new PropertyPath('0'), $form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsEmpty() + { + $form = $this->factory->create('form', null, array('property_path' => '')); + + $this->assertNull($form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsFalse() + { + $form = $this->factory->create('form', null, array('property_path' => false)); + + $this->assertNull($form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsNull() + { + $form = $this->factory->createNamed('form', 'title', null, array('property_path' => null)); + + $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); + } + + public function testPassRequiredAsOption() + { + $form = $this->factory->create('form', null, array('required' => false)); + + $this->assertFalse($form->isRequired()); + + $form = $this->factory->create('form', null, array('required' => true)); + + $this->assertTrue($form->isRequired()); + } + + public function testPassDisabledAsOption() + { + $form = $this->factory->create('form', null, array('disabled' => true)); + + $this->assertTrue($form->isDisabled()); + } + + public function testBoundDataIsTrimmedBeforeTransforming() + { + $form = $this->factory->createBuilder('form') + ->appendClientTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[a]' => 'a', + ))) + ->getForm(); + + $form->bind(' a '); + + $this->assertEquals('a', $form->getClientData()); + $this->assertEquals('reverse[a]', $form->getData()); + } + + public function testBoundDataIsNotTrimmedBeforeTransformingIfNoTrimming() + { + $form = $this->factory->createBuilder('form', null, array('trim' => false)) + ->appendClientTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[ a ]' => ' a ', + ))) + ->getForm(); + + $form->bind(' a '); + + $this->assertEquals(' a ', $form->getClientData()); + $this->assertEquals('reverse[ a ]', $form->getData()); + } + + public function testPassIdAndNameToView() + { + $form = $this->factory->createNamed('form', 'name'); + $view = $form->createView(); + + $this->assertEquals('name', $view->get('id')); + $this->assertEquals('name', $view->get('name')); + $this->assertEquals('name', $view->get('full_name')); + } + + public function testStripLeadingUnderscoresAndDigitsFromId() + { + $form = $this->factory->createNamed('form', '_09name'); + $view = $form->createView(); + + $this->assertEquals('name', $view->get('id')); + $this->assertEquals('_09name', $view->get('name')); + $this->assertEquals('_09name', $view->get('full_name')); + } + + public function testPassIdAndNameToViewWithParent() + { + $parent = $this->factory->createNamed('form', 'parent'); + $parent->add($this->factory->createNamed('form', 'child')); + $view = $parent->createView(); + + $this->assertEquals('parent_child', $view['child']->get('id')); + $this->assertEquals('child', $view['child']->get('name')); + $this->assertEquals('parent[child]', $view['child']->get('full_name')); + } + + public function testPassIdAndNameToViewWithGrandParent() + { + $parent = $this->factory->createNamed('form', 'parent'); + $parent->add($this->factory->createNamed('form', 'child')); + $parent['child']->add($this->factory->createNamed('form', 'grand_child')); + $view = $parent->createView(); + + $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id')); + $this->assertEquals('grand_child', $view['child']['grand_child']->get('name')); + $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name')); + } + + public function testNonReadOnlyFormWithReadOnlyParentBeingReadOnly() + { + $parent = $this->factory->createNamed('form', 'parent', null, array('read_only' => true)); + $child = $this->factory->createNamed('form', 'child'); + $view = $parent->add($child)->createView(); + + $this->assertTrue($view['child']->get('read_only')); + } + + public function testReadOnlyFormWithNonReadOnlyParentBeingReadOnly() + { + $parent = $this->factory->createNamed('form', 'parent'); + $child = $this->factory->createNamed('form', 'child', null, array('read_only' => true)); + $view = $parent->add($child)->createView(); + + $this->assertTrue($view['child']->get('read_only')); + } + + public function testNonReadOnlyFormWithNonReadOnlyParentBeingNonReadOnly() + { + $parent = $this->factory->createNamed('form', 'parent'); + $child = $this->factory->createNamed('form', 'child'); + $view = $parent->add($child)->createView(); + + $this->assertFalse($view['child']->get('read_only')); + } + + public function testPassMaxLengthToView() + { + $form = $this->factory->create('form', null, array('max_length' => 10)); + $view = $form->createView(); + + $this->assertSame(10, $view->get('max_length')); + } + + public function testPassTranslationDomainToView() + { + $form = $this->factory->create('form', null, array('translation_domain' => 'test')); + $view = $form->createView(); + + $this->assertSame('test', $view->get('translation_domain')); + } + + public function testPassDefaultLabelToView() + { + $form = $this->factory->createNamed('form', '__test___field'); + $view = $form->createView(); + + $this->assertSame('Test field', $view->get('label')); + } + + public function testPassLabelToView() + { + $form = $this->factory->createNamed('form', '__test___field', null, array('label' => 'My label')); + $view = $form->createView(); + + $this->assertSame('My label', $view->get('label')); + } + + public function testDefaultTranslationDomain() + { + $form = $this->factory->create('form'); + $view = $form->createView(); + + $this->assertSame('messages', $view->get('translation_domain')); + } + + public function testBindWithEmptyDataCreatesObjectIfClassAvailable() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + $form->add($this->factory->createNamed('form', 'lastName')); + + $form->setData(null); + // partially empty, still an object is created + $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testBindWithEmptyDataCreatesObjectIfInitiallyBoundWithObject() + { + $form = $this->factory->create('form', null, array( + // data class is inferred from the passed object + 'data' => new Author(), + 'required' => false, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + $form->add($this->factory->createNamed('form', 'lastName')); + + $form->setData(null); + // partially empty, still an object is created + $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testBindWithEmptyDataDoesNotCreateObjectIfDataClassIsNull() + { + $form = $this->factory->create('form', null, array( + 'data' => new Author(), + 'data_class' => null, + 'required' => false, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + + $form->setData(null); + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testBindEmptyWithEmptyDataCreatesNoObjectIfNotRequired() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + $form->add($this->factory->createNamed('form', 'lastName')); + + $form->setData(null); + $form->bind(array('firstName' => '', 'lastName' => '')); + + $this->assertNull($form->getData()); + } + + public function testBindEmptyWithEmptyDataCreatesObjectIfRequired() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => true, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + $form->add($this->factory->createNamed('form', 'lastName')); + + $form->setData(null); + $form->bind(array('firstName' => '', 'lastName' => '')); + + $this->assertEquals(new Author(), $form->getData()); + } + + /* + * We need something to write the field values into + */ + public function testBindWithEmptyDataStoresArrayIfNoClassAvailable() + { + $form = $this->factory->create('form'); + $form->add($this->factory->createNamed('form', 'firstName')); + + $form->setData(null); + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testBindWithEmptyDataPassesEmptyStringToTransformerIfNoChildren() + { + $form = $this->factory->createBuilder('form') + ->appendClientTransformer(new FixedDataTransformer(array( + // required for the initial, internal setData(null) + null => 'null', + // required to test that bind(null) is converted to '' + 'empty' => '', + ))) + ->getForm(); + + $form->bind(null); + + $this->assertSame('empty', $form->getData()); + } + + public function testBindWithEmptyDataUsesEmptyDataOption() + { + $author = new Author(); + + $form = $this->factory->create('form', null, array( + 'empty_data' => $author, + )); + $form->add($this->factory->createNamed('form', 'firstName')); + + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame($author, $form->getData()); + $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 + */ + public function testSetDataThroughParamsWithZero() + { + $form = $this->factory->create('form', null, array('data' => 0)); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('0', $view->get('value')); + $this->assertSame('0', $form->getData()); + + $form = $this->factory->create('form', null, array('data' => '0')); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('0', $view->get('value')); + $this->assertSame('0', $form->getData()); + + $form = $this->factory->create('form', null, array('data' => '00000')); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('00000', $view->get('value')); + $this->assertSame('00000', $form->getData()); + } + + /** + * @expectedException Symfony\Component\Form\Exception\FormException + */ + public function testAttributesException() + { + $form = $this->factory->create('form', null, array('attr' => '')); + } + + public function testNameCanBeEmptyString() + { + $form = $this->factory->createNamed('form', ''); + + $this->assertEquals('', $form->getName()); + } public function testSubformDoesntCallSetters() { $author = new FormTest_AuthorWithoutRefSetter(new Author()); $builder = $this->factory->createBuilder('form'); $builder->add('reference', 'form'); - $builder->get('reference')->add('firstName', 'field'); + $builder->get('reference')->add('firstName', 'form'); $builder->setData($author); $form = $builder->getForm(); @@ -77,7 +454,7 @@ class FormTypeTest extends TypeTestCase $builder = $this->factory->createBuilder('form'); $builder->add('referenceCopy', 'form'); - $builder->get('referenceCopy')->add('firstName', 'field'); + $builder->get('referenceCopy')->add('firstName', 'form'); $builder->setData($author); $form = $builder->getForm(); @@ -99,7 +476,7 @@ class FormTypeTest extends TypeTestCase $builder = $this->factory->createBuilder('form'); $builder->add('referenceCopy', 'form', array('by_reference' => false)); - $builder->get('referenceCopy')->add('firstName', 'field'); + $builder->get('referenceCopy')->add('firstName', 'form'); $builder->setData($author); $form = $builder->getForm(); 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 df571ba9d8..6d9139633e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -21,7 +21,7 @@ class RepeatedTypeTest extends TypeTestCase parent::setUp(); $this->form = $this->factory->create('repeated', null, array( - 'type' => 'field', + 'type' => 'form', )); $this->form->setData(null); } @@ -37,7 +37,7 @@ class RepeatedTypeTest extends TypeTestCase public function testSetOptions() { $form = $this->factory->create('repeated', null, array( - 'type' => 'field', + 'type' => 'form', 'options' => array('label' => 'Global'), )); @@ -47,11 +47,11 @@ class RepeatedTypeTest extends TypeTestCase $this->assertTrue($form['second']->isRequired()); } - public function testSetOptionsPerField() + public function testSetOptionsPerChild() { $form = $this->factory->create('repeated', null, array( // the global required value cannot be overriden - 'type' => 'field', + 'type' => 'form', 'first_options' => array('label' => 'Test', 'required' => false), 'second_options' => array('label' => 'Test2') )); @@ -66,17 +66,17 @@ class RepeatedTypeTest extends TypeTestCase { $form = $this->factory->create('repeated', null, array( 'required' => false, - 'type' => 'field', + 'type' => 'form', )); $this->assertFalse($form['first']->isRequired()); $this->assertFalse($form['second']->isRequired()); } - public function testSetOptionsPerFieldAndOverwrite() + public function testSetOptionsPerChildAndOverwrite() { $form = $this->factory->create('repeated', null, array( - 'type' => 'field', + 'type' => 'form', 'options' => array('label' => 'Label'), 'second_options' => array('label' => 'Second label') )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php deleted file mode 100644 index 9b87717e5d..0000000000 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener; - -use Symfony\Component\Form\Event\DataEvent; -use Symfony\Component\Form\Extension\Csrf\EventListener\EnsureCsrfFieldListener; - -class EnsureCsrfFieldListenerTest extends \PHPUnit_Framework_TestCase -{ - private $form; - private $formFactory; - private $field; - private $event; - - protected function setUp() - { - if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { - $this->markTestSkipped('The "EventDispatcher" component is not available'); - } - - $this->formFactory = $this->getMock('Symfony\\Component\\Form\\FormFactoryInterface'); - $this->form = $this->getMock('Symfony\\Component\\Form\\Tests\\FormInterface'); - $this->field = $this->getMock('Symfony\\Component\\Form\\Tests\\FormInterface'); - $this->event = new DataEvent($this->form, array()); - } - - protected function tearDown() - { - $this->form = null; - $this->formFactory = null; - $this->field = null; - $this->event = null; - } - - public function testAddField() - { - $this->formFactory->expects($this->once()) - ->method('createNamed') - ->with('csrf', '_token', null, array()) - ->will($this->returnValue($this->field)); - $this->form->expects($this->once()) - ->method('add') - ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); - - $listener = new EnsureCsrfFieldListener($this->formFactory, '_token'); - $listener->ensureCsrfField($this->event); - } - - public function testIntention() - { - $this->formFactory->expects($this->once()) - ->method('createNamed') - ->with('csrf', '_token', null, array('intention' => 'something')) - ->will($this->returnValue($this->field)); - $this->form->expects($this->once()) - ->method('add') - ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); - - $listener = new EnsureCsrfFieldListener($this->formFactory, '_token', 'something'); - $listener->ensureCsrfField($this->event); - } - - public function testProvider() - { - $provider = $this->getMock('Symfony\\Component\\Form\\Extension\\Csrf\\CsrfProvider\\CsrfProviderInterface'); - - $this->formFactory->expects($this->once()) - ->method('createNamed') - ->with('csrf', '_token', null, array('csrf_provider' => $provider)) - ->will($this->returnValue($this->field)); - $this->form->expects($this->once()) - ->method('add') - ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); - - $listener = new EnsureCsrfFieldListener($this->formFactory, '_token', null, $provider); - $listener->ensureCsrfField($this->event); - } -} diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php deleted file mode 100644 index 4483e7f392..0000000000 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; - -class CsrfTypeTest extends TypeTestCase -{ - protected $provider; - - protected function setUp() - { - parent::setUp(); - - $this->provider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); - } - - protected function tearDown() - { - parent::tearDown(); - - $this->provider = null; - } - - protected function getNonRootForm() - { - $form = $this->getMock('Symfony\Component\Form\Tests\FormInterface'); - $form->expects($this->any()) - ->method('isRoot') - ->will($this->returnValue(false)); - - return $form; - } - - public function testGenerateCsrfToken() - { - $this->provider->expects($this->once()) - ->method('generateCsrfToken') - ->with('%INTENTION%') - ->will($this->returnValue('token')); - - $form = $this->factory->create('csrf', null, array( - 'csrf_provider' => $this->provider, - 'intention' => '%INTENTION%' - )); - - $this->assertEquals('token', $form->getData()); - } - - public function testValidateTokenOnBind() - { - $this->provider->expects($this->once()) - ->method('isCsrfTokenValid') - ->with('%INTENTION%', 'token') - ->will($this->returnValue(true)); - - $form = $this->factory->create('csrf', null, array( - 'csrf_provider' => $this->provider, - 'intention' => '%INTENTION%' - )); - $form->bind('token'); - - $this->assertEquals('token', $form->getData()); - } - - public function testDontValidateTokenIfParentIsNotRoot() - { - $this->provider->expects($this->never()) - ->method('isCsrfTokenValid'); - - $form = $this->factory->create('csrf', null, array( - 'csrf_provider' => $this->provider, - 'intention' => '%INTENTION%' - )); - $form->setParent($this->getNonRootForm()); - $form->bind('token'); - } - - public function testCsrfTokenIsRegeneratedIfValidationFails() - { - $this->provider->expects($this->at(0)) - ->method('generateCsrfToken') - ->with('%INTENTION%') - ->will($this->returnValue('token1')); - $this->provider->expects($this->at(1)) - ->method('isCsrfTokenValid') - ->with('%INTENTION%', 'invalid') - ->will($this->returnValue(false)); - - // The token is regenerated to avoid stalled tokens, for example when - // the session ID changed - $this->provider->expects($this->at(2)) - ->method('generateCsrfToken') - ->with('%INTENTION%') - ->will($this->returnValue('token2')); - - $form = $this->factory->create('csrf', null, array( - 'csrf_provider' => $this->provider, - 'intention' => '%INTENTION%' - )); - $form->bind('invalid'); - - $this->assertEquals('token2', $form->getData()); - } -} diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php index 20da376d79..9024002b52 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -11,43 +11,188 @@ namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; +use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + class FormTypeCsrfExtensionTest extends TypeTestCase { - public function testCsrfProtectionByDefault() - { - $form = $this->factory->create('form', null, array( - 'csrf_field_name' => 'csrf', - )); + protected $csrfProvider; - $this->assertTrue($form->has('csrf')); + protected function setUp() + { + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + + parent::setUp(); + } + + protected function tearDown() + { + $this->csrfProvider = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new CsrfExtension($this->csrfProvider), + )); + } + + public function testCsrfProtectionByDefaultIfRootAndChildren() + { + $view = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + )) + ->add($this->factory->createNamedBuilder('form', 'child')) + ->getForm() + ->createView(); + + $this->assertTrue($view->hasChild('csrf')); + } + + public function testNoCsrfProtectionByDefaultIfChildrenButNotRoot() + { + $view = $this->factory + ->createNamedBuilder('form', 'root') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + )) + ->add($this->factory->createNamedBuilder('form', 'child')) + ) + ->getForm() + ->get('form') + ->createView(); + + $this->assertFalse($view->hasChild('csrf')); + } + + public function testNoCsrfProtectionByDefaultIfRootButNoChildren() + { + $view = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + )) + ->getForm() + ->createView(); + + $this->assertFalse($view->hasChild('csrf')); } public function testCsrfProtectionCanBeDisabled() { - $form = $this->factory->create('form', null, array( - 'csrf_protection' => false, - )); - - $this->assertCount(0, $form); - } - - public function testCsrfTokenIsOnlyIncludedInRootView() - { - $view = - $this->factory->createBuilder('form', null, array( + $view = $this->factory + ->createBuilder('form', null, array( 'csrf_field_name' => 'csrf', + 'csrf_protection' => false, )) - ->add('notCsrf', 'text') - ->add( - $this->factory->createNamedBuilder('form', 'child', null, array( - 'csrf_field_name' => 'csrf', - )) - ->add('notCsrf', 'text') - ) + ->add($this->factory->createNamedBuilder('form', 'child')) ->getForm() ->createView(); - $this->assertEquals(array('csrf', 'notCsrf', 'child'), array_keys(iterator_to_array($view))); - $this->assertEquals(array('notCsrf'), array_keys(iterator_to_array($view['child']))); + $this->assertFalse($view->hasChild('csrf')); + } + + public function testGenerateCsrfToken() + { + $this->csrfProvider->expects($this->once()) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token')); + + $view = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%' + )) + ->add($this->factory->createNamedBuilder('form', 'child')) + ->getForm() + ->createView(); + + $this->assertEquals('token', $view->getChild('csrf')->get('value')); + } + + public function provideBoolean() + { + return array( + array(true), + array(false), + ); + } + + /** + * @dataProvider provideBoolean + */ + public function testValidateTokenOnBindIfRootAndChildren($valid) + { + $this->csrfProvider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue($valid)); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%' + )) + ->add($this->factory->createNamedBuilder('form', 'child')) + ->getForm(); + + $form->bind(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + + // Remove token from data + $this->assertSame(array('child' => 'foobar'), $form->getData()); + + // Validate accordingly + $this->assertSame($valid, $form->isValid()); + } + + public function testDontValidateTokenIfChildrenButNoRoot() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createNamedBuilder('form', 'root') + ->add($this->factory + ->createNamedBuilder('form', 'form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%' + )) + ->add($this->factory->createNamedBuilder('form', 'child')) + ) + ->getForm() + ->get('form'); + + $form->bind(array( + 'child' => 'foobar', + 'csrf' => 'token', + )); + } + + public function testDontValidateTokenIfRootButNoChildren() + { + $this->csrfProvider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory + ->createBuilder('form', null, array( + 'csrf_field_name' => 'csrf', + 'csrf_provider' => $this->csrfProvider, + 'intention' => '%INTENTION%' + )) + ->getForm(); + + $form->bind(array( + 'csrf' => 'token', + )); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php deleted file mode 100644 index 3b1ce91783..0000000000 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; - -use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase as BaseTestCase; -use Symfony\Component\Form\Extension\Csrf\CsrfExtension; - -abstract class TypeTestCase extends BaseTestCase -{ - protected $csrfProvider; - - protected function setUp() - { - $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); - - parent::setUp(); - } - - protected function tearDown() - { - $this->csrfProvider = null; - - parent::tearDown(); - } - - protected function getExtensions() - { - return array_merge(parent::getExtensions(), array( - new CsrfExtension($this->csrfProvider), - )); - } -} diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php index 0071b36fd7..187ce1a2b7 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php @@ -93,6 +93,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $builder = new FormBuilder($name, $this->factory, $this->dispatcher); $builder->setAttribute('property_path', new PropertyPath($propertyPath ?: $name)); $builder->setAttribute('error_mapping', array()); + $builder->setErrorBubbling(false); return $builder; } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php similarity index 79% rename from src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php rename to src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php index fdf011d66a..5cd0ea753b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -13,18 +13,18 @@ namespace Symfony\Component\Form\Tests\Extension\Validator\Type; use Symfony\Component\Form\FormInterface; -class FieldTypeValidatorExtensionTest extends TypeTestCase +class FormTypeValidatorExtensionTest extends TypeTestCase { public function testValidationGroupNullByDefault() { - $form = $this->factory->create('field'); + $form = $this->factory->create('form'); $this->assertNull($form->getAttribute('validation_groups')); } public function testValidationGroupsCanBeSetToString() { - $form = $this->factory->create('field', null, array( + $form = $this->factory->create('form', null, array( 'validation_groups' => 'group', )); @@ -33,7 +33,7 @@ class FieldTypeValidatorExtensionTest extends TypeTestCase public function testValidationGroupsCanBeSetToArray() { - $form = $this->factory->create('field', null, array( + $form = $this->factory->create('form', null, array( 'validation_groups' => array('group1', 'group2'), )); @@ -42,7 +42,7 @@ class FieldTypeValidatorExtensionTest extends TypeTestCase public function testValidationGroupsCanBeSetToCallback() { - $form = $this->factory->create('field', null, array( + $form = $this->factory->create('form', null, array( 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), )); @@ -51,7 +51,7 @@ class FieldTypeValidatorExtensionTest extends TypeTestCase public function testValidationGroupsCanBeSetToClosure() { - $form = $this->factory->create('field', null, array( + $form = $this->factory->create('form', null, array( 'validation_groups' => function(FormInterface $form){ return null; }, )); @@ -60,10 +60,10 @@ class FieldTypeValidatorExtensionTest extends TypeTestCase public function testBindValidatesData() { - $builder = $this->factory->createBuilder('field', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'validation_groups' => 'group', )); - $builder->add('firstName', 'field'); + $builder->add('firstName', 'form'); $form = $builder->getForm(); $this->validator->expects($this->once()) diff --git a/src/Symfony/Component/Form/Tests/FormBuilderTest.php b/src/Symfony/Component/Form/Tests/FormBuilderTest.php index f5276f322e..bbae7ee2dd 100644 --- a/src/Symfony/Component/Form/Tests/FormBuilderTest.php +++ b/src/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -71,7 +71,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase * Changing the name is not allowed, otherwise the name and property path * are not synchronized anymore * - * @see FieldType::buildForm + * @see FormType::buildForm */ public function testNoSetName() { diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 96ceea5532..49e53e8337 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -346,7 +346,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', $builder->getName()); } - public function testCreateBuilderForPropertyCreatesFieldWithHighestConfidence() + public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() { $this->guesser1->expects($this->once()) ->method('guessType') @@ -378,7 +378,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase $this->assertEquals('builderInstance', $builder); } - public function testCreateBuilderCreatesTextFieldIfNoGuess() + public function testCreateBuilderCreatesTextFormIfNoGuess() { $this->guesser1->expects($this->once()) ->method('guessType') @@ -541,7 +541,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase $factory->createNamedBuilder($type, "text", "value", array("unknown" => "opt")); } - public function testFieldTypeCreatesDefaultValueForEmptyDataOption() + public function testFormTypeCreatesDefaultValueForEmptyDataOption() { $factory = new FormFactory(array(new \Symfony\Component\Form\Extension\Core\CoreExtension())); diff --git a/src/Symfony/Component/Form/Tests/FormTest.php b/src/Symfony/Component/Form/Tests/FormTest.php index a5c20dcbcf..08ef363e4b 100644 --- a/src/Symfony/Component/Form/Tests/FormTest.php +++ b/src/Symfony/Component/Form/Tests/FormTest.php @@ -159,6 +159,35 @@ class FormTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array(), $parent->getErrors()); } + public function testErrorsBubbleUpIfNullAndChildren() + { + $error = new FormError('Error!'); + $parent = $this->form; + $form = $this->getBuilder() + ->setErrorBubbling(null) + ->add($this->getBuilder('child')) + ->getForm(); + + $form->setParent($parent); + $form->addError($error); + + $this->assertEquals(array(), $form->getErrors()); + $this->assertEquals(array($error), $parent->getErrors()); + } + + public function testErrorsDontBubbleUpIfNullAndNoChildren() + { + $error = new FormError('Error!'); + $parent = $this->form; + $form = $this->getBuilder()->setErrorBubbling(null)->getForm(); + + $form->setParent($parent); + $form->addError($error); + + $this->assertEquals(array($error), $form->getErrors()); + $this->assertEquals(array(), $parent->getErrors()); + } + public function testValidIfAllChildrenAreValid() { $this->form->add($this->getValidForm('firstName')); @@ -1026,7 +1055,7 @@ class FormTest extends \PHPUnit_Framework_TestCase /** * @dataProvider requestMethodProvider */ - public function testBindPostOrPutRequestWithSingleFieldForm($method) + public function testBindPostOrPutRequestWithSingleChildForm($method) { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { $this->markTestSkipped('The "HttpFoundation" component is not available'); @@ -1063,7 +1092,7 @@ class FormTest extends \PHPUnit_Framework_TestCase /** * @dataProvider requestMethodProvider */ - public function testBindPostOrPutRequestWithSingleFieldFormUploadedFile($method) + public function testBindPostOrPutRequestWithSingleChildFormUploadedFile($method) { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { $this->markTestSkipped('The "HttpFoundation" component is not available'); @@ -1247,7 +1276,7 @@ class FormTest extends \PHPUnit_Framework_TestCase public function testCreateViewAcceptsParent() { - $parent = new FormView(); + $parent = new FormView('form'); $form = $this->getBuilder()->getForm(); $view = $form->createView($parent);