[Form] reverted the templating part to be similar to what we have today

This commit is contained in:
Fabien Potencier 2011-04-07 09:57:44 +02:00
parent dfa1da0416
commit 7f2294395c
67 changed files with 556 additions and 1804 deletions

View File

@ -0,0 +1,259 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Twig\Extension;
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\Exception\FormException;
/**
* FormExtension extends Twig with form capabilities.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class FormExtension extends \Twig_Extension
{
protected $resources;
protected $templates;
protected $environment;
protected $themes;
public function __construct(array $resources = array())
{
$this->themes = new \SplObjectStorage();
$this->resources = $resources;
}
/**
* {@inheritdoc}
*/
public function initRuntime(\Twig_Environment $environment)
{
$this->environment = $environment;
}
/**
* Sets a theme for a given context.
*
* @param TemplateContext $context A TemplateContext instance
* @param array $resources An array of resources
*/
public function setTheme(TemplateContext $context, array $resources)
{
$this->themes->attach($context, $resources);
}
/**
* Returns the token parser instance to add to the existing list.
*
* @return array An array of Twig_TokenParser instances
*/
public function getTokenParsers()
{
return array(
// {% form_theme form "SomeBungle::widgets.twig" %}
new FormThemeTokenParser(),
);
}
public function getFunctions()
{
return array(
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
'form_widget' => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
'form_data' => new \Twig_Function_Method($this, 'renderData', array('is_safe' => array('html'))),
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
'form_rest' => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
);
}
/**
* Renders the HTML enctype in the form tag, if necessary
*
* Example usage in Twig templates:
*
* <form action="..." method="post" {{ form_enctype(form) }}>
*
* @param TemplateContext $context The context for which to render the encoding type
*/
public function renderEnctype(TemplateContext $context)
{
return $this->render($context, 'enctype');
}
/**
* Renders a row for the context.
*
* @param TemplateContext $context The context to render as a row
*/
public function renderRow(TemplateContext $context)
{
$context->setRendered();
return $this->render($context, 'row');
}
public function renderRest(TemplateContext $context)
{
return $this->render($context, 'rest');
}
/**
* Renders the HTML for a given context
*
* Example usage in Twig:
*
* {{ form_widget(context) }}
*
* You can pass attributes element during the call:
*
* {{ form_widget(context, {'class': 'foo'}) }}
*
* Some fields also accept additional variables as parameters:
*
* {{ form_widget(context, {}, {'separator': '+++++'}) }}
*
* @param TemplateContext $context The context 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 renderWidget(TemplateContext $context, array $attributes = array(), array $parameters = array(), $resources = null)
{
$context->setRendered();
if (null !== $resources && !is_array($resources)) {
$resources = array($resources);
}
return $this->render($context, 'widget', array(), $resources);
}
/**
* Renders the errors of the given context
*
* @param TemplateContext $context The context to render the errors for
* @param array $params Additional variables passed to the template
*/
public function renderErrors(TemplateContext $context, array $parameters = array())
{
return $this->render($context, 'errors', $parameters);
}
/**
* Renders the label of the given context
*
* @param TemplateContext $context The context to render the label for
*/
public function renderLabel(TemplateContext $context, $label = null)
{
return $this->render($context, 'label', null === $label ? array() : array('label' => $label));
}
/**
* Renders the widget data of the given context
*
* @param TemplateContext $context The context to render the data for
*/
public function renderData(TemplateContext $context)
{
return $form->getData();
}
protected function render(TemplateContext $context, $section, array $variables = array(), array $resources = null)
{
$templates = $this->getTemplates($context, $resources);
$blocks = $context->get('types');
foreach ($blocks as &$block) {
$block = $block.'__'.$section;
if (isset($templates[$block])) {
if ('widget' === $section) {
$context->set('is_rendered', true);
}
return $templates[$block]->renderBlock($block, array_merge($context->all(), $variables));
}
}
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
}
protected function getTemplate(TemplateContext $context, $name, array $resources = null)
{
$templates = $this->getTemplates($context, $resources);
return $templates[$name];
}
protected function getTemplates(TemplateContext $context, 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 = $context;
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);
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'form';
}
}

View File

@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Node;
namespace Symfony\Bridge\Twig\Node;
/**
*

View File

@ -9,9 +9,9 @@
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\TokenParser;
namespace Symfony\Bridge\Twig\TokenParser;
use Symfony\Bundle\TwigBundle\Node\FormThemeNode;
use Symfony\Bridge\Twig\Node\FormThemeNode;
/**
*

View File

@ -35,6 +35,6 @@ class AddFormGuessersPass implements CompilerPassInterface
$guessers[] = new Reference($serviceId);
}
$container->getDefinition('form.factory')->setArgument(2, $guessers);
$container->getDefinition('form.factory')->setArgument(1, $guessers);
}
}
}

View File

@ -1,46 +0,0 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
/*
* 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.
*/
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds all services with the tag "form.renderer.factory" as argument
* to the "form.renderer.factory.loader" service
*
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
*/
class AddFormRendererFactoriesPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('form.renderer.factory.loader')) {
return;
}
// Builds an array with service IDs as keys and tag aliases as values
$types = array();
$tags = $container->findTaggedServiceIds('form.renderer.factory');
foreach ($tags as $serviceId => $arguments) {
$alias = isset($arguments[0]['alias'])
? $arguments[0]['alias']
: $serviceId;
// Flip, because we want tag aliases (= type identifiers) as keys
$types[$alias] = $serviceId;
}
$container->getDefinition('form.renderer.factory.loader')->setArgument(1, $types);
}
}

View File

@ -404,13 +404,6 @@ class FrameworkExtension extends Extension
$container->getDefinition('templating.engine.delegating')->setArgument(1, $engines);
$container->setAlias('templating', 'templating.engine.delegating');
}
$container->setAlias('form.theme.factory', 'form.theme.factory.default');
foreach ($config['engines'] as $engine) {
if ($container->hasDefinition('form.theme.factory.' . $engine)) {
$container->setAlias('form.theme.factory', 'form.theme.factory.' . $engine);
}
}
}
/**

View File

@ -1,43 +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\Bundle\FrameworkBundle\Form;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Renderer\Loader\RendererFactoryLoaderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ContainerAwareRendererFactoryLoader implements RendererFactoryLoaderInterface
{
private $container;
private $serviceIds;
public function __construct(ContainerInterface $container, array $serviceIds)
{
$this->container = $container;
$this->serviceIds = $serviceIds;
}
public function getRendererFactory($name)
{
if (!isset($this->serviceIds[$name])) {
throw new FormException(sprintf('No renderer factory exists with name "%s"', $name));
}
return $this->container->get($this->serviceIds[$name]);
}
public function hasRendererFactory($name)
{
return isset($this->serviceIds[$name]);
}
}

View File

@ -1,95 +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\Bundle\FrameworkBundle\Form;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
use Symfony\Component\Templating\PhpEngine;
/**
* Renders a Form using the PHP Templating Engine.
*
* Each field is rendered as slot of a template.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class PhpEngineTheme implements ThemeInterface
{
/**
* @var array
*/
static protected $cache = array();
/**
* @var PhpEngine
*/
private $engine;
/**
* @var string
*/
private $templateDir;
/**
* @param PhpEngine $engine
*/
public function __construct(PhpEngine $engine, $templateDir = null)
{
$this->engine = $engine;
$this->templateDir = $templateDir;
}
public function render($blocks, $section, array $parameters)
{
$blocks = (array)$blocks;
foreach ($blocks as &$block) {
$block = $block.'_'.$section;
if ($template = $this->lookupTemplate($block)) {
return $this->engine->render($template, $parameters);
}
}
throw new FormException(sprintf('The form theme is missing the "%s" template files', implode('", "', $blocks)));
}
protected function lookupTemplate($templateName)
{
if (isset(self::$cache[$templateName])) {
return self::$cache[$templateName];
}
$template = $templateName.'.html.php';
if ($this->templateDir) {
$template = $this->templateDir . ':' . $template;
}
if (!$this->engine->exists($template)) {
$template = false;
}
self::$cache[$templateName] = $template;
return $template;
}
public function attributes(array $attribs)
{
$html = '';
foreach ($attribs as $k => $v) {
$html .= $this->engine->escape($k) . '="' . $this->engine->escape($v) .'" ';
}
return $html;
}
}

View File

@ -1,47 +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\Bundle\FrameworkBundle\Form;
use Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface;
use Symfony\Component\Templating\PhpEngine;
/**
* Constructs PhpEngineTheme instances
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class PhpEngineThemeFactory implements ThemeFactoryInterface
{
/**
* @var PhpEngine
*/
private $engine;
/**
* @var string
*/
private $defaultTemplateDir;
public function __construct(PhpEngine $engine, $defaultTemplateDir = null)
{
$this->engine = $engine;
$this->defaultTemplateDir = $defaultTemplateDir;
}
/**
* @see Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface::create()
*/
public function create($templateDir = null)
{
return new PhpEngineTheme($this->engine, $templateDir ?: $this->defaultTemplateDir);
}
}

