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:
commit
97a745e973
@ -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;
|
||||
|
@ -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 %}
|
||||
|
@ -1 +0,0 @@
|
||||
<label <?php echo $view['form']->attributes() ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>
|
@ -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>
|
||||
|
@ -0,0 +1 @@
|
||||
<?php echo $view['form']->label($form['file']) ?>
|
@ -0,0 +1 @@
|
||||
<label <?php echo $view['form']->attributes(false) ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>
|
@ -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 ?>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"]
|
||||
]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user