[Form] Improved the renderer implementation, added concepts of plugins and themes

This commit is contained in:
Bernhard Schussek 2011-02-18 20:17:44 +01:00
parent 6cc0a58edc
commit ed68fd66a9
14 changed files with 440 additions and 423 deletions

View File

@ -1,23 +1,12 @@
{% block field_row %}
{% block row %}
{% spaceless %}
<div>
{# TODO: would be nice to rename this variable to "field" #}
{{ form_label(child) }}
{{ form_errors(child) }}
{{ form_field(child) }}
{{ field.renderer.label }}
{{ field.renderer.errors }}
{{ field.renderer.widget }}
</div>
{% endspaceless %}
{% endblock field_row %}
{% block form %}
{% spaceless %}
{{ form_errors(field) }}
{% for child in field.visibleFields %}
{{ block('field_row') }}
{% endfor %}
{{ form_hidden(field) }}
{% endspaceless %}
{% endblock form %}
{% endblock row %}
{% block errors %}
{% spaceless %}
@ -34,7 +23,7 @@
{% block hidden %}
{% spaceless %}
{% for child in field.allHiddenFields %}
{{ form_field(child) }}
{{ child.renderer.widget }}
{% endfor %}
{% endspaceless %}
{% endblock hidden %}
@ -60,35 +49,49 @@
{% endspaceless %}
{% endblock field_attributes %}
{% block text_field %}
{% block form__widget %}
{% spaceless %}
{{ field.renderer.errors }}
{% for child in field.visibleFields %}
{{ child.renderer.row }}
{% endfor %}
{{ field.renderer.hidden }}
{% endspaceless %}
{% endblock form__widget %}
{% block collection__widget %}
{{ block('form__widget') }}
{% endblock collection__widget %}
{% block text__widget %}
{% spaceless %}
{% if attr.type is defined and attr.type != "text" %}
<input {{ block('field_attributes') }} value="{{ field.displayedData }}" />
{% else %}
{% set attr = attr|merge({ 'maxlength': attr.maxlength|default(field.maxlength) }) %}
{% set attr = attr|merge({ 'maxlength': attr.maxlength|default(max_length) }) %}
<input type="text" {{ block('field_attributes') }} value="{{ field.displayedData }}" />
{% endif %}
{% endspaceless %}
{% endblock text_field %}
{% endblock text__widget %}
{% block password_field %}
{% block password__widget %}
{% spaceless %}
{% set attr = attr|merge({ 'maxlength': attr.maxlength|default(field.maxlength) }) %}
<input type="password" {{ block('field_attributes') }} value="{{ field.displayedData }}" />
{% endspaceless %}
{% endblock password_field %}
{% endblock password__widget %}
{% block hidden_field %}
{% block hidden__widget %}
{% spaceless %}
<input type="hidden" id="{{ field.id }}" name="{{ field.name }}"{% if field.disabled %} disabled="disabled"{% endif %} value="{{ field.displayedData }}" />
{% endspaceless %}
{% endblock hidden_field %}
{% endblock hidden__widget %}
{% block textarea_field %}
{% block textarea__widget %}
{% spaceless %}
<textarea {{ block('field_attributes') }}>{{ field.displayedData }}</textarea>
{% endspaceless %}
{% endblock textarea_field %}
{% endblock textarea__widget %}
{% block options %}
{% spaceless %}
@ -106,11 +109,11 @@
{% endspaceless %}
{% endblock options %}
{% block choice_field %}
{% block choice__widget %}
{% spaceless %}
{% if field.isExpanded %}
{% for choice, child in field %}
{{ form_field(child) }}
{{ child.renderer.widget }}
<label for="{{ child.id }}">{{ field.label(choice) }}</label>
{% endfor %}
{% else %}
@ -125,79 +128,79 @@
</select>
{% endif %}
{% endspaceless %}
{% endblock choice_field %}
{% endblock choice__widget %}
{% block checkbox_field %}
{% block checkbox__widget %}
{% spaceless %}
<input type="checkbox" {{ block('field_attributes') }}{% if field.hasValue %} value="{{ field.value }}"{% endif %}{% if field.ischecked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock checkbox_field %}
{% endblock checkbox__widget %}
{% block radio_field %}
{% block radio__widget %}
{% spaceless %}
<input type="radio" {{ block('field_attributes') }}{% if field.hasValue %} value="{{ field.value }}"{% endif %}{% if field.ischecked %} checked="checked"{% endif %} />
{% endspaceless %}
{% endblock radio_field %}
{% endblock radio__widget %}
{% block date_time_field %}
{% block date_time__widget %}
{% spaceless %}
{{ form_errors(field.date) }}
{{ form_errors(field.time) }}
{{ form_field(field.date) }}
{{ form_field(field.time) }}
{{ field.date.renderer.errors }}
{{ field.time.renderer.errors }}
{{ field.date.renderer.widget }}
{{ field.time.renderer.widget }}
{% endspaceless %}
{% endblock date_time_field %}
{% endblock date_time__widget %}
{% block date_field %}
{% block date__widget %}
{% spaceless %}
{% if field.isField %}
{{ block('text_field') }}
{{ block('text__widget') }}
{% else %}
{{ field.pattern|replace({ '{{ year }}': form_field(field.year), '{{ month }}': form_field(field.month), '{{ day }}': form_field(field.day) })|raw }}
{{ field.pattern|replace({ '{{ year }}': field.year.renderer.widget, '{{ month }}': field.month.renderer.widget, '{{ day }}': field.day.renderer.widget })|raw }}
{% endif %}
{% endspaceless %}
{% endblock date_field %}
{% endblock date__widget %}
{% block time_field %}
{% block time__widget %}
{% spaceless %}
{% if field.isField %}{% set attr = attr|merge({ 'size': 1 }) %}{% endif %}
{{ form_field(field.hour, attr) }}:{{ form_field(field.minute, attr) }}{% if field.isWithSeconds %}:{{ form_field(field.second, attr) }}{% endif %}
{{ field.hour.renderer.widget(attr) }}:{{ field.minute.renderer.widget(attr) }}{% if field.isWithSeconds %}:{{ field.second.renderer.widget(attr) }}{% endif %}
{% endspaceless %}
{% endblock time_field %}
{% endblock time__widget %}
{% block number_field %}
{% block number__widget %}
{% spaceless %}
{% set attr = attr|merge({ 'type': 'number' }) %}
{{ block('text_field') }}
{{ block('text__widget') }}
{% endspaceless %}
{% endblock number_field %}
{% endblock number__widget %}
{% block money_field %}
{% block money__widget %}
{% spaceless %}
{{ field.pattern|replace({ '{{ widget }}': block('number_field') })|raw }}
{{ money_pattern|replace({ '{{ widget }}': block('text__widget') })|raw }}
{% endspaceless %}
{% endblock money_field %}
{% endblock money__widget %}
{% block url_field %}
{% block url__widget %}
{% spaceless %}
{% set attr = attr|merge({ 'type': 'url' }) %}
{{ block('text_field') }}
{{ block('text__widget') }}
{% endspaceless %}
{% endblock url_field %}
{% endblock url__widget %}
{% block percent_field %}
{% block percent__widget %}
{% spaceless %}
{{ block('text_field') }} %
{{ block('text__widget') }} %
{% endspaceless %}
{% endblock percent_field %}
{% endblock percent__widget %}
{% block file_field %}
{% block file__widget %}
{% spaceless %}
{% set group = field %}
{% set field = group.file %}
<input type="file" {{ block('field_attributes') }} />
{{ form_field(group.token) }}
{{ form_field(group.original_name) }}
{{ group.token.renderer.widget }}
{{ group.original_name.renderer.widget }}
{% endspaceless %}
{% endblock file_field %}
{% endblock file__widget %}

View File

@ -45,7 +45,6 @@ class MoneyField extends NumberField
*/
protected function configure()
{
$this->addRequiredOption('currency');
$this->addOption('precision', 2);
$this->addOption('divisor', 1);
@ -57,44 +56,4 @@ class MoneyField extends NumberField
'divisor' => $this->getOption('divisor'),
)));
}
/**
* Returns the pattern for this locale
*
* The pattern contains the placeholder "{{ widget }}" where the HTML tag should
* be inserted
*/
public function getPattern()
{
if (!$this->getOption('currency')) {
return '{{ widget }}';
}
if (!isset(self::$patterns[\Locale::getDefault()])) {
self::$patterns[\Locale::getDefault()] = array();
}
if (!isset(self::$patterns[\Locale::getDefault()][$this->getOption('currency')])) {
$format = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::CURRENCY);
$pattern = $format->formatCurrency('123', $this->getOption('currency'));
// the spacings between currency symbol and number are ignored, because
// a single space leads to better readability in combination with input
// fields
// the regex also considers non-break spaces (0xC2 or 0xA0 in UTF-8)
preg_match('/^([^\s\xc2\xa0]*)[\s\xc2\xa0]*123[,.]00[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/', $pattern, $matches);
if (!empty($matches[1])) {
self::$patterns[\Locale::getDefault()] = $matches[1].' {{ widget }}';
} else if (!empty($matches[2])) {
self::$patterns[\Locale::getDefault()] = '{{ widget }} '.$matches[2];
} else {
self::$patterns[\Locale::getDefault()] = '{{ widget }}';
}
}
return self::$patterns[\Locale::getDefault()];
}
}

