소스 검색

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

Bernhard Schussek 14 년 전
부모
커밋
ed68fd66a9

+ 65 - 62
src/Symfony/Bundle/TwigBundle/Resources/views/form.html.twig

@@ -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 %}
 

+ 1 - 42
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 <bernhard.schussek@symfony-project.com>
  */
@@ -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()];
-    }
 }

+ 115 - 0
src/Symfony/Component/Form/Renderer/DefaultRenderer.php

@@ -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
+        ));
+    }
+}

+ 0 - 120
src/Symfony/Component/Form/Renderer/Engine/TwigEngine.php

@@ -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);
-    }
-}

+ 0 - 146
src/Symfony/Component/Form/Renderer/FieldRenderer.php

@@ -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()))),
-        ));
-    }
-}

+ 0 - 37
src/Symfony/Component/Form/Renderer/FormRenderer.php

@@ -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"' : '';
-    }
-}

+ 39 - 0
src/Symfony/Component/Form/Renderer/Plugin/EnctypePlugin.php

@@ -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"' : '');
+    }
+}

+ 39 - 0
src/Symfony/Component/Form/Renderer/Plugin/MaxLengthPlugin.php

@@ -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);
+    }
+}

+ 71 - 0
src/Symfony/Component/Form/Renderer/Plugin/MoneyPatternPlugin.php

@@ -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()];
+    }
+}

+ 19 - 0
src/Symfony/Component/Form/Renderer/Plugin/PluginInterface.php

@@ -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);
+}

+ 17 - 0
src/Symfony/Component/Form/Renderer/RendererInterface.php

@@ -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());
 }

+ 3 - 2
src/Symfony/Component/Form/Renderer/Engine/EngineInterface.php

@@ -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);
 }

+ 71 - 0
src/Symfony/Component/Form/Renderer/Theme/TwigTheme.php

@@ -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);
+    }
+}

+ 0 - 14
src/Symfony/Component/Form/TextField.php

@@ -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');
-    }
 }