diff --git a/src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig b/src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig
index 734799759c..68c5ae2171 100644
--- a/src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig
+++ b/src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig
@@ -1,23 +1,12 @@
-{% block field_row %}
+{% block row %}
{% spaceless %}
- {# 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 }}
{% 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" %}
{% else %}
- {% set attr = attr|merge({ 'maxlength': attr.maxlength|default(field.maxlength) }) %}
+ {% set attr = attr|merge({ 'maxlength': attr.maxlength|default(max_length) }) %}
{% 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) }) %}
{% endspaceless %}
-{% endblock password_field %}
+{% endblock password__widget %}
-{% block hidden_field %}
+{% block hidden__widget %}
{% spaceless %}
{% endspaceless %}
-{% endblock hidden_field %}
+{% endblock hidden__widget %}
-{% block textarea_field %}
+{% block textarea__widget %}
{% spaceless %}
{% 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 }}
{% endfor %}
{% else %}
@@ -125,79 +128,79 @@
{% endif %}
{% endspaceless %}
-{% endblock choice_field %}
+{% endblock choice__widget %}
-{% block checkbox_field %}
+{% block checkbox__widget %}
{% spaceless %}
{% endspaceless %}
-{% endblock checkbox_field %}
+{% endblock checkbox__widget %}
-{% block radio_field %}
+{% block radio__widget %}
{% spaceless %}
{% 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 %}
- {{ 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 %}
diff --git a/src/Symfony/Component/Form/MoneyField.php b/src/Symfony/Component/Form/MoneyField.php
index f2b0312a3d..986087a735 100644
--- a/src/Symfony/Component/Form/MoneyField.php
+++ b/src/Symfony/Component/Form/MoneyField.php
@@ -24,7 +24,7 @@ use Symfony\Component\Form\ValueTransformer\MoneyToLocalizedStringTransformer;
* * currency: The currency to display the money with. This is the 3-letter
* ISO 4217 currency code.
* * divisor: A number to divide the money by before displaying. Default 1.
- *
+ *
* @see Symfony\Component\Form\NumberField
* @author Bernhard Schussek
*/
@@ -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()];
- }
}
diff --git a/src/Symfony/Component/Form/Renderer/DefaultRenderer.php b/src/Symfony/Component/Form/Renderer/DefaultRenderer.php
new file mode 100644
index 0000000000..d8034d7be5
--- /dev/null
+++ b/src/Symfony/Component/Form/Renderer/DefaultRenderer.php
@@ -0,0 +1,115 @@
+
+ *
+ * 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
+ ));
+ }
+}
\ No newline at end of file
diff --git a/src/Symfony/Component/Form/Renderer/Engine/TwigEngine.php b/src/Symfony/Component/Form/Renderer/Engine/TwigEngine.php
deleted file mode 100644
index 3a2bc8a0da..0000000000
--- a/src/Symfony/Component/Form/Renderer/Engine/TwigEngine.php
+++ /dev/null
@@ -1,120 +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\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);
- }
-}
\ No newline at end of file
diff --git a/src/Symfony/Component/Form/Renderer/FieldRenderer.php b/src/Symfony/Component/Form/Renderer/FieldRenderer.php
deleted file mode 100644
index 4afaa532cb..0000000000
--- a/src/Symfony/Component/Form/Renderer/FieldRenderer.php
+++ /dev/null
@@ -1,146 +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\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:
- *
- *