View File

@ -0,0 +1,115 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer;
use Symfony\Component\Form\FieldInterface;
use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
use Symfony\Component\Form\Renderer\Plugin\PluginInterface;
class DefaultRenderer implements RendererInterface
{
private $field;
private $template;
private $theme;
private $parameters = array();
private $plugins = array();
private $initialized = false;
public function __construct(ThemeInterface $theme, $template)
{
$this->theme = $theme;
$this->template = $template;
}
private function setUpPlugins()
{
if (!$this->initialized) {
$this->initialized = true;
foreach ($this->plugins as $plugin) {
$plugin->setUp($this);
}
}
}
public function setTheme(ThemeInterface $theme)
{
$this->theme = $theme;
}
public function getTheme()
{
return $this->theme;
}
public function addPlugin(PluginInterface $plugin)
{
$this->initialized = false;
$this->plugins[] = $plugin;
}
public function setParameter($name, $value)
{
$this->parameters[$name] = $value;
}
public function getWidget(array $attributes = array(), array $parameters = array())
{
return $this->render('widget', $attributes, $parameters);
}
public function getErrors(array $attributes = array(), array $parameters = array())
{
return $this->render('errors', $attributes, $parameters);
}
public function getRow(array $attributes = array(), array $parameters = array())
{
return $this->render('row', $attributes, $parameters);
}
public function getHidden(array $attributes = array(), array $parameters = array())
{
return $this->render('hidden', $attributes, $parameters);
}
/**
* Renders the label of the given field
*
* @param FieldInterface $field The field to render the label for
* @param array $params Additional variables passed to the template
*/
public function getLabel($label = null, array $attributes = array(), array $parameters = array())
{
if (null !== $label) {
$parameters['label'] = $label;
}
return $this->render('label', $attributes, $parameters);
}
protected function render($block, array $attributes = array(), array $parameters = array())
{
$this->setUpPlugins();
return $this->theme->render($this->template, $block, array_replace(
array('attr' => $attributes),
$this->parameters,
$parameters
));
}
}