View File

@ -14,7 +14,6 @@ namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFormTypesPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFormGuessersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddFormRendererFactoriesPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RoutingResolverPass;
@ -80,7 +79,6 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new AddConstraintValidatorsPass());
$container->addCompilerPass(new AddFormTypesPass());
$container->addCompilerPass(new AddFormGuessersPass());
$container->addCompilerPass(new AddFormRendererFactoriesPass());
$container->addCompilerPass(new AddClassesToCachePass());
$container->addCompilerPass(new AddClassesToAutoloadMapPass());
$container->addCompilerPass(new TranslatorPass());

View File

@ -9,14 +9,6 @@
<parameter key="form.type.loader.class">Symfony\Bundle\FrameworkBundle\Form\ContainerAwareTypeLoader</parameter>
<parameter key="form.guesser.validator.class">Symfony\Component\Form\Type\Guesser\ValidatorTypeGuesser</parameter>
<parameter key="form.csrf_provider.class">Symfony\Component\Form\CsrfProvider\SessionCsrfProvider</parameter>
<parameter key="form.renderer.factory.loader.class">Symfony\Bundle\FrameworkBundle\Form\ContainerAwareRendererFactoryLoader</parameter>
<parameter key="form.renderer.factory.theme.class">Symfony\Component\Form\Renderer\ThemeRendererFactory</parameter>
<parameter key="form.theme.factory.twig.class">Symfony\Component\Form\Renderer\Theme\TwigThemeFactory</parameter>
<parameter key="form.theme.factory.php.class">Symfony\Component\Form\Renderer\Theme\PhpThemeFactory</parameter>
<parameter key="form.theme.factory.phpengine.class">Symfony\Bundle\FrameworkBundle\Form\PhpEngineThemeFactory</parameter>
<parameter key="form.theme.template.twig">TwigBundle::div_layout.html.twig</parameter>
<parameter key="form.theme.template.phpengine">FrameworkBundle:Form</parameter>
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
<parameter key="form.csrf_protection.enabled">true</parameter>
<parameter key="form.csrf_protection.field_name">_token</parameter>
<parameter key="form.csrf_protection.secret">secret</parameter>
@ -32,7 +24,6 @@
<!-- FormFactory -->
<service id="form.factory" class="%form.factory.class%">
<argument type="service" id="form.type.loader" />
<argument type="service" id="form.renderer.factory.loader" />
<!--
All services with tag "form.guesser" are inserted here by
AddFormGuessersPass
@ -51,48 +42,7 @@
<argument type="service" id="session" />
<argument>%form.csrf_protection.secret%</argument>
</service>
<!-- RendererFactoryLoader -->
<service id="form.renderer.factory.loader" class="%form.renderer.factory.loader.class%">
<argument type="service" id="service_container" />
<!--
All services with tag "form.renderer.factory" are inserted here by
AddFormRendererFactoriesPass
-->
<argument type="collection" />
</service>
<!-- RendererFactories -->
<service id="form.renderer.factory.twig" class="%form.renderer.factory.theme.class%">
<tag name="form.renderer.factory" alias="twig" />
<argument type="service" id="form.theme.factory.twig" />
</service>
<service id="form.renderer.factory.default" class="%form.renderer.factory.theme.class%">
<tag name="form.renderer.factory" alias="default" />
<argument type="service" id="form.theme.factory.default" />
</service>
<service id="form.renderer.factory.php" class="%form.renderer.factory.theme.class%">
<tag name="form.renderer.factory" alias="php" />
<argument type="service" id="form.theme.factory.php" />
</service>
<!-- Themes -->
<service id="form.theme.factory.twig" class="%form.theme.factory.twig.class%">
<argument type="service" id="twig" />
<argument>%form.theme.template.twig%</argument>
</service>
<service id="form.theme.factory.default" class="%form.theme.factory.php.class%">
<argument>%kernel.charset%</argument>
</service>
<service id="form.theme.factory.php" class="%form.theme.factory.phpengine.class%">
<argument type="service" id="templating.engine.php" />
<argument>%form.theme.template.phpengine%</argument>
</service>
<!-- TemporaryStorage - where should we put this? -->
<service id="file.temporary_storage" class="%file.temporary_storage.class%">
<argument type="service" id="session" />

View File

