diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md index 0af744d7c3..b949f534f4 100644 --- a/UPGRADE-2.1.md +++ b/UPGRADE-2.1.md @@ -576,6 +576,30 @@ {% endblock %} ```` + * Custom styling of individual rows of a collection form has been removed for + performance reasons. Instead, all rows now have the same block name, where + the word "entry" replaces the previous occurence of the row index. + + Before: + + ``` + {% block _author_tags_0_label %} + {# ... #} + {% endblock %} + + {% block _author_tags_1_label %} + {# ... #} + {% endblock %} + ``` + + After: + + ``` + {% block _author_tags_entry_label %} + {# ... #} + {% endblock %} + ``` + #### Other BC Breaks * The order of the first two arguments of the methods `createNamed` and diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 310198864b..f77965f689 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -245,7 +245,7 @@ class FormExtension extends \Twig_Extension $this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables); } else { $types = $view->getVar('types'); - $types[] = $custom; + $types[] = $view->getVar('full_block_name'); $typeIndex = count($types) - 1; $this->varStack[$rendering] = array( 'variables' => array_replace_recursive($view->getVars(), $variables), diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig index 6b9c0bbbb4..12fd7c66d8 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/custom_widgets.html.twig @@ -1,5 +1,16 @@ {% block _text_id_widget %} -
- {{ form_widget(form) }} -
+{% spaceless %} +
+ {{ form_widget(form) }} +
+{% endspaceless %} {% endblock _text_id_widget %} + +{% block _name_entry_label %} +{% spaceless %} + {% if label is empty %} + {% set label = name|humanize %} + {% endif %} + +{% endspaceless %} +{% endblock _name_entry_label %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php index fd8c3deeab..a2b5afc1d5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @@ -246,7 +246,7 @@ class FormHelper extends Helper $variables = array_replace_recursive($this->varStack[$rendering]['variables'], $variables); } else { $types = $view->getVar('types'); - $types[] = $custom; + $types[] = $view->getVar('full_block_name'); $typeIndex = count($types) - 1; $variables = array_replace_recursive($view->getVars(), $variables); $this->varStack[$rendering]['types'] = $types; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php new file mode 100644 index 0000000000..0efecf0c72 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php @@ -0,0 +1,2 @@ +humanize($name); } ?> + diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index cbc37f0766..88a19bc07e 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -170,3 +170,5 @@ CHANGELOG * `getExtensions` * `setExtensions` * ChoiceType now caches its created choice lists to improve performance + * [BC BREAK] Rows of a collection field cannot be themed individually anymore. All rows in the collection + field now have the same block names, which contains "entry" where it previously contained the row index. diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php index 6b0d1ec1ee..cc88d3a686 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -16,6 +16,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormViewInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class CollectionType extends AbstractType @@ -73,6 +74,12 @@ class CollectionType extends AbstractType */ public function setDefaultOptions(OptionsResolverInterface $resolver) { + $optionsFilter = function (Options $options, $value) { + $value['block_name'] = 'entry'; + + return $value; + }; + $resolver->setDefaults(array( 'allow_add' => false, 'allow_delete' => false, @@ -81,6 +88,10 @@ class CollectionType extends AbstractType 'type' => 'text', 'options' => array(), )); + + $resolver->setFilters(array( + 'options' => $optionsFilter, + )); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 0ef0def253..0f7f9a2332 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -59,6 +59,7 @@ class FormType extends AbstractType public function buildView(FormViewInterface $view, FormInterface $form, array $options) { $name = $form->getName(); + $blockName = $options['block_name'] ?: $form->getName(); $readOnly = $options['read_only']; $translationDomain = $options['translation_domain']; @@ -67,23 +68,30 @@ class FormType extends AbstractType throw new FormException('Form node with empty name can be used only as root form node.'); } - if ('' !== ($parentFullName = $view->getParent()->getVar('full_name'))) { - $id = sprintf('%s_%s', $view->getParent()->getVar('id'), $name); + $parentView = $view->getParent(); + + if ('' !== ($parentFullName = $parentView->getVar('full_name'))) { + $id = sprintf('%s_%s', $parentView->getVar('id'), $name); $fullName = sprintf('%s[%s]', $parentFullName, $name); + $fullBlockName = sprintf('%s_%s', $parentView->getVar('full_block_name'), $blockName); } else { $id = $name; $fullName = $name; + $fullBlockName = '_' . $blockName; } - // Complex fields are read-only if themselves or their parent is. - $readOnly = $readOnly || $view->getParent()->getVar('read_only'); + // Complex fields are read-only if they themselves or their parents are. + if (!$readOnly) { + $readOnly = $parentView->getVar('read_only'); + } if (!$translationDomain) { - $translationDomain = $view->getParent()->getVar('translation_domain'); + $translationDomain = $parentView->getVar('translation_domain'); } } else { $id = $name; $fullName = $name; + $fullBlockName = '_' . $blockName; // Strip leading underscores and digits. These are allowed in // form names, but not in HTML4 ID attributes. @@ -105,6 +113,7 @@ class FormType extends AbstractType 'id' => $id, 'name' => $name, 'full_name' => $fullName, + 'full_block_name' => $fullBlockName, 'read_only' => $readOnly, 'errors' => $form->getErrors(), 'valid' => $form->isBound() ? $form->isValid() : true, @@ -177,7 +186,14 @@ class FormType extends AbstractType return false !== $options['property_path']; }; + // If data is given, the form is locked to that data + // (independent of its value) + $resolver->setOptional(array( + 'data', + )); + $resolver->setDefaults(array( + 'block_name' => null, 'data_class' => $dataClass, 'empty_data' => $emptyData, 'trim' => true, @@ -198,12 +214,6 @@ class FormType extends AbstractType 'translation_domain' => null, )); - // If data is given, the form is locked to that data - // (independent of its value) - $resolver->setOptional(array( - 'data', - )); - $resolver->setAllowedTypes(array( 'attr' => 'array', 'label_attr' => 'array', diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index d4ae48ee4a..01c8d96968 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -557,6 +557,27 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest ] /following-sibling::input[@type="hidden"] ] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div[./label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::div[./label[.="Custom label: [trans]2[/trans]"]] + ] ' ); } diff --git a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php index 801f6a8ae6..1436d835d0 100644 --- a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -338,6 +338,27 @@ abstract class AbstractTableLayoutTest extends AbstractLayoutTest ] ] [count(.//input)=3] +' + ); + } + + /** + * The block "_name_child_label" should be overridden in the theme of the + * implemented driver. + */ + public function testCollectionRowWithCustomBlock() + { + $collection = array('one', 'two', 'three'); + $form = $this->factory->createNamedBuilder('name', 'collection', $collection) + ->getForm(); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/table + [ + ./tr[./td/label[.="Custom label: [trans]0[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]1[/trans]"]] + /following-sibling::tr[./td/label[.="Custom label: [trans]2[/trans]"]] + ] ' ); }