View File

@ -1,120 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Engine;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FieldInterface;
class TwigEngine implements EngineInterface
{
protected $environment;
protected $resources;
public function __construct(\Twig_Environment $environment, array $resources = array())
{
$this->environment = $environment;
$this->resources = $resources;
}
public function render(FieldInterface $field, $name, array $arguments, array $resources = null)
{
if ('field' === $name) {
list($name, $template) = $this->getWidget($field, $resources);
} else {
$template = $this->getTemplate($field, $name);
}
return $template->renderBlock($name, $arguments);
}
/**
* @param FieldInterface $field The field to get the widget for
* @param array $resources An array of template resources
* @return array
*/
protected function getWidget(FieldInterface $field, array $resources = null)
{
$class = get_class($field);
$templates = $this->getTemplates($field, $resources);
// find a template for the given class or one of its parents
do {
$parts = explode('\\', $class);
$c = array_pop($parts);
// convert the base class name (e.g. TextareaField) to underscores (e.g. textarea_field)
$underscore = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($c, '_', '.')));
if (isset($templates[$underscore])) {
return array($underscore, $templates[$underscore]);
}
} while (false !== $class = get_parent_class($class));
throw new \RuntimeException(sprintf('Unable to render the "%s" field.', $field->getKey()));
}
protected function getTemplate(FieldInterface $field, $name, array $resources = null)
{
$templates = $this->getTemplates($field, $resources);
return $templates[$name];
}
protected function getTemplates(FieldInterface $field, array $resources = null)
{
// templates are looked for in the following resources:
// * resources provided directly into the function call
// * resources from the themes (and its parents)
// * default resources
// defaults
$all = $this->resources;
// themes
$parent = $field;
do {
if (isset($this->themes[$parent])) {
$all = array_merge($all, $this->themes[$parent]);
}
} while ($parent = $parent->getParent());
// local
$all = array_merge($all, null !== $resources ? (array) $resources : array());
$templates = array();
foreach ($all as $resource) {
if (!$resource instanceof \Twig_Template) {
$resource = $this->environment->loadTemplate($resource);
}
$blocks = array();
foreach ($this->getBlockNames($resource) as $name) {
$blocks[$name] = $resource;
}
$templates = array_replace($templates, $blocks);
}
return $templates;
}
protected function getBlockNames($resource)
{
$names = $resource->getBlockNames();
$parent = $resource;
while (false !== $parent = $parent->getParent(array())) {
$names = array_merge($names, $parent->getBlockNames());
}
return array_unique($names);
}
}

