Merge remote branch 'vicb/form-rendering-fix'

* vicb/form-rendering-fix:
  [Form] Fix accessibility for file inputs
  [FrameworkBundle] Fix the FormHelper phpDoc
  [FrameworkBundle][Form] Add some phpDoc for the FormHelper class
  [FrameworkBundle][Form] Fix label rendering
  [FrameworkBundle][Form] Fix rendering search inputs in PHP
  [Form] FormType labels should never have a for attribute
  [Form] Never render a view again
This commit is contained in:
Fabien Potencier 2011-06-07 19:46:20 +02:00
commit 97a745e973
9 changed files with 189 additions and 33 deletions

View File

@ -197,6 +197,12 @@ class FormExtension extends \Twig_Extension
*/
protected function render(FormView $view, $section, array $variables = array())
{
$mainTemplate = in_array($section, array('widget', 'row'));
if ($mainTemplate && $view->isRendered()) {
return '';
}
$templates = $this->getTemplates($view);
$blocks = $view->get('types');
array_unshift($blocks, '_'.$view->get('id'));
@ -205,9 +211,6 @@ class FormExtension extends \Twig_Extension
$block = $block.'_'.$section;
if (isset($templates[$block])) {
if ('widget' === $section || 'row' === $section) {
$view->setRendered();
}
$this->varStack[$view] = array_replace(
$view->all(),
@ -217,6 +220,10 @@ class FormExtension extends \Twig_Extension
$html = $templates[$block]->renderBlock($block, $this->varStack[$view]);
if ($mainTemplate) {
$view->setRendered();
}
unset($this->varStack[$view]);
return $html;

View File

@ -10,11 +10,17 @@
{% endspaceless %}
{% endblock field_label %}
{% block collection_label %}
{% block form_label %}
{% spaceless %}
<label {% for attrname,attrvalue in attr %} {{attrname}}="{{attrvalue}}"{% endfor %}>{{ label|trans }}</label>
{% endspaceless %}
{% endblock collection_label %}
{% endblock form_label %}
{% block file_label %}
{% spaceless %}
{{ form_label(form.file) }}
{% endspaceless %}
{% endblock file_label %}
{% block field_errors %}
{% spaceless %}

View File

@ -1 +0,0 @@
<label <?php echo $view['form']->attributes() ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>

View File

@ -1 +1 @@
<label for="<?php echo $view->escape($id) ?>" <?php echo $view['form']->attributes() ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>
<label for="<?php echo $view->escape($id) ?>" <?php echo $view['form']->attributes(false) ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>

View File

@ -0,0 +1 @@
<?php echo $view['form']->label($form['file']) ?>

View File

@ -0,0 +1 @@
<label <?php echo $view['form']->attributes(false) ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>

View File

@ -1,6 +1,6 @@
<input type="search"
<?php echo $view['form']->attributes() ?>
name="<?php echo $view->escape($name) ?>"
name="<?php echo $view->escape($full_name) ?>"
value="<?php echo $view->escape($value) ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>

View File

@ -38,7 +38,14 @@ class FormHelper extends Helper
$this->varStack = new \SplObjectStorage();
}
public function attributes()
/**
* Renders the attributes for the current view.
*
* @param Boolean $includeId Whether to render the id attribute
*
* @return string The HTML markup
*/
public function attributes($includeId = true)
{
$html = '';
$attr = array();
@ -51,7 +58,7 @@ class FormHelper extends Helper
$attr = $vars['attr'];
}
if (isset($vars['id'])) {
if (true === $includeId && isset($vars['id'])) {
$attr['id'] = $vars['id'];
}
}
@ -63,11 +70,40 @@ class FormHelper extends Helper
return $html;
}
/**
* Renders the HTML enctype in the form tag, if necessary.
*
* Example usage templates:
*
* <form action="..." method="post" <?php echo $view['form']->enctype() ?>>
*
* @param FormView $view The view for which to render the encoding type
*
* @return string The html markup
*/
public function enctype(FormView $view)
{
return $this->renderSection($view, 'enctype');
}
/**
* Renders the HTML for a given view.
*
* Example usage:
*
* <?php echo view['form']->widget() ?>
*
* You can pass options during the call:
*
* <?php echo view['form']->widget(array('attr' => array('class' => 'foo'))) ?>
*
* <?php echo view['form']->widget(array('separator' => '+++++)) ?>
*
* @param FormView $view The view for which to render the widget
* @param array $variables Additional variables passed to the template
*
* @return string The html markup
*/
public function widget(FormView $view, array $variables = array())
{
return trim($this->renderSection($view, 'widget', $variables));
@ -76,16 +112,25 @@ class FormHelper extends Helper
/**
* Renders the entire form field "row".
*
* @param FormView $view
* @param array $variables
* @param FormView $view The view for which to render the row
* @param array $variables Additional variables passed to the template
*
* @return string
* @return string The html markup
*/
public function row(FormView $view, array $variables = array())
{
return $this->renderSection($view, 'row', $variables);
}
/**
* Renders the label of the given view.
*
* @param FormView $view The view for which to render the label
* @param string $label The label
* @param array $variables Additional variables passed to the template
*
* @return string The html markup
*/
public function label(FormView $view, $label = null, array $variables = array())
{
if ($label !== null) {
@ -95,18 +140,56 @@ class FormHelper extends Helper
return $this->renderSection($view, 'label', $variables);
}
/**
* Renders the errors of the given view.
*
* @param FormView $view The view to render the errors for
*
* @return string The html markup
*/
public function errors(FormView $view)
{
return $this->renderSection($view, 'errors');
}
/**
* Renders views which have not already been rendered.
*
* @param FormView $view The parent view
* @param array $variables An array of variables
*
* @return string The html markup
*/
public function rest(FormView $view, array $variables = array())
{
return $this->renderSection($view, 'rest', $variables);
}
/**
* Renders a template.
*
* 1. This function first looks for a block named "_<view id>_<section>",
* 2. if such a block is not found the function will look for a block named
* "<type name>_<section>",
* 3. the type name is recursively replaced by the parent type name until a
* corresponding block is found
*
* @param FormView $view The form view
* @param string $section The section to render (i.e. 'row', 'widget', 'label', ...)
* @param array $variables Additional variables
*
* @return string The html markup
*
* @throws FormException if no template block exists to render the given section of the view
*/
protected function renderSection(FormView $view, $section, array $variables = array())
{
$mainTemplate = in_array($section, array('row', 'widget'));
if ($mainTemplate && $view->isRendered()) {
return '';
}
$template = null;
$blocks = $view->get('types');
array_unshift($blocks, '_'.$view->get('id'));
@ -124,11 +207,13 @@ class FormHelper extends Helper
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
}
if ('widget' === $section || 'row' === $section) {
$html = $this->render($view, $template, $variables);
if ($mainTemplate) {
$view->setRendered();
}
return $this->render($view, $template, $variables);
return $html;
}
public function render(FormView $view, $template, array $variables = array())
@ -149,13 +234,20 @@ class FormHelper extends Helper
return $html;
}
protected function lookupTemplate($templateName)
/**
* Returns the name of the template to use to render the block
*
* @param string $blockName The name of the block
*
* @return string|Boolean The template logical name or false when no template is found
*/
protected function lookupTemplate($blockName)
{
if (isset(self::$cache[$templateName])) {
return self::$cache[$templateName];
if (isset(self::$cache[$blockName])) {
return self::$cache[$blockName];
}
$template = $templateName.'.html.php';
$template = $blockName.'.html.php';
/*
if ($this->templateDir) {
$template = $this->templateDir.':'.$template;
@ -166,7 +258,7 @@ class FormHelper extends Helper
$template = false;
}
self::$cache[$templateName] = $template;
self::$cache[$blockName] = $template;
return $template;
}

View File

@ -144,14 +144,11 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
$html = $this->renderRest($view);
$this->assertMatchesXpath($html,
'/input
[@type="hidden"]
[@id="parent__token"]
'/input[@type="hidden"][@id="parent__token"]
/following-sibling::div
[
./label[@for="parent_child1"]
/following-sibling::div
[@id="parent_child1"]
./label[not(@for)]
/following-sibling::div[@id="parent_child1"]
[
./div
[
@ -160,23 +157,21 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
]
]
]
/following-sibling::div
[
./label[@for="parent_child2"]
/following-sibling::div
[@id="parent_child2"]
./label[not(@for)]
/following-sibling::div[@id="parent_child2"]
[
./div
[
./label[@for="parent_child2_field1"]
/following-sibling::input[@id="parent_child2_field1"]
]
/following-sibling::div
[
./label[@for="parent_child2_field2"]
]
]
]
[count(//label)=4]
[count(//input[@type="text"])=2]
'
);
}
@ -373,4 +368,59 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
'
);
}
public function testSearchInputName()
{
$form = $this->factory->createNamedBuilder('form', 'full')
->add('name', 'search')
->getForm();
$this->assertWidgetMatchesXpath($form->createView(), array(),
'/div
[
./input[@type="hidden"][@id="full__token"]
/following-sibling::div
[
./label[@for="full_name"]
/following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"]
]
]
[count(//input)=2]
'
);
}
public function testLabelHasNoId()
{
$form = $this->factory->createNamed('text', 'name');
$html = $this->renderRow($form->createView());
$this->assertMatchesXpath($html,
'/div
[
./label[@for="name"][not(@id)]
/following-sibling::input[@id="name"]
]
'
);
}
public function testFileLabelAccessibility()
{
$form = $this->factory->createNamed('file', 'name');
$html = $this->renderRow($form->createView());
$this->assertMatchesXpath($html,
'/div
[
./label[@for="name_file"]
/following-sibling::div[@id="name"]
[
./input[@id="name_file"][@type="file"]
]
]
'
);
}
}