Просмотр исходного кода

[Form] Experimentally refactored Twig rendering support to Form component

Bernhard Schussek 14 лет назад
Родитель
Сommit
27e1e5251c

+ 22 - 0
src/Symfony/Component/Form/Field.php

@@ -14,6 +14,7 @@ namespace Symfony\Component\Form;
 use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
 use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
 use Symfony\Component\Form\DataProcessor\DataProcessorInterface;
+use Symfony\Component\Form\Renderer\RendererInterface;
 
 /**
  * Base class for form fields
@@ -62,6 +63,7 @@ class Field extends Configurable implements FieldInterface
     private $dataProcessor;
     private $propertyPath;
     private $transformationSuccessful = true;
+    private $renderer;
 
     public function __construct($key = null, array $options = array())
     {
@@ -483,6 +485,26 @@ class Field extends Configurable implements FieldInterface
         return $this->dataProcessor;
     }
 
+    /**
+     * Sets the renderer
+     *
+     * @param RendererInterface $renderer
+     */
+    public function setRenderer(RendererInterface $renderer)
+    {
+        $this->renderer = $renderer;
+    }
+
+    /**
+     * Returns the renderer
+     *
+     * @return RendererInterface
+     */
+    public function getRenderer()
+    {
+        return $this->renderer;
+    }
+
     /**
      * Normalizes the value if a normalization transformer is set
      *

+ 16 - 0
src/Symfony/Component/Form/Renderer/Engine/EngineInterface.php

@@ -0,0 +1,16 @@
+<?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;
+
+interface EngineInterface
+{
+}

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

@@ -0,0 +1,120 @@
+<?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);
+    }
+}

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

@@ -0,0 +1,146 @@
+<?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()))),
+        ));
+    }
+}

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

@@ -0,0 +1,37 @@
+<?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"' : '';
+    }
+}

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

@@ -0,0 +1,16 @@
+<?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;
+
+interface RendererInterface
+{
+}