View File

@ -1,146 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer;
use Symfony\Component\Form\FieldInterface;
use Symfony\Component\Form\Renderer\Engine\EngineInterface;
class FieldRenderer implements RendererInterface
{
private $field;
private $engine;
public function __construct(FieldInterface $field, EngineInterface $engine)
{
$this->field = $field;
$this->engine = $engine;
}
protected function getField()
{
return $this->field;
}
protected function getEngine()
{
return $this->engine;
}
public function __toString()
{
return $this->widget();
}
/**
* Renders the HTML enctype in the form tag, if necessary
*
* Example usage in Twig templates:
*
* <form action="..." method="post" {{ form.render.enctype }}>
*
* @param Form $form The form for which to render the encoding type
*/
public function enctype()
{
return $this->field->isMultipart() ? 'enctype="multipart/form-data"' : '';
}
/**
* Renders a field row.
*
* @param FieldInterface $field The field to render as a row
*/
public function row()
{
return $this->engine->render($this->field, 'field_row', array(
'child' => $this->field,
));
}
/**
* Renders the HTML for an individual form field
*
* Example usage in Twig:
*
* {{ form_field(field) }}
*
* You can pass attributes element during the call:
*
* {{ form_field(field, {'class': 'foo'}) }}
*
* Some fields also accept additional variables as parameters:
*
* {{ form_field(field, {}, {'separator': '+++++'}) }}
*
* @param FieldInterface $field The field to render
* @param array $attributes HTML attributes passed to the template
* @param array $parameters Additional variables passed to the template
* @param array|string $resources A resource or array of resources
*/
public function widget(array $attributes = array(), array $parameters = array(), $resources = null)
{
if (null !== $resources && !is_array($resources)) {
$resources = array($resources);
}
return $this->engine->render($this->field, 'field', array(
'field' => $this->field,
'attr' => $attributes,
'params' => $parameters,
), $resources);
}
/**
* Renders all hidden fields of the given field group
*
* @param FormInterface $group The field group
* @param array $params Additional variables passed to the
* template
*/
public function hidden(array $parameters = array())
{
return $this->engine->render($this->field, 'hidden', array(
'field' => $this->field,
'params' => $parameters,
));
}
/**
* Renders the errors of the given field
*
* @param FieldInterface $field The field to render the errors for
* @param array $params Additional variables passed to the template
*/
public function errors(array $parameters = array())
{
return $this->engine->render($this->field, 'errors', array(
'field' => $this->field,
'params' => $parameters,
));
}
/**
* Renders the label of the given field
*
* @param FieldInterface $field The field to render the label for
* @param array $params Additional variables passed to the template
*/
public function label($label = null, array $parameters = array())
{
return $this->render($this->field, 'label', array(
'field' => $this->field,
'params' => $parameters,
'label' => null !== $label ? $label : ucfirst(strtolower(str_replace('_', ' ', $this->field->getKey()))),
));
}
}

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\Engine\EngineInterface;
class FormRenderer extends FieldRenderer
{
public function __construct(FormInterface $form, EngineInterface $engine)
{
parent::__construct($form, $engine);
}
/**
* Renders the HTML enctype in the form tag, if necessary
*
* Example usage in Twig templates:
*
* <form action="..." method="post" {{ form.render.enctype }}>
*
* @param Form $form The form for which to render the encoding type
*/
public function enctype()
{
return $this->getField()->isMultipart() ? 'enctype="multipart/form-data"' : '';
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license infieldation, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Plugin;
use Symfony\Component\Form\Renderer\RendererInterface;
use Symfony\Component\Form\FieldInterface;
class EnctypePlugin implements PluginInterface
{
private $field;
public function __construct(FieldInterface $field)
{
$this->field = $field;
}
/**
* Renders the HTML enctype in the field tag, if necessary
*
* Example usage in Twig templates:
*
* <field action="..." method="post" {{ field.render.enctype }}>
*
* @param Form $field The field for which to render the encoding type
*/
public function setUp(RendererInterface $renderer)
{
$renderer->setParameter('enctype', $this->field->isMultipart() ? 'enctype="multipart/form-data"' : '');
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license infieldation, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Plugin;
use Symfony\Component\Form\Renderer\RendererInterface;
use Symfony\Component\Form\FieldInterface;
class MaxLengthPlugin implements PluginInterface
{
private $maxLength;
public function __construct($maxLength)
{
$this->maxLength = $maxLength;
}
/**
* Renders the HTML enctype in the field tag, if necessary
*
* Example usage in Twig templates:
*
* <field action="..." method="post" {{ field.render.enctype }}>
*
* @param Form $field The field for which to render the encoding type
*/
public function setUp(RendererInterface $renderer)
{
$renderer->setParameter('max_length', $this->maxLength);
}
}

View File

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Plugin;
use Symfony\Component\Form\Renderer\RendererInterface;
class MoneyPatternPlugin implements PluginInterface
{
private static $patterns = array();
private $currency = 'EUR';
public function __construct($currency)
{
$this->currency = $currency;
}
public function setUp(RendererInterface $renderer)
{
$renderer->setParameter('money_pattern', self::getPattern($this->currency));
}
/**
* Returns the pattern for this locale
*
* The pattern contains the placeholder "{{ widget }}" where the HTML tag should
* be inserted
*/
private static function getPattern($currency)
{
if (!$currency) {
return '{{ widget }}';
}
if (!isset(self::$patterns[\Locale::getDefault()])) {
self::$patterns[\Locale::getDefault()] = array();
}
if (!isset(self::$patterns[\Locale::getDefault()][$currency])) {
$format = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::CURRENCY);
$pattern = $format->formatCurrency('123', $currency);
// the spacings between currency symbol and number are ignored, because
// a single space leads to better readability in combination with input
// fields
// the regex also considers non-break spaces (0xC2 or 0xA0 in UTF-8)
preg_match('/^([^\s\xc2\xa0]*)[\s\xc2\xa0]*123[,.]00[\s\xc2\xa0]*([^\s\xc2\xa0]*)$/', $pattern, $matches);
if (!empty($matches[1])) {
self::$patterns[\Locale::getDefault()] = $matches[1].' {{ widget }}';
} else if (!empty($matches[2])) {
self::$patterns[\Locale::getDefault()] = '{{ widget }} '.$matches[2];
} else {
self::$patterns[\Locale::getDefault()] = '{{ widget }}';
}
}
return self::$patterns[\Locale::getDefault()];
}
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Plugin;
use Symfony\Component\Form\Renderer\RendererInterface;
interface PluginInterface
{
function setUp(RendererInterface $renderer);
}

View File

@ -13,4 +13,21 @@ namespace Symfony\Component\Form\Renderer;
interface RendererInterface
{
function setParameter($name, $value);
function getWidget(array $attributes = array(), array $parameters = array());
function getErrors(array $attributes = array(), array $parameters = array());
function getRow(array $attributes = array(), array $parameters = array());
function getHidden(array $attributes = array(), array $parameters = array());
/**
* Renders the label of the given field
*
* @param FieldInterface $field The field to render the label for
* @param array $params Additional variables passed to the template
*/
function getLabel($label = null, array $attributes = array(), array $parameters = array());
}

View File

@ -9,8 +9,9 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Engine;
namespace Symfony\Component\Form\Renderer\Theme;
interface EngineInterface
interface ThemeInterface
{
function render($template, $block, array $parameters);
}

View File

@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer\Theme;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FieldInterface;
use Symfony\Component\Form\Exception\FormException;
class TwigTheme implements ThemeInterface
{
private $environment;
private $template;
private $blocks;
public function __construct(\Twig_Environment $environment, $template)
{
$this->environment = $environment;
$this->template = $template;
}
private function initialize()
{
if (!$this->blocks) {
$this->blocks = array();
if (!$this->template instanceof \Twig_Template) {
$this->template = $this->environment->loadTemplate($this->template);
}
foreach ($this->getBlockNames($this->template) as $blockName) {
$this->blocks[$blockName] = true;
}
}
}
private function getBlockNames(\Twig_Template $template)
{
$names = $template->getBlockNames();
$parent = $template;
while (false !== $parent = $parent->getParent(array())) {
$names = array_merge($names, $parent->getBlockNames());
}
return array_unique($names);
}
public function render($template, $block, array $parameters)
{
$this->initialize();
if (isset($this->blocks[$template.'__'.$block])) {
$blockName = $template.'__'.$block;
} else if (isset($this->blocks[$block])) {
$blockName = $block;
} else {
throw new FormException(sprintf('The form theme is missing the "%s" block', $block));
}
return $this->template->renderBlock($blockName, $parameters);
}
}

View File

@ -22,18 +22,4 @@ namespace Symfony\Component\Form;
*/
class TextField extends Field
{
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOption('max_length');
parent::configure();
}
public function getMaxLength()
{
return $this->getOption('max_length');
}
}