@ -5,5 +5,5 @@
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($checked): ?>checked="checked"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -1,7 +1,7 @@
<?php if ($expanded): ?>
<?php foreach ($renderer as $choice => $child): ?>
<?php echo $child->getWidget() ?>
<?php echo $child->getLabel() ?>
<?php foreach ($context as $choice => $child): ?>
<?php echo $view['form']->widget($child) ?>
<?php echo $view['form']->label($child) ?>
<?php endforeach ?>
<?php else: ?>
<select
@ -10,33 +10,33 @@
<?php if ($read_only): ?> disabled="disabled"<?php endif ?>
<?php if ($multiple): ?> multiple="multiple"<?php endif ?>
<?php if ($class): ?> class="<?php echo $class ?>"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
>
<?php if (!$required): ?><option value=""><?php echo $empty_value; ?></option><?php endif; ?>
<?php if (count($preferred_choices) > 0): ?>
<?php foreach ($preferred_choices as $choice => $label): ?>
<?php if ($renderer->isChoiceGroup($label)): ?>
<?php if ($context->isChoiceGroup($label)): ?>
<optgroup label="<?php echo $choice ?>">
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
<option value="<?php echo $nestedChoice ?>"<?php if ($renderer->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $nestedLabel ?></option>
<option value="<?php echo $nestedChoice ?>"<?php if ($context->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $nestedLabel ?></option>
<?php endforeach ?>
</optgroup>
<?php else: ?>
<option value="<?php echo $choice ?>"<?php if ($renderer->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $label ?></option>
<option value="<?php echo $choice ?>"<?php if ($context->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $label ?></option>
<?php endif ?>
<?php endforeach ?>
<option disabled="disabled"><?php echo $separator ?></option>
<?php endif ?>
<?php foreach ($choices as $choice => $label): ?>
<?php if ($renderer->isChoiceGroup($label)): ?>
<?php if ($context->isChoiceGroup($label)): ?>
<optgroup label="<?php echo $choice ?>">
<?php foreach ($label as $nestedChoice => $nestedLabel): ?>
<option value="<?php echo $nestedChoice ?>"<?php if ($renderer->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $nestedLabel ?></option>
<option value="<?php echo $nestedChoice ?>"<?php if ($context->isChoiceSelected($nestedChoice)): ?> selected="selected"<?php endif?>><?php echo $nestedLabel ?></option>
<?php endforeach ?>
</optgroup>
<?php else: ?>
<option value="<?php echo $choice ?>"<?php if ($renderer->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $label ?></option>
<option value="<?php echo $choice ?>"<?php if ($context->isChoiceSelected($choice)): ?> selected="selected"<?php endif?>><?php echo $label ?></option>
<?php endif ?>
<?php endforeach ?>
</select>
<?php endif ?>
<?php endif ?>

View File

@ -1 +1 @@
<?php echo $renderer->getTheme()->render('form', 'widget', $renderer->getVars()); ?>
<?php echo $view['form']->render('form', 'widget', $renderer->getVars()); ?>

View File

@ -6,12 +6,12 @@
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $form['view']->attributes($attr); endif; ?>
/>
<?php else: ?>
<?php echo str_replace(array('{{ year }}', '{{ month }}', '{{ day }}'), array(
$renderer['year']->getWidget(),
$renderer['month']->getWidget(),
$renderer['day']->getWidget(),
$form['view']->widget($context['year']),
$form['view']->widget($context['month']),
$form['view']->widget($context['day']),
), $date_pattern) ?>
<?php endif ?>

View File

@ -1,3 +1,3 @@
<?php echo $renderer['date']->getWidget() ?>
<?php echo $form['form']->widget($context['date']) ?>
<?php /* keep space */ ?>
<?php echo $renderer['time']->getWidget() ?>
<?php echo $form['form']->widget($context['time']) ?>

View File

@ -1 +1 @@
<?php if ($renderer->getVar('multipart')): ?>enctype="multipart/form-data"<?php endif ?>
<?php if ($context->get('multipart')): ?>enctype="multipart/form-data"<?php endif ?>

View File

@ -1,5 +1,5 @@
<?php foreach ($renderer as $field): ?>
<?php if (!$field->isRendered()): ?>
<?php echo $field->getWidget() ?>
<?php foreach ($context->getChildren() as $context): ?>
<?php if (!$context->isRendered()): ?>
<?php echo $view['form']->widget($context) ?>
<?php endif; ?>
<?php endforeach; ?>

View File

@ -1,5 +1,5 @@
<div>
<?php echo $renderer->getLabel() ?>
<?php echo $renderer->getErrors() ?>
<?php echo $renderer->getWidget() ?>
<?php echo $view['form']->label($context) ?>
<?php echo $view['form']->errors($context) ?>
<?php echo $view['form']->widget($context) ?>
</div>

View File

@ -6,5 +6,5 @@
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length && $max_length > 0): ?>maxlength="<?php echo $max_length ?>"<?php endif; ?>
<?php if ($size && $size > 0): ?>size="<?php echo $size ?>"<?php endif; ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -1,10 +1,10 @@
<input type="file"
id="<?php echo $renderer['file']->getVar('id') ?>"
name="<?php echo $renderer['file']->getVar('name') ?>"
<?php if ($renderer['file']->getVar('disabled')): ?>disabled="disabled"<?php endif ?>
<?php if ($renderer['file']->getVar('required')): ?>required="required"<?php endif ?>
<?php if ($renderer['file']->getVar('class')): ?>class="<?php echo $renderer['file']->getVar('class') ?>"<?php endif ?>
id="<?php echo $context['file']->get('id') ?>"
name="<?php echo $context['file']->get('name') ?>"
<?php if ($context['file']->get('disabled')): ?>disabled="disabled"<?php endif ?>
<?php if ($context['file']->get('required')): ?>required="required"<?php endif ?>
<?php if ($context['file']->get('class')): ?>class="<?php echo $context['file']->get('class') ?>"<?php endif ?>
/>
<?php echo $renderer['token']->getWidget() ?>
<?php echo $renderer['name']->getWidget() ?>
<?php echo $view['form']->widget($context['token']) ?>
<?php echo $view['form']->widget($context['name']) ?>

View File

@ -1,8 +1,8 @@
<div>
<?php echo $renderer->getErrors() ?>
<?php foreach ($renderer as $field): ?>
<?php echo $field->getRow(); ?>
<?php echo $view['form']->errors($context); ?>
<?php foreach ($context->getChildren() as $context): ?>
<?php echo $view['form']->row($context); ?>
<?php endforeach; ?>
<?php echo $renderer->getRest() ?>
<?php echo $view['form']->rest($context) ?>
</div>

View File

@ -3,5 +3,5 @@
name="<?php echo $name ?>"
value="<?php echo $value ?>"
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -7,5 +7,5 @@
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if ($max_length && $max_length > 0): ?>maxlength="<?php echo $max_length; ?>"<?php endif; ?>
<?php if ($size && $size > 0): ?>size="<?php echo $size; ?>"<?php endif; ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -7,5 +7,5 @@
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if ($max_length && $max_length > 0): ?>maxlength="<?php echo $max_length; ?>"<?php endif; ?>
<?php if ($size && $size > 0): ?>size="<?php echo $size; ?>"<?php endif; ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -1 +1 @@
<?php echo $renderer->getTheme()->render('number', 'widget', $renderer->getVars()) ?> %
<?php echo $view['form']->widget('number', 'widget', $context) ?> %

View File

@ -6,5 +6,5 @@
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($checked): ?>checked="checked"<?php endif ?>
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -1,3 +1,3 @@
<?php foreach ($renderer as $field): ?>
<?php echo $field->getRow(); ?>
<?php foreach ($context->getChildren() as $context): ?>
<?php echo $view['form']->row($context); ?>
<?php endforeach; ?>

View File

@ -7,5 +7,5 @@
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($max_length && $max_length > 0): ?>maxlength="<?php echo $max_length; ?>"<?php endif; ?>
<?php if ($size && $size > 0): ?>size="<?php echo $size; ?>"<?php endif; ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -4,7 +4,7 @@
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
><?php
echo $view->escape($value)
?></textarea>

View File

@ -1,12 +1,12 @@
<?php
// There should be no spaces between the colons and the widgets, that's why
// this block is written in a single PHP tag
echo $renderer['hour']->getWidget(array('size' => 1));
echo $view['form']->widget($context['hour'], array('size' => 1));
echo ':';
echo $renderer['minute']->getWidget(array('size' => 1));
echo $view['form']->widget($context['minute'], array('size' => 1));
if ($with_seconds) {
echo ':';
echo $renderer['second']->getWidget(array('size' => 1));
echo $view['form']->widget($context['second'], array('size' => 1));
}
?>

View File

@ -5,5 +5,5 @@
<?php if ($read_only): ?>disabled="disabled"<?php endif ?>
<?php if ($required): ?>required="required"<?php endif ?>
<?php if ($class): ?>class="<?php echo $class ?>"<?php endif ?>
<?php if (isset($attr)): echo $renderer->getTheme()->attributes($attr); endif; ?>
<?php if (isset($attr)): echo $view['form']->attributes($attr); endif; ?>
/>

View File

@ -12,12 +12,12 @@
namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Form\FieldInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\Exception\FormException;
/**
* Form is a factory that wraps Form instances.
*
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
@ -33,77 +33,24 @@ class FormHelper extends Helper
$this->engine = $engine;
}
public function getName()
public function attributes(array $attribs)
{
return 'form';
}
public function attributes($attributes)
{
if ($attributes instanceof \Traversable) {
$attributes = iterator_to_array($attributes);
$html = '';
foreach ($attribs as $k => $v) {
$html .= $this->engine->escape($k).'="'.$this->engine->escape($v).'" ';
}
return implode('', array_map(array($this, 'attributesCallback'), array_keys($attributes), array_values($attributes)));
return $html;
}
private function attribute($name, $value)
public function enctype(TemplateContext $context)
{
return sprintf('%s="%s"', $name, true === $value ? $name : $value);
return $this->renderTemplate($context, 'enctype');
}
/**
* Prepares an attribute key and value for HTML representation.
*
* It removes empty attributes, except for the value one.
*
* @param string $name The attribute name
* @param string $value The attribute value
*
* @return string The HTML representation of the HTML key attribute pair.
*/
private function attributesCallback($name, $value)
public function widget(TemplateContext $context, array $parameters = array(), $template = null)
{
if (false === $value || null === $value || ('' === $value && 'value' != $name)) {
return '';
}
return ' '.$this->attribute($name, $value);
}
/**
* Renders the form tag.
*
* This method only renders the opening form tag.
* You need to close it after the form rendering.
*
* This method takes into account the multipart widgets.
*
* @param string $url The URL for the action
* @param array $attributes An array of HTML attributes
*
* @return string An HTML representation of the opening form tag
*/
public function enctype(/*Form */$form)
{
return $form->isMultipart() ? ' enctype="multipart/form-data"' : '';
}
public function render(/*FieldInterface */$field, array $attributes = array(), array $parameters = array(), $template = null)
{
if (null === $template) {
$template = $this->lookupTemplate($field);
if (null === $template) {
throw new \RuntimeException(sprintf('Unable to find a template to render the "%s" widget.', $field->getKey()));
}
}
return trim($this->engine->render($template, array(
'field' => $field,
'attr' => $attributes,
'params' => $parameters,
)));
return trim($this->renderTemplate($context, 'widget', $parameters, $template));
}
/**
@ -112,85 +59,68 @@ class FormHelper extends Helper
* @param FieldInterface $field
* @return string
*/
public function row(/*FieldInterface*/ $field, $template = null)
public function row(TemplateContext $context, $template = null)
{
if (null === $template) {
$template = 'FrameworkBundle:Form:field_row.html.php';
}
return $this->engine->render($template, array(
'field' => $field,
));
return $this->renderTemplate($context, 'row', array(), $template);
}
public function label(/*FieldInterface */$field, $label = false, array $parameters = array(), $template = null)
public function label(TemplateContext $context, $label = null, array $parameters = array(), $template = null)
{
if (null === $template) {
$template = 'FrameworkBundle:Form:label.html.php';
}
return $this->engine->render($template, array(
'field' => $field,
'params' => $parameters,
'label' => $label ? $label : ucfirst(strtolower(str_replace('_', ' ', $field->getKey())))
));
return $this->renderTemplate($context, 'label', null === $label ? array() : array('label' => $label));
}
public function errors(/*FieldInterface */$field, array $parameters = array(), $template = null)
public function errors(TemplateContext $context, array $parameters = array(), $template = null)
{
if (null === $template) {
$template = 'FrameworkBundle:Form:errors.html.php';
}
return $this->engine->render($template, array(
'field' => $field,
'params' => $parameters,
));
return $this->renderTemplate($context, 'errors', array(), $template);
}
public function hidden(/*FormInterface */$form, array $parameters = array(), $template = null)
public function rest(TemplateContext $context, array $parameters = array(), $template = null)
{
if (null === $template) {
$template = 'FrameworkBundle:Form:hidden.html.php';
}
return $this->engine->render($template, array(
'field' => $form,
'params' => $parameters,
));
return $this->renderTemplate($context, 'rest', array(), $template);
}
protected function lookupTemplate(/*FieldInterface */$field)
protected function renderTemplate(TemplateContext $context, $section, array $variables = array(), array $resources = null)
{
$fqClassName = get_class($field);
$template = null;
$blocks = $context->get('types');
foreach ($blocks as &$block) {
$block = $block.'_'.$section;
if (isset(self::$cache[$fqClassName])) {
return self::$cache[$fqClassName];
}
if ($template = $this->lookupTemplate($block)) {
if ('widget' === $section) {
$context->set('is_rendered', true);
}
// find a template for the given class or one of its parents
$currentFqClassName = $fqClassName;
do {
$parts = explode('\\', $currentFqClassName);
$className = array_pop($parts);
$underscoredName = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($className, '_', '.')));
if ($this->engine->exists($guess = 'FrameworkBundle:Form:'.$underscoredName.'.html.php')) {
$template = $guess;
return $this->engine->render($template, array_merge($context->all(), $variables));
}
$currentFqClassName = get_parent_class($currentFqClassName);
} while (null === $template && false !== $currentFqClassName);
if (null === $template && $field instanceof FormInterface) {
$template = 'FrameworkBundle:Form:form.html.php';
}
self::$cache[$fqClassName] = $template;
throw new FormException(sprintf('Unable to render form as none of the following blocks exist: "%s".', implode('", "', $blocks)));
}
protected function lookupTemplate($templateName)
{
if (isset(self::$cache[$templateName])) {
return self::$cache[$templateName];
}
$template = $templateName.'.html.php';
/*
if ($this->templateDir) {
$template = $this->templateDir.':'.$template;
}
*/
$template = 'FrameworkBundle:Form:'.$template;
if (!$this->engine->exists($template)) {
$template = false;
}
self::$cache[$templateName] = $template;
return $template;
}
public function getName()
{
return 'form';
}
}

View File

@ -1,300 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Extension;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FieldInterface;
use Symfony\Component\Form\CollectionField;
use Symfony\Component\Form\HybridField;
use Symfony\Bundle\TwigBundle\TokenParser\FormThemeTokenParser;
/**
* FormExtension extends Twig with form capabilities.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class FormExtension extends \Twig_Extension
{
protected $resources;
protected $templates;
protected $environment;
protected $themes;
public function __construct(array $resources = array())
{
$this->themes = new \SplObjectStorage();
$this->resources = $resources;
}
/**
* {@inheritdoc}
*/
public function initRuntime(\Twig_Environment $environment)
{
$this->environment = $environment;
}
/**
* Sets a theme for a given field.
*
* @param FieldInterface $field A FieldInterface instance
* @param array $resources An array of resources
*/
public function setTheme(FieldInterface $field, array $resources)
{
$this->themes->attach($field, $resources);
}
/**
* Returns the token parser instance to add to the existing list.
*
* @return array An array of Twig_TokenParser instances
*/
public function getTokenParsers()
{
return array(
// {% form_theme form "SomeBungle::widgets.twig" %}
new FormThemeTokenParser(),
);
}
public function getFunctions()
{
return array(
'form_enctype' => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
'form_field' => new \Twig_Function_Method($this, 'renderField', array('is_safe' => array('html'))),
'form_hidden' => new \Twig_Function_Method($this, 'renderHidden', array('is_safe' => array('html'))),
'form_errors' => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
'form_label' => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
'form_data' => new \Twig_Function_Method($this, 'renderData', array('is_safe' => array('html'))),
'form_row' => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
);
}
/**
* Renders the HTML enctype in the form tag, if necessary
*
* Example usage in Twig templates:
*
* <form action="..." method="post" {{ form_enctype(form) }}>
*
* @param Form $form The form for which to render the encoding type
*/
public function renderEnctype(Form $form)
{
return $form->isMultipart() ? 'enctype="multipart/form-data"' : '';
}
/**
* Renders a field row.
*
* @param FieldInterface $field The field to render as a row
*/
public function renderRow(FieldInterface $field)
{
return $this->render($field, 'field_row', array(
'child' => $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 renderField(FieldInterface $field, array $attributes = array(), array $parameters = array(), $resources = null)
{
if (null !== $resources && !is_array($resources)) {
$resources = array($resources);
}
return $this->render($field, 'field', array(
'field' => $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 renderHidden(FormInterface $group, array $parameters = array())
{
return $this->render($group, 'hidden', array(
'field' => $group,
'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 renderErrors(FieldInterface $field, array $parameters = array())
{
return $this->render($field, 'errors', array(
'field' => $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 renderLabel(FieldInterface $field, $label = null, array $parameters = array())
{
return $this->render($field, 'label', array(
'field' => $field,
'params' => $parameters,
'label' => null !== $label ? $label : ucfirst(strtolower(str_replace('_', ' ', $field->getKey()))),
));
}
/**
* Renders the widget data of the given field
*
* @param FieldInterface $field The field to render the data for
*/
public function renderData(FieldInterface $field)
{
return $field->getData();
}
protected 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);
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'form';
}
}

View File

@ -52,9 +52,11 @@
<tag name="twig.extension" />
</service>
<service id="twig.extension.form" class="Symfony\Bundle\TwigBundle\Extension\FormExtension" public="false">
<service id="twig.extension.form" class="Symfony\Bridge\Twig\Extension\FormExtension" public="false">
<tag name="twig.extension" />
<argument>%twig.form.resources%</argument>
<argument type="collection">
<argument>TwigBundle::table_layout.html.twig</argument>
</argument>
</service>
<service id="twig.extension.text" class="Twig_Extensions_Extension_Text" public="false" />

View File

@ -3,9 +3,9 @@
{% block field__row %}
{% spaceless %}
<div>
{{ renderer.label }}
{{ renderer.errors }}
{{ renderer.widget }}
{{ form_label(context) }}
{{ form_errors(context) }}
{{ form_widget(context) }}
</div>
{% endspaceless %}
{% endblock field__row %}
@ -13,6 +13,6 @@
{% block form__widget %}
{% spaceless %}
{{ block('field__rows') }}
{{ renderer.rest }}
{{ form_rest(context) }}
{% endspaceless %}
{% endblock form__widget %}

View File

@ -1,18 +0,0 @@
{% extends "widgets.html.twig" %}
{% block field__row %}
{% spaceless %}
<div>
{{ renderer.label }}
{{ renderer.errors }}
{{ renderer.widget }}
</div>
{% endspaceless %}
{% endblock field__row %}
{% block form__widget %}
{% spaceless %}
{{ block('field__rows') }}
{{ renderer.rest }}
{% endspaceless %}
{% endblock form__widget %}

View File

@ -1,25 +1,23 @@
{% extends "TwigBundle::widgets.html.twig" %}
{% block field__row %}
{% spaceless %}
<tr>
<td>{{ renderer.label }}</td>
<td>
{{ renderer.errors }}
{{ renderer.widget }}
{{ form_label(context) }}
</td>
<td>
{{ form_errors(context) }}
{{ form_widget(context) }}
</td>
</tr>
{% endspaceless %}
{% endblock field__row %}
{% block form__errors %}
{% spaceless %}
<tr>
<td colspan="2">
{{ block('field__errors') }}
</td>
</tr>
{% endspaceless %}
{% endblock form__errors %}
{% block repeated__errors %}
@ -27,10 +25,8 @@
{% endblock repeated__errors %}
{% block form__widget %}
{% spaceless %}
<table>
{{ block('field__rows') }}
{{ renderer.rest }}
{{ block('field__rows') }}
{{ form_rest(context) }}
</table>
{% endspaceless %}
{% endblock form__widget %}

View File

@ -1,10 +1,8 @@
{% block field__rows %}
{% spaceless %}
{{ renderer.errors }}
{% for field in renderer %}
{{ field.row }}
{{ form_errors(context) }}
{% for context in context.children %}
{{ form_row(context) }}
{% endfor %}
{% endspaceless %}
{% endblock field__rows %}
{% block field__enctype %}
@ -14,25 +12,21 @@
{% endblock field__enctype %}
{% block field__errors %}
{% spaceless %}
{% if errors|length > 0 %}
<ul>
{% for error in errors %}
<li>{% trans error.messageTemplate with error.messageParameters from 'validators' %}</li>
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}
{% endif %}
{% endblock field__errors %}
{% block field__rest %}
{% spaceless %}
{% for field in renderer %}
{% if not field.rendered %}
{{ field.row }}
{% for context in context.children %}
{% if not context.rendered %}
{{ form_widget(context) }}
{% endif %}
{% endfor %}
{% endspaceless %}
{% endblock field__rest %}
{% block field__label %}
@ -70,14 +64,12 @@
{% endblock password__widget %}
{% block hidden__widget %}
{% spaceless %}
{% set type = type|default('hidden') %}
{{ block('field__widget') }}
{% endspaceless %}
{% endblock hidden__widget %}
{% block hidden__row %}
{{ renderer.widget }}
{{ form_widget(context) }}
{% endblock hidden__row %}
{% block textarea__widget %}
@ -89,14 +81,14 @@
{% block options %}
{% spaceless %}
{% for choice, label in options %}
{% if renderer.choiceGroup(label) %}
{% if context.choiceGroup(label) %}
<optgroup label="{{ choice }}">
{% for nestedChoice, nestedLabel in label %}
<option value="{{ nestedChoice }}"{% if renderer.choiceSelected(nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel }}</option>
<option value="{{ nestedChoice }}"{% if context.choiceSelected(nestedChoice) %} selected="selected"{% endif %}>{{ nestedLabel }}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ choice }}"{% if renderer.choiceSelected(choice) %} selected="selected"{% endif %}>{{ label }}</option>
<option value="{{ choice }}"{% if context.choiceSelected(choice) %} selected="selected"{% endif %}>{{ label }}</option>
{% endif %}
{% endfor %}
{% endspaceless %}
@ -105,9 +97,10 @@
{% block choice__widget %}
{% spaceless %}
{% if expanded %}
{% for choice, field in renderer %}
{{ field.widget }}
{{ field.label(renderer.choiceLabel(choice)) }}
{% for choice, child in context %}
{{ form_widget(child) }}
{{ child.set('label', context.choiceLabel(choice)) }}
{{ form_label(child) }}
{% endfor %}
{% else %}
<select {{ block('attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
@ -140,10 +133,10 @@
{% block date_time__widget %}
{% spaceless %}
{{ renderer.date.errors }}
{{ renderer.time.errors }}
{{ renderer.date.widget }}
{{ renderer.time.widget }}
{{ form_errors(context.date) }}
{{ form_errors(context.time) }}
{{ form_widget(context.date) }}
{{ form_widget(context.time) }}
{% endspaceless %}
{% endblock date_time__widget %}
@ -152,10 +145,10 @@
{% if widget == 'text' %}
{{ block('text__widget') }}
{% else %}
{{ date_pattern|replace({
'{{ year }}': renderer.year.widget,
'{{ month }}': renderer.month.widget,
'{{ day }}': renderer.day.widget
{{ date_pattern|replace({
'{{ year }}': form_widget(context.year),
'{{ month }}': form_widget(context.month),
'{{ day }}': form_widget(context.day),
})|raw }}
{% endif %}
{% endspaceless %}
@ -163,7 +156,7 @@
{% block time__widget %}
{% spaceless %}
{{ renderer.hour.widget({ 'size': '1' }) }}:{{ renderer.minute.widget({ 'size': '1' }) }}{% if with_seconds %}:{{ renderer.second.widget({ 'size': '1' }) }}{% endif %}
{{ form_widget(context.hour, { 'size': '1' }) }}:{{ form_widget(context.minute, { 'size': '1' }) }}{% if with_seconds %}:{{ form_widget(context.second, { 'size': '1' }) }}{% endif %}
{% endspaceless %}
{% endblock time__widget %}
@ -203,9 +196,9 @@
{% block file__widget %}
{% spaceless %}
{{ renderer.file.widget }}
{{ renderer.token.widget }}
{{ renderer.name.widget }}
{{ form_widget(context.file) }}
{{ form_widget(context.token) }}
{{ form_widget(context.name) }}
{% endspaceless %}
{% endblock file__widget %}

View File

@ -17,17 +17,15 @@ use Symfony\Component\Form\Type\Guesser\TypeGuesserInterface;
use Symfony\Component\Form\Type\Guesser\Guess;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Renderer\Loader\RendererFactoryLoaderInterface;
use Symfony\Component\Form\TemplateContext;
class FormFactory implements FormFactoryInterface
{
private $typeLoader;
private $rendererFactoryLoader;
private $guessers = array();
public function __construct(TypeLoaderInterface $typeLoader, RendererFactoryLoaderInterface $rendererFactoryLoader, array $guessers = array())
public function __construct(TypeLoaderInterface $typeLoader, array $guessers = array())
{
foreach ($guessers as $guesser) {
if (!$guesser instanceof TypeGuesserInterface) {
@ -36,7 +34,6 @@ class FormFactory implements FormFactoryInterface
}
$this->typeLoader = $typeLoader;
$this->guessers = $guessers;
$this->rendererFactoryLoader = $rendererFactoryLoader;
}
public function createBuilder($type, $name = null, array $options = array())
@ -141,11 +138,9 @@ class FormFactory implements FormFactoryInterface
return $this->createBuilderForProperty($class, $property, $options)->getForm();
}
public function createRenderer(FormInterface $form, $name = null)
public function createTemplateContext(FormInterface $form)
{
// TODO if $name === null, use default renderer
return $this->rendererFactoryLoader->getRendererFactory($name)->create($form);
return TemplateContext::create($form);
}
/**

View File

@ -23,5 +23,5 @@ interface FormFactoryInterface
function createForProperty($class, $property, array $options = array());
function createRenderer(FormInterface $form, $name = null);
}
function createTemplateContext(FormInterface $form);
}

View File

@ -1,46 +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\Loader;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Renderer\RendererFactoryInterface;
class ArrayRendererFactoryLoader implements RendererFactoryLoaderInterface
{
private $factories;
public function __construct(array $factories)
{
foreach ($factories as $factory) {
if (!$factory instanceof RendererFactoryInterface) {
throw new UnexpectedTypeException($factory, 'Symfony\Component\Form\Renderer\RendererFactoryInterface');
}
}
$this->factories = $factories;
}
public function getRendererFactory($name)
{
if (!isset($this->factories[$name])) {
throw new FormException(sprintf('No renderer factory exists with name "%s"', $name));
}
return $this->factories[$name];
}
public function hasRendererFactory($name)
{
return isset($this->factories[$name]);
}
}

View File

@ -1,19 +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\Loader;
interface RendererFactoryLoaderInterface
{
function getRendererFactory($name);
function hasRendererFactory($name);
}

View File

@ -1,19 +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;
interface RendererFactoryInterface
{
function create(FormInterface $form);
}

View File

@ -1,22 +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;
interface RendererInterface
{
/**
* Returns the rendered representation of the form
*
* @return string
*/
function render();
}

View File

@ -1,323 +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\Theme;
/**
* Default Php Theme that renders a form without a dependency on a template engine.
*
* To change the rendering of this theme just extend the class and overwrite
* the respective methods.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class PhpTheme implements ThemeInterface
{
/**
* Charset to be used with htmlentities.
*
* @var string
*/
private $charset;
public function __construct($charset = 'UTF-8')
{
$this->charset = $charset;
}
public function render($blocks, $section, array $parameters)
{
$blocks = (array)$blocks;
foreach ($blocks as $block) {
$method = $block.'_'.$section;
if (method_exists($this, $method)) {
return $this->$method($parameters);
}
}
$blocks = array_map(function ($block) use ($section) {
return $block.'_'.$section;
}, $blocks);
throw new \BadMethodCallException(sprintf('PhpTheme does not support the form block methods "%s"', implode('", "', $blocks)));
}
protected function checkbox_widget($attr)
{
$html = '<input type="checkbox" ' . $this->attributes($attr);
if ($attr['checked']) {
$html .= 'checked="checked" ';
}
$html .= '/>' . PHP_EOL;
return $html;
}
protected function choice_widget($attr)
{
if ($attr['expanded']) {
$html = '';
foreach ($attr['renderer'] as $choice => $child) {
$html .= $child->getWidget() . $child->getLabel() . PHP_EOL;
}
} else {
$html = '<select ' . $this->attributes($attr);
if ($attr['multiple']) {
$html .= 'multiple="multiple" ';
}
$html .= '>' . PHP_EOL;
if (!$attr['required']) {
$html .= '<option value="">' . $this->escape($attr['empty_value']) .'</option>';
}
if (count($attr['preferred_choices']) > 0) {
$html .= $this->choice_list($attr['renderer'], $attr['preferred_choices']);
$html .= '<option disabled="disabled">' . $this->escape($attr['separator']) . '</option>' . PHP_EOL;
}
$html .= $this->choice_list($attr['renderer'], $attr['choices']);
$html .= '</select>' . PHP_EOL;
}
return $html;
}
protected function choice_list($renderer, $choices)
{
$html = '';
foreach ($choices as $choice => $label) {
if ($renderer->isChoiceGroup($label)) {
$html .= '<optgroup label="' . $this->escape($choice) .'">' . PHP_EOL;
foreach ($label as $nestedChoice => $nestedLabel) {
$html .= '<option value="' . $nestedChoice . '"' .
(($renderer->isChoiceSelected($nestedChoice)) ? ' selected="selected"' : '') .
'>';
$html .= $this->escape($nestedLabel);
$html .= '</option>' . PHP_EOL;
}
$html .= '</optgroup>' . PHP_EOL;
} else {
$html .= '<option value="' . $choice . '"' .
(($renderer->isChoiceSelected($choice)) ? ' selected="selected"' : '') .
'>';
$html .= $this->escape($label);
$html .= '</option>' . PHP_EOL;
}
}
return $html;
}
protected function collection_row($attr)
{
return $this->form_widget($attr);
}
protected function date_widget($attr)
{
if ($attr['widget'] == "text") {
return $this->text_widget($attr);
} else {
return str_replace(array('{{ year }}', '{{ month }}', '{{ day }}'), array(
$attr['renderer']['year']->getWidget(),
$attr['renderer']['month']->getWidget(),
$attr['renderer']['day']->getWidget(),
), $attr['date_pattern']);
}
}
protected function datetime_widget($attr)
{
return $attr['renderer']['date']->getWidget() . " " . $attr['renderer']['time']->getWidget() . PHP_EOL;
}
protected function field_errors($attr)
{
$html = '';
if ($attr['errors']) {
$html = '<ul>' . PHP_EOL;
foreach ($attr['errors'] as $error) {
$html .= '<li>'.$this->escape($error->getMessageTemplate()).'</li>' . PHP_EOL;
}
$html .= '</ul> . PHP_EOL';
}
return $html;
}
protected function file_widget($attr)
{
$html = '<input type="file" ' . $this->attributes($attr['renderer']['file']->getVars()) . '/>' . PHP_EOL;
$html .= $attr['renderer']['token']->getWidget();
$html .= $attr['renderer']['name']->getWidget();
return $html;
}
protected function form_widget($attr)
{
$html = '<div>' . $attr['renderer']->getErrors();
foreach ($attr['renderer'] as $child) {
$html .= $child->getRow();
}
$html .= $attr['renderer']->getRest();
return $html;
}
protected function hidden_row($attr)
{
// Dont render hidden rows, they are rendered in $form->getRest()
return '';
}
protected function hidden_widget($attr)
{
return '<input type="hidden" ' . $this->attributes($attr). ' />' . PHP_EOL;
}
protected function integer_widget($attr)
{
return $this->number_widget($attr);
}
protected function field_label($attr)
{
return '<label for="' . $this->escape($attr['id']) . '">'.$this->escape($attr['label']) . '</label>' . PHP_EOL;
}
protected function money_widget($attr)
{
return str_replace('{{ widget }}', $this->number_widget($attr), $attr['money_pattern']);
}
protected function number_widget($attr)
{
return '<input type="number" ' . $this->attributes($attr) . '/>' . PHP_EOL;
}
protected function password_widget($attr)
{
return '<input type="password" ' . $this->attributes($attr) . '/>' . PHP_EOL;
}
protected function percent_widget($attr)
{
return $this->number_widget($attr) . ' %';
}
protected function radio_widget($attr)
{
$html = '<input type="radio" ' . $this->attributes($attr);
if ($attr['checked']) {
$html .= 'checked="checked" ';
}
$html .= '/>' . PHP_EOL;
return $html;
}
protected function repeated_row($attr)
{
$html = '';
foreach ($attr['renderer'] as $child) {
$html .= $child->getRow();
}
return $html;
}
protected function field_rest($attr)
{
$html = '';
foreach ($attr['renderer'] as $child) {
if (!$child->isRendered()) {
$html .= $child->getRow();
}
}
return $html;
}
protected function field_row($attr)
{
return '<div>' . $attr['renderer']->getLabel() . $attr['renderer']->getErrors() . $attr['renderer']->getWidget() . '</div>' . PHP_EOL;
}
protected function text_widget($attr)
{
return '<input type="text" ' . $this->attributes($attr) . ' />' . PHP_EOL;
}
protected function textarea_widget($attr)
{
$html = '<textarea id="' . $this->escape($attr['id']) . "' " .
'name="' . $this->escape($attr['name']) . "' ";
if ($attr['read_only']) {
$html .= 'disabled="disabled" ';
}
if ($attr['required']) {
$html .= 'required="required" ';
}
if ($attr['class']) {
$html .= 'class="' . $this->escape($attr['class']) . '" ';
}
$html .= '>' . $this->escape($value) . '</textarea>' . PHP_EOL;
return $html;
}
protected function time_widget($attr)
{
$html = $attr['renderer']['hour']->getWidget(array('size' => 1));
$html .= ':' .$attr['renderer']['minute']->getWidget(array('size' => 1));
if ($attr['with_seconds']) {
$html .= ':' .$attr['renderer']['second']->getWidget(array('size' => 1));
}
return $html . PHP_EOL;
}
protected function url_widget($attr)
{
return '<input type="url" ' . $this->attributes($attr) . '/>' . PHP_EOL;
}
protected function widget($attr)
{
return '<input type="'. $attr['type'] . '" ' . $this->attributes($attr) . '/>' . PHP_EOL;
}
protected function attributes($attr)
{
$html = 'id="' . $this->escape($attr['id']).'" ';
$html .= 'name="' . $this->escape($attr['name']).'" ';
if ($attr['value']) {
$html .= 'value="' . $this->escape($attr['value']) .'" ';
}
if ($attr['read_only']) {
$html .= 'disabled="disabled" ';
}
if ($attr['required']) {
$html .= 'required="required" ';
}
if ($attr['class']) {
$html .= 'class="' . $this->escape($attr['class']) . '" ';
}
if (isset($attr['size']) && $attr['size'] > 0) {
$html .= 'size="' . $this->escape($attr['size']) . '" ';
}
if (isset($attr['max_length']) && $attr['max_length'] > 0) {
$html .= 'maxlength="' . $this->escape($attr['max_length']) . '" ';
}
if (isset($attr['attr'])) {
foreach ($attr['attr'] as $k => $v) {
$html .= $this->escape($k).'="'.$this->escape($v).'" ';
}
}
return $html;
}
protected function escape($val)
{
return htmlentities($val, \ENT_QUOTES, $this->charset);
}
}

View File

@ -1,44 +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\Theme;
use Symfony\Component\Form\Exception\FormException;
/**
* Creates PhpTheme instances
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class PhpThemeFactory implements ThemeFactoryInterface
{
/**
* @var string
*/
private $charset;
public function __construct($charset = 'UTF-8')
{
$this->charset = $charset;
}
/**
* @see Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface::create()
*/
public function create($template = null)
{
if ($template !== null) {
throw new FormException('PHP don\'t accept templates');
}
return new PhpTheme($this->charset);
}
}

View File

@ -1,17 +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\Theme;
interface ThemeFactoryInterface
{
function create($template = null);
}

View File

@ -1,17 +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\Theme;
interface ThemeInterface
{
function render($blocks, $section, array $parameters);
}

View File

@ -1,96 +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\Theme;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Exception\FormException;
class TwigTheme implements ThemeInterface
{
private $environment;
private $templates;
private $templatesByBlock;
public function __construct(\Twig_Environment $environment, $templates)
{
if (empty($templates)) {
$templates = array();
} else if (!is_array($templates)) {
// Don't use type casting, because then objects (Twig_Template)
// are converted to arrays
$templates = array($templates);
}
$this->environment = $environment;
$this->templates = array();
foreach ($templates as $template) {
// Remove duplicate template names
if (!is_string($template)) {
$this->templates[] = $template;
} else if (!isset($this->templates[$template])) {
$this->templates[$template] = $template;
}
}
}
private function initialize()
{
if (!$this->templatesByBlock) {
$this->templatesByBlock = array();
foreach ($this->templates as $key => $template) {
if (!$template instanceof \Twig_Template) {
$this->templates[$key] = $template = $this->environment->loadTemplate($template);
}
foreach ($this->getBlockNames($template) as $blockName) {
$this->templatesByBlock[$blockName] = $template;
}
}
}
}
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($blocks, $section, array $parameters)
{
$this->initialize();
$blocks = (array)$blocks;
foreach ($blocks as $block) {
$blockName = $block.'__'.$section;
if (isset($this->templatesByBlock[$blockName])) {
return $this->templatesByBlock[$blockName]->renderBlock($blockName, $parameters);
}
}
$blocks = array_map(function ($block) use ($section) {
return $block.'__'.$section;
}, $blocks);
throw new FormException(sprintf('The form theme is missing the "%s" blocks', implode('", "', $blocks)));
}
}

View File

@ -1,67 +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\Theme;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* Creates TwigTheme instances
*
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
*/
class TwigThemeFactory implements ThemeFactoryInterface
{
/**
* @var Twig_Environment
*/
private $environment;
/**
* @var array
*/
private $fallbackTemplates;
public function __construct(\Twig_Environment $environment, $fallbackTemplates = null)
{
if (empty($fallbackTemplates)) {
$fallbackTemplates = array();
} else if (!is_array($fallbackTemplates)) {
// Don't use type casting, because then objects (Twig_Template)
// are converted to arrays
$fallbackTemplates = array($fallbackTemplates);
}
$this->environment = $environment;
$this->fallbackTemplates = $fallbackTemplates;
}
/**
* @see Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface::create()
*/
public function create($template = null)
{
if (null !== $template && !is_string($template) && !$template instanceof \Twig_Template) {
throw new UnexpectedTypeException($template, 'string or Twig_Template');
}
$templates = $template
? array_merge($this->fallbackTemplates, array($template))
: $this->fallbackTemplates;
if (count($templates) === 0) {
throw new FormException('Twig themes either need default templates or templates passed during creation');
}
return new TwigTheme($this->environment, $templates);
}
}

View File

@ -1,61 +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\Exception\FormException;
use Symfony\Component\Form\Renderer\ThemeRenderer;
use Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface;
class ThemeRendererFactory implements RendererFactoryInterface
{
private $themeFactory;
private $defaultTemplate;
public function __construct(ThemeFactoryInterface $themeFactory, $defaultTemplate = null)
{
$this->themeFactory = $themeFactory;
$this->defaultTemplate = $defaultTemplate;
}
public function create(FormInterface $form, ThemeRenderer $parent = null)
{
$renderer = new ThemeRenderer($this->themeFactory);
if (!$parent) {
$renderer->setTemplate($this->defaultTemplate);
} else {
$renderer->setParent($parent);
}
$types = (array)$form->getTypes();
$children = array();
foreach ($types as $type) {
$renderer->setBlock($type->getName());
$type->buildRenderer($renderer, $form);
}
foreach ($form as $key => $child) {
$children[$key] = $this->create($child, $renderer);
}
$renderer->setChildren($children);
foreach ($types as $type) {
$type->buildRendererBottomUp($renderer, $form);
}
return $renderer;
}
}

View File

@ -1,40 +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;
interface ThemeRendererInterface extends RendererInterface
{
/**
* Set a renderer variable that is used to render a relevant part of the attached field.
*
* @param string $name
* @param string $value
* @return void
*/
function setVar($name, $value);
/**
* Set an arbitrary attribute to be rendered with the primary input element of the widget.
*
* Examples could include "accesskey" or HTML5 "data-*" attributes.
*
* Warning: Do not attempt to overwrite id, name, class, size or maxlength, disabled or requried attributes with this setting.
* They have their own renderer variables that should be set through {@setVar()}.
*
* Important: This is a convenience method, all variables set have to be accessed through {@getVar('attr')}
*
* @param string $name
* @param string $value
* @return void
*/
function setAttribute($name, $value);
}

View File

@ -9,20 +9,14 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Renderer;
namespace Symfony\Component\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\Theme\ThemeInterface;
use Symfony\Component\Form\Renderer\Theme\ThemeFactoryInterface;
use Symfony\Component\Form\Util\ChoiceUtil;
class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAggregate
class TemplateContext implements \ArrayAccess, \IteratorAggregate
{
private $blockHistory = array();
private $themeFactory;
private $theme;
static $cache;
private $vars = array(
'value' => null,
@ -31,6 +25,10 @@ class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAg
'attr' => array(),
);
private $parent;
private $children = array();
/**
* Is the form attached to this renderer rendered?
*
@ -42,72 +40,62 @@ class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAg
*/
private $rendered = false;
private $parent;
private $children = array();
public function __construct(ThemeFactoryInterface $themeFactory)
static public function create(FormInterface $form)
{
$this->themeFactory = $themeFactory;
}
public function setParent(self $parent)
{
$this->parent = $parent;
}
public function setChildren(array $children)
{
$this->children = $children;
}
public function setTemplate($template)
{
$this->setTheme($this->themeFactory->create($template));
}
public function setTheme(ThemeInterface $theme)
{
$this->theme = $theme;
}
public function getTheme()
{
if (!$this->theme && $this->parent) {
return $this->parent->getTheme();
if (null === self::$cache) {
self::$cache = new \SplObjectStorage();
}
return $this->theme;
if (isset(self::$cache[$form])) {
return self::$cache[$form];
}
// populate the cache for the root form
$root = $form;
while ($root->getParent()) {
$root = $root->getParent();
}
self::$cache[$root] = new self($root);
return self::$cache[$form];
}
public function setBlock($block)
private function __construct(FormInterface $form, self $parent = null)
{
array_unshift($this->blockHistory, $block);
$this->parent = $parent;
$types = (array) $form->getTypes();
$children = array();
$this->set('context', $this);
foreach ($types as $type) {
$type->buildVariables($this, $form);
}
foreach ($form as $key => $child) {
$children[$key] = self::$cache[$child] = new self($child, $this);
}
$this->setChildren($children);
foreach ($types as $type) {
$type->buildVariablesBottomUp($this, $form);
}
}
public function getBlock()
{
reset($this->blockHistory);
return current($this->block);
}
public function setVar($name, $value)
public function set($name, $value)
{
$this->vars[$name] = $value;
}
public function setAttribute($name, $value)
{
$this->vars['attr'][$name] = $value;
}
public function hasVar($name)
public function has($name)
{
return array_key_exists($name, $this->vars);
}
public function getVar($name)
public function get($name)
{
if (!isset($this->vars[$name])) {
return null;
@ -116,71 +104,29 @@ class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAg
return $this->vars[$name];
}
public function getVars()
public function all()
{
return $this->vars;
}
public function setAttribute($name, $value)
{
$this->vars['attr'][$name] = $value;
}
public function isRendered()
{
return $this->rendered;
}
public function getWidget(array $vars = array())
public function setRendered()
{
$this->rendered = true;
return $this->renderPart('widget', $vars);
}
public function getErrors(array $vars = array())
public function setParent(self $parent)
{
return $this->renderPart('errors', $vars);
}
public function getRow(array $vars = array())
{
$this->rendered = true;
return $this->renderPart('row', $vars);
}
public function getRest(array $vars = array())
{
return $this->renderPart('rest', $vars);
}
/**
* Renders the label of the given form
*
* @param FormInterface $form The form to render the label for
* @param array $params Additional variables passed to the block
*/
public function getLabel($label = null, array $vars = array())
{
if (null !== $label) {
$vars['label'] = $label;
}
return $this->renderPart('label', $vars);
}
public function getEnctype()
{
return $this->renderPart('enctype', $this->vars);
}
protected function renderPart($part, array $vars = array())
{
return $this->getTheme()->render($this->blockHistory, $part, array_replace(
$this->vars,
$vars
));
}
public function render(array $vars = array())
{
return $this->getWidget($vars);
$this->parent = $parent;
}
public function getParent()
@ -193,6 +139,11 @@ class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAg
return null !== $this->parent;
}
public function setChildren(array $children)
{
$this->children = $children;
}
public function getChildren()
{
return $this->children;
@ -239,7 +190,7 @@ class ThemeRenderer implements ThemeRendererInterface, \ArrayAccess, \IteratorAg
return isset($this->vars['choices'][$choice])
? $this->vars['choices'][$choice]
: (isset($this->vars['preferred_choices'][$choice])
? $this->cars['preferred_choices'][$choice]
? $this->vars['preferred_choices'][$choice]
: null
);
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
abstract class AbstractType implements FormTypeInterface
{
@ -22,11 +22,11 @@ abstract class AbstractType implements FormTypeInterface
{
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
}
public function buildRendererBottomUp(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariablesBottomUp(TemplateContext $variables, FormInterface $form)
{
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\DataTransformer\BooleanToStringTransformer;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class CheckboxType extends AbstractType
{
@ -24,10 +24,10 @@ class CheckboxType extends AbstractType
->setAttribute('value', $options['value']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('value', $form->getAttribute('value'));
$renderer->setVar('checked', (bool)$form->getData());
$variables->set('value', $form->getAttribute('value'));
$variables->set('checked', (bool)$form->getData());
}
public function getDefaultOptions(array $options)

View File

@ -17,7 +17,7 @@ use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Symfony\Component\Form\EventListener\FixRadioInputListener;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\DataTransformer\ScalarToChoiceTransformer;
use Symfony\Component\Form\DataTransformer\ScalarToBooleanChoicesTransformer;
use Symfony\Component\Form\DataTransformer\ArrayToChoicesTransformer;
@ -70,23 +70,23 @@ class ChoiceType extends AbstractType
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$choices = $form->getAttribute('choice_list')->getChoices();
$preferred = array_flip($form->getAttribute('preferred_choices'));
$renderer->setVar('multiple', $form->getAttribute('multiple'));
$renderer->setVar('expanded', $form->getAttribute('expanded'));
$renderer->setVar('preferred_choices', array_intersect_key($choices, $preferred));
$renderer->setVar('choices', array_diff_key($choices, $preferred));
$renderer->setVar('separator', '-------------------');
$renderer->setVar('empty_value', '');
$variables->set('multiple', $form->getAttribute('multiple'));
$variables->set('expanded', $form->getAttribute('expanded'));
$variables->set('preferred_choices', array_intersect_key($choices, $preferred));
$variables->set('choices', array_diff_key($choices, $preferred));
$variables->set('separator', '-------------------');
$variables->set('empty_value', '');
if ($renderer->getVar('multiple') && !$renderer->getVar('expanded')) {
if ($variables->get('multiple') && !$variables->get('expanded')) {
// Add "[]" to the name in case a select tag with multiple options is
// displayed. Otherwise only one of the selected options is sent in the
// POST request.
$renderer->setVar('name', $renderer->getVar('name').'[]');
$variables->set('name', $variables->get('name').'[]');
}
}

View File

@ -15,7 +15,7 @@ use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\ChoiceList\PaddedChoiceList;
use Symfony\Component\Form\ChoiceList\MonthChoiceList;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\DataTransformer\DateTimeToLocalizedStringTransformer;
use Symfony\Component\Form\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\DataTransformer\DateTimeToStringTransformer;
@ -77,11 +77,11 @@ class DateType extends AbstractType
->setAttribute('widget', $options['widget']);
}
public function buildRendererBottomUp(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariablesBottomUp(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('widget', $form->getAttribute('widget'));
$variables->set('widget', $form->getAttribute('widget'));
if ($renderer->hasChildren()) {
if ($variables->hasChildren()) {
$pattern = $form->getAttribute('formatter')->getPattern();
@ -94,7 +94,7 @@ class DateType extends AbstractType
$pattern = '{{ year }}-{{ month }}-{{ day }}';
}
$renderer->setVar('date_pattern', $pattern);
$variables->set('date_pattern', $pattern);
}
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\EventListener\TrimListener;
use Symfony\Component\Form\Validator\DefaultValidator;
use Symfony\Component\Form\Validator\DelegatingValidator;
@ -64,11 +64,11 @@ class FieldType extends AbstractType
}
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
if ($renderer->hasParent()) {
$parentId = $renderer->getParent()->getVar('id');
$parentName = $renderer->getParent()->getVar('name');
if ($variables->hasParent()) {
$parentId = $variables->getParent()->get('id');
$parentName = $variables->getParent()->get('name');
$id = sprintf('%s_%s', $parentId, $form->getName());
$name = sprintf('%s[%s]', $parentName, $form->getName());
} else {
@ -76,19 +76,24 @@ class FieldType extends AbstractType
$name = $form->getName();
}
$renderer->setVar('renderer', $renderer);
$renderer->setVar('id', $id);
$renderer->setVar('name', $name);
$renderer->setVar('errors', $form->getErrors());
$renderer->setVar('value', $form->getClientData());
$renderer->setVar('read_only', $form->isReadOnly());
$renderer->setVar('required', $form->isRequired());
$renderer->setVar('class', null);
$renderer->setVar('max_length', $form->getAttribute('max_length'));
$renderer->setVar('size', null);
$renderer->setVar('label', ucfirst(strtolower(str_replace('_', ' ', $form->getName()))));
$renderer->setVar('multipart', false);
$renderer->setVar('attr', array());
$variables->set('id', $id);
$variables->set('name', $name);
$variables->set('errors', $form->getErrors());
$variables->set('value', $form->getClientData());
$variables->set('read_only', $form->isReadOnly());
$variables->set('required', $form->isRequired());
$variables->set('class', null);
$variables->set('max_length', $form->getAttribute('max_length'));
$variables->set('size', null);
$variables->set('label', ucfirst(strtolower(str_replace('_', ' ', $form->getName()))));
$variables->set('multipart', false);
$variables->set('attr', array());
$types = array();
foreach (array_reverse((array) $form->getTypes()) as $type) {
$types[] = $type->getName();
}
$variables->set('types', $types);
}
public function getDefaultOptions(array $options)

View File

@ -19,7 +19,7 @@ use Symfony\Component\Form\DataTransformer\DataTransformerChain;
use Symfony\Component\Form\DataTransformer\ReversedTransformer;
use Symfony\Component\Form\DataTransformer\FileToStringTransformer;
use Symfony\Component\Form\DataTransformer\FileToArrayTransformer;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\HttpFoundation\File\TemporaryStorage;
class FileType extends AbstractType
@ -48,10 +48,10 @@ class FileType extends AbstractType
->add('name', 'hidden');
}
public function buildRendererBottomUp(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariablesBottomUp(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('multipart', true);
$renderer['file']->setVar('type', 'file');
$variables->set('multipart', true);
$variables['file']->set('type', 'file');
}
public function getDefaultOptions(array $options)

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\DataMapper\PropertyPathMapper;
use Symfony\Component\EventDispatcher\EventDispatcher;
@ -36,18 +36,18 @@ class FormType extends AbstractType
}
}
public function buildRendererBottomUp(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariablesBottomUp(TemplateContext $variables, FormInterface $form)
{
$multipart = false;
foreach ($renderer as $child) {
if ($child->getVar('multipart')) {
foreach ($variables as $child) {
if ($child->get('multipart')) {
$multipart = true;
break;
}
}
$renderer->setVar('multipart', $multipart);
$variables->set('multipart', $multipart);
}
public function getDefaultOptions(array $options)

View File

@ -13,15 +13,15 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
interface FormTypeInterface
{
function buildForm(FormBuilder $builder, array $options);
function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form);
function buildVariables(TemplateContext $variables, FormInterface $form);
function buildRendererBottomUp(ThemeRendererInterface $renderer, FormInterface $form);
function buildVariablesBottomUp(TemplateContext $variables, FormInterface $form);
function createBuilder($name, array $options);

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\DataTransformer\MoneyToLocalizedStringTransformer;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class MoneyType extends AbstractType
{
@ -26,9 +26,9 @@ class MoneyType extends AbstractType
->setAttribute('currency', $options['currency']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('money_pattern', self::getPattern($form->getAttribute('currency')));
$variables->set('money_pattern', self::getPattern($form->getAttribute('currency')));
}
public function getDefaultOptions(array $options)

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class PasswordType extends AbstractType
{
@ -22,10 +22,10 @@ class PasswordType extends AbstractType
$builder->setAttribute('always_empty', $options['always_empty']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
if ($form->getAttribute('always_empty') || !$form->isBound()) {
$renderer->setVar('value', '');
$variables->set('value', '');
}
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\DataTransformer\BooleanToStringTransformer;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class RadioType extends AbstractType
{
@ -24,13 +24,13 @@ class RadioType extends AbstractType
->setAttribute('value', $options['value']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('value', $form->getAttribute('value'));
$renderer->setVar('checked', (bool)$form->getData());
$variables->set('value', $form->getAttribute('value'));
$variables->set('checked', (bool)$form->getData());
if ($renderer->hasParent()) {
$renderer->setVar('name', $renderer->getParent()->getVar('name'));
if ($variables->hasParent()) {
$variables->set('name', $variables->getParent()->get('name'));
}
}

View File

@ -13,7 +13,7 @@ namespace Symfony\Component\Form\Type;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class TextType extends AbstractType
{
@ -22,9 +22,9 @@ class TextType extends AbstractType
$builder->setAttribute('max_length', $options['max_length']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('max_length', $form->getAttribute('max_length'));
$variables->set('max_length', $form->getAttribute('max_length'));
}
public function getParent(array $options)

View File

@ -18,7 +18,7 @@ use Symfony\Component\Form\DataTransformer\ReversedTransformer;
use Symfony\Component\Form\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Renderer\ThemeRendererInterface;
use Symfony\Component\Form\TemplateContext;
class TimeType extends AbstractType
{
@ -71,10 +71,10 @@ class TimeType extends AbstractType
->setAttribute('with_seconds', $options['with_seconds']);
}
public function buildRenderer(ThemeRendererInterface $renderer, FormInterface $form)
public function buildVariables(TemplateContext $variables, FormInterface $form)
{
$renderer->setVar('widget', $form->getAttribute('widget'));
$renderer->setVar('with_seconds', $form->getAttribute('with_seconds'));
$variables->set('widget', $form->getAttribute('widget'));
$variables->set('with_seconds', $form->getAttribute('with_seconds'));
}
public function getDefaultOptions(array $options)