[Form] Extracted common parts of FormHelper and FormExtension into separate classes
This commit is contained in:
parent
216c539e41
commit
629093ed25
|
@ -1053,6 +1053,21 @@
|
|||
$registry->addType($registry->resolveType(new MyFormType()));
|
||||
```
|
||||
|
||||
* The method `renderBlock()` of the helper for the PHP Templating component was
|
||||
deprecated and will be removed in Symfony 2.3. You should use `block()` instead.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
```
|
||||
|
||||
### Validator
|
||||
|
||||
* The methods `setMessage()`, `getMessageTemplate()` and
|
||||
|
|
|
@ -12,11 +12,9 @@
|
|||
namespace Symfony\Bridge\Twig\Extension;
|
||||
|
||||
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Bridge\Twig\Form\TwigRendererInterface;
|
||||
use Symfony\Component\Form\FormViewInterface;
|
||||
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
use Symfony\Component\Form\Util\FormUtil;
|
||||
|
||||
/**
|
||||
* FormExtension extends Twig with form capabilities.
|
||||
|
@ -26,21 +24,17 @@ use Symfony\Component\Form\Util\FormUtil;
|
|||
*/
|
||||
class FormExtension extends \Twig_Extension
|
||||
{
|
||||
protected $csrfProvider;
|
||||
protected $resources;
|
||||
protected $blocks;
|
||||
protected $environment;
|
||||
protected $themes;
|
||||
protected $varStack;
|
||||
protected $template;
|
||||
/**
|
||||
* This property is public so that it can be accessed directly from compiled
|
||||
* templates without having to call a getter, which slightly decreases performance.
|
||||
*
|
||||
* @var \Symfony\Component\Form\FormRendererInterface
|
||||
*/
|
||||
public $renderer;
|
||||
|
||||
public function __construct(CsrfProviderInterface $csrfProvider = null, array $resources = array())
|
||||
public function __construct(TwigRendererInterface $renderer)
|
||||
{
|
||||
$this->csrfProvider = $csrfProvider;
|
||||
$this->themes = new \SplObjectStorage();
|
||||
$this->varStack = array();
|
||||
$this->blocks = new \SplObjectStorage();
|
||||
$this->resources = $resources;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,25 +42,11 @@ class FormExtension extends \Twig_Extension
|
|||
*/
|
||||
public function initRuntime(\Twig_Environment $environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
$this->renderer->setEnvironment($environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a theme for a given view.
|
||||
*
|
||||
* @param FormView $view A FormView instance
|
||||
* @param array|string $resources An array of resource names|a resource name
|
||||
*/
|
||||
public function setTheme(FormView $view, $resources)
|
||||
{
|
||||
$this->themes->attach($view, (array) $resources);
|
||||
$this->blocks = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
|
@ -76,305 +56,39 @@ class FormExtension extends \Twig_Extension
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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_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'))),
|
||||
'csrf_token' => new \Twig_Function_Method($this, 'getCsrfToken'),
|
||||
'_form_is_choice_group' => new \Twig_Function_Method($this, 'isChoiceGroup', array('is_safe' => array('html'))),
|
||||
'_form_is_choice_selected' => new \Twig_Function_Method($this, 'isChoiceSelected', array('is_safe' => array('html'))),
|
||||
'form_enctype' => new \Twig_Function_Method($this, 'renderer->renderEnctype', array('is_safe' => array('html'))),
|
||||
'form_widget' => new \Twig_Function_Method($this, 'renderer->renderWidget', array('is_safe' => array('html'))),
|
||||
'form_errors' => new \Twig_Function_Method($this, 'renderer->renderErrors', array('is_safe' => array('html'))),
|
||||
'form_label' => new \Twig_Function_Method($this, 'renderer->renderLabel', array('is_safe' => array('html'))),
|
||||
'form_row' => new \Twig_Function_Method($this, 'renderer->renderRow', array('is_safe' => array('html'))),
|
||||
'form_rest' => new \Twig_Function_Method($this, 'renderer->renderRest', array('is_safe' => array('html'))),
|
||||
'csrf_token' => new \Twig_Function_Method($this, 'renderer->renderCsrfToken'),
|
||||
'_form_is_choice_group' => new \Twig_Function_Method($this, 'renderer->isChoiceGroup', array('is_safe' => array('html'))),
|
||||
'_form_is_choice_selected' => new \Twig_Function_Method($this, 'renderer->isChoiceSelected', array('is_safe' => array('html'))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'humanize' => new \Twig_Filter_Function(__NAMESPACE__.'\humanize'),
|
||||
'humanize' => new \Twig_Filter_Method($this, 'renderer->humanize'),
|
||||
);
|
||||
}
|
||||
|
||||
public function isChoiceGroup($label)
|
||||
{
|
||||
return FormUtil::isChoiceGroup($label);
|
||||
}
|
||||
|
||||
public function isChoiceSelected(FormView $view, ChoiceView $choice)
|
||||
{
|
||||
return FormUtil::isChoiceSelected($choice->getValue(), $view->getVar('value'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML enctype in the form tag, if necessary
|
||||
*
|
||||
* Example usage in Twig templates:
|
||||
*
|
||||
* <form action="..." method="post" {{ form_enctype(form) }}>
|
||||
*
|
||||
* @param FormView $view The view for which to render the encoding type
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderEnctype(FormView $view)
|
||||
{
|
||||
return $this->render($view, 'enctype');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a row for the view.
|
||||
*
|
||||
* @param FormView $view The view to render as a row
|
||||
* @param array $variables An array of variables
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderRow(FormView $view, array $variables = array())
|
||||
{
|
||||
return $this->render($view, 'row', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders views which have not already been rendered.
|
||||
*
|
||||
* @param FormView $view The parent view
|
||||
* @param array $variables An array of variables
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderRest(FormView $view, array $variables = array())
|
||||
{
|
||||
return $this->render($view, 'rest', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for a given view
|
||||
*
|
||||
* Example usage in Twig:
|
||||
*
|
||||
* {{ form_widget(view) }}
|
||||
*
|
||||
* You can pass options during the call:
|
||||
*
|
||||
* {{ form_widget(view, {'attr': {'class': 'foo'}}) }}
|
||||
*
|
||||
* {{ form_widget(view, {'separator': '+++++'}) }}
|
||||
*
|
||||
* @param FormView $view The view to render
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderWidget(FormView $view, array $variables = array())
|
||||
{
|
||||
return $this->render($view, 'widget', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the errors of the given view
|
||||
*
|
||||
* @param FormView $view The view to render the errors for
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderErrors(FormView $view)
|
||||
{
|
||||
return $this->render($view, 'errors');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the label of the given view
|
||||
*
|
||||
* @param FormView $view The view to render the label for
|
||||
* @param string $label Label name
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The html markup
|
||||
*/
|
||||
public function renderLabel(FormView $view, $label = null, array $variables = array())
|
||||
{
|
||||
if ($label !== null) {
|
||||
$variables += array('label' => $label);
|
||||
}
|
||||
|
||||
return $this->render($view, 'label', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* 1. This function first looks for a block named "_<view id>_<section>",
|
||||
* 2. if such a block is not found the function will look for a block named
|
||||
* "<type name>_<section>",
|
||||
* 3. the type name is recursively replaced by the parent type name until a
|
||||
* corresponding block is found
|
||||
*
|
||||
* @param FormView $view The form view
|
||||
* @param string $section The section to render (i.e. 'row', 'widget', 'label', ...)
|
||||
* @param array $variables Additional variables
|
||||
*
|
||||
* @return string The html markup
|
||||
*
|
||||
* @throws FormException if no template block exists to render the given section of the view
|
||||
*/
|
||||
protected function render(FormView $view, $section, array $variables = array())
|
||||
{
|
||||
$mainTemplate = in_array($section, array('widget', 'row'));
|
||||
|
||||
if ($mainTemplate && $view->isRendered()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (null === $this->template) {
|
||||
$this->template = reset($this->resources);
|
||||
if (!$this->template instanceof \Twig_Template) {
|
||||
$this->template = $this->environment->loadTemplate($this->template);
|
||||
}
|
||||
}
|
||||
|
||||
$custom = '_'.$view->getVar('id');
|
||||
$rendering = $custom.$section;
|
||||
$blocks = $this->getBlocks($view);
|
||||
|
||||
if (isset($this->varStack[$rendering])) {
|
||||
$typeIndex = $this->varStack[$rendering]['typeIndex'] - 1;
|
||||
$types = $this->varStack[$rendering]['types'];
|
||||
$this->varStack[$rendering]['variables'] = array_replace_recursive($this->varStack[$rendering]['variables'], $variables);
|
||||
} else {
|
||||
$types = $view->getVar('types');
|
||||
$types[] = $view->getVar('full_block_name');
|
||||
$typeIndex = count($types) - 1;
|
||||
$this->varStack[$rendering] = array(
|
||||
'variables' => array_replace_recursive($view->getVars(), $variables),
|
||||
'types' => $types,
|
||||
);
|
||||
}
|
||||
|
||||
do {
|
||||
$types[$typeIndex] .= '_'.$section;
|
||||
|
||||
if (isset($blocks[$types[$typeIndex]])) {
|
||||
$this->varStack[$rendering]['typeIndex'] = $typeIndex;
|
||||
|
||||
$context = $this->environment->mergeGlobals($this->varStack[$rendering]['variables']);
|
||||
|
||||
// we do not call renderBlock here to avoid too many nested level calls (XDebug limits the level to 100 by default)
|
||||
ob_start();
|
||||
$this->template->displayBlock($types[$typeIndex], $context, $blocks);
|
||||
$html = ob_get_clean();
|
||||
|
||||
if ($mainTemplate) {
|
||||
$view->setRendered();
|
||||
}
|
||||
|
||||
unset($this->varStack[$rendering]);
|
||||
|
||||
return $html;
|
||||
}
|
||||
} while (--$typeIndex >= 0);
|
||||
|
||||
throw new FormException(sprintf(
|
||||
'Unable to render the form as none of the following blocks exist: "%s".',
|
||||
implode('", "', array_reverse($types))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CSRF token.
|
||||
*
|
||||
* Use this helper for CSRF protection without the overhead of creating a
|
||||
* form.
|
||||
*
|
||||
* <code>
|
||||
* <input type="hidden" name="token" value="{{ csrf_token('rm_user_' ~ user.id) }}">
|
||||
* </code>
|
||||
*
|
||||
* Check the token in your action using the same intention.
|
||||
*
|
||||
* <code>
|
||||
* $csrfProvider = $this->get('form.csrf_provider');
|
||||
* if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) {
|
||||
* throw new \RuntimeException('CSRF attack detected.');
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param string $intention The intention of the protected action
|
||||
*
|
||||
* @return string A CSRF token
|
||||
*/
|
||||
public function getCsrfToken($intention)
|
||||
{
|
||||
if (!$this->csrfProvider instanceof CsrfProviderInterface) {
|
||||
throw new \BadMethodCallException('CSRF token can only be generated if a CsrfProviderInterface is injected in the constructor.');
|
||||
}
|
||||
|
||||
return $this->csrfProvider->generateCsrfToken($intention);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blocks used to render the view.
|
||||
*
|
||||
* Templates are looked for in the resources in the following order:
|
||||
* * resources from the themes (and its parents)
|
||||
* * resources from the themes of parent views (up to the root view)
|
||||
* * default resources
|
||||
*
|
||||
* @param FormView $view The view
|
||||
*
|
||||
* @return array An array of Twig_TemplateInterface instances
|
||||
*/
|
||||
protected function getBlocks(FormView $view)
|
||||
{
|
||||
if (!$this->blocks->contains($view)) {
|
||||
$rootView = !$view->hasParent();
|
||||
|
||||
$templates = $rootView ? $this->resources : array();
|
||||
|
||||
if (isset($this->themes[$view])) {
|
||||
$templates = array_merge($templates, $this->themes[$view]);
|
||||
}
|
||||
|
||||
$blocks = array();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
if (!$template instanceof \Twig_Template) {
|
||||
$template = $this->environment->loadTemplate($template);
|
||||
}
|
||||
|
||||
$templateBlocks = array();
|
||||
do {
|
||||
$templateBlocks = array_merge($template->getBlocks(), $templateBlocks);
|
||||
} while (false !== $template = $template->getParent(array()));
|
||||
$blocks = array_merge($blocks, $templateBlocks);
|
||||
}
|
||||
|
||||
if (!$rootView) {
|
||||
$blocks = array_merge($this->getBlocks($view->getParent()), $blocks);
|
||||
}
|
||||
|
||||
$this->blocks->attach($view, $blocks);
|
||||
} else {
|
||||
$blocks = $this->blocks[$view];
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
}
|
||||
|
||||
function humanize($text)
|
||||
{
|
||||
return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text))));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?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\Form;
|
||||
|
||||
use Symfony\Component\Form\FormRenderer;
|
||||
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class TwigRenderer extends FormRenderer implements TwigRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var TwigRendererEngineInterface
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
|
||||
{
|
||||
parent::__construct($engine, $csrfProvider);
|
||||
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setEnvironment(\Twig_Environment $environment)
|
||||
{
|
||||
$this->engine->setEnvironment($environment);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?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\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractRendererEngine;
|
||||
use Symfony\Component\Form\FormViewInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class TwigRendererEngine extends AbstractRendererEngine implements TwigRendererEngineInterface
|
||||
{
|
||||
/**
|
||||
* @var \Twig_Environment
|
||||
*/
|
||||
private $environment;
|
||||
|
||||
/**
|
||||
* @var \Twig_Template
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setEnvironment(\Twig_Environment $environment)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderBlock(FormViewInterface $view, $resource, $block, array $variables = array())
|
||||
{
|
||||
$cacheKey = $view->getVar(self::CACHE_KEY_VAR);
|
||||
|
||||
$context = $this->environment->mergeGlobals($variables);
|
||||
|
||||
ob_start();
|
||||
|
||||
// By contract,This method can only be called after getting the resource
|
||||
// (which is passed to the method). Getting a resource for the first time
|
||||
// (with an empty cache) is guaranteed to invoke loadResourcesFromTheme(),
|
||||
// where the property $template is initialized.
|
||||
|
||||
// We do not call renderBlock here to avoid too many nested level calls
|
||||
// (XDebug limits the level to 100 by default)
|
||||
$this->template->displayBlock($block, $context, $this->resources[$cacheKey]);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache with the resource for a given block name.
|
||||
*
|
||||
* This implementation eagerly loads all blocks of the themes assigned to the given view
|
||||
* and all of its ancestors views. This is necessary, because Twig receives the
|
||||
* list of blocks later. At that point, all blocks must already be loaded, for the
|
||||
* case that the function "block()" is used in the Twig template.
|
||||
*
|
||||
* @see getResourceForBlock()
|
||||
*
|
||||
* @param string $cacheKey The cache key of the form view.
|
||||
* @param FormViewInterface $view The form view for finding the applying themes.
|
||||
* @param string $block The name of the block to load.
|
||||
*
|
||||
* @return Boolean True if the resource could be loaded, false otherwise.
|
||||
*/
|
||||
protected function loadResourceForBlock($cacheKey, FormViewInterface $view, $block)
|
||||
{
|
||||
// Recursively try to find the block in the themes assigned to $view,
|
||||
// then of its parent view, then of the parent view of the parent and so on.
|
||||
// When the root view is reached in this recursion, also the default
|
||||
// themes are taken into account.
|
||||
|
||||
// Check each theme whether it contains the searched block
|
||||
if (isset($this->themes[$cacheKey])) {
|
||||
for ($i = count($this->themes[$cacheKey]) - 1; $i >= 0; --$i) {
|
||||
$this->loadResourcesFromTheme($cacheKey, $this->themes[$cacheKey][$i]);
|
||||
// CONTINUE LOADING (see doc comment)
|
||||
}
|
||||
}
|
||||
|
||||
// Check the default themes once we reach the root view without success
|
||||
if (!$view->hasParent()) {
|
||||
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
|
||||
$this->loadResourcesFromTheme($cacheKey, $this->defaultThemes[$i]);
|
||||
// CONTINUE LOADING (see doc comment)
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not find anything in the themes of the current view, proceed
|
||||
// with the themes of the parent view
|
||||
if ($view->hasParent()) {
|
||||
$parentCacheKey = $view->getParent()->getVar(self::CACHE_KEY_VAR);
|
||||
|
||||
if (!isset($this->resources[$parentCacheKey])) {
|
||||
$this->loadResourceForBlock($parentCacheKey, $view->getParent(), $block);
|
||||
}
|
||||
|
||||
// EAGER CACHE POPULATION (see doc comment)
|
||||
foreach ($this->resources[$parentCacheKey] as $blockName => $resource) {
|
||||
if (!isset($this->resources[$cacheKey][$blockName])) {
|
||||
$this->resources[$cacheKey][$blockName] = $resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->resources[$cacheKey][$block])) {
|
||||
// Cache that we didn't find anything to speed up further accesses
|
||||
$this->resources[$cacheKey][$block] = false;
|
||||
}
|
||||
|
||||
return false !== $this->resources[$cacheKey][$block];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the resources for all blocks in a theme.
|
||||
*
|
||||
* @param string $cacheKey The cache key for storing the resource.
|
||||
* @param mixed $theme The theme to load the block from. This parameter
|
||||
* is passed by reference, because it might be necessary
|
||||
* to initialize the theme first. Any changes made to
|
||||
* this variable will be kept and be available upon
|
||||
* further calls to this method using the same theme.
|
||||
*/
|
||||
protected function loadResourcesFromTheme($cacheKey, &$theme)
|
||||
{
|
||||
if (!$theme instanceof \Twig_Template) {
|
||||
/* @var \Twig_Template $theme */
|
||||
$theme = $this->environment->loadTemplate($theme);
|
||||
}
|
||||
|
||||
if (null === $this->template) {
|
||||
// Store the first \Twig_Template instance that we find so that
|
||||
// we can call displayBlock() later on. It doesn't matter *which*
|
||||
// template we use for that, since we pass the used blocks manually
|
||||
// anyway.
|
||||
$this->template = $theme;
|
||||
}
|
||||
|
||||
foreach ($theme->getBlocks() as $block => $blockData) {
|
||||
if (!isset($this->resources[$cacheKey][$block])) {
|
||||
// The resource given back is the key to the bucket that
|
||||
// contains this block.
|
||||
$this->resources[$cacheKey][$block] = $blockData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?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\Form;
|
||||
|
||||
use Symfony\Component\Form\FormRendererEngineInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface TwigRendererEngineInterface extends FormRendererEngineInterface
|
||||
{
|
||||
/**
|
||||
* Sets Twig's environment.
|
||||
*
|
||||
* @param \Twig_Environment $environment
|
||||
*/
|
||||
public function setEnvironment(\Twig_Environment $environment);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?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\Form;
|
||||
|
||||
use Symfony\Component\Form\FormRendererInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface TwigRendererInterface extends FormRendererInterface
|
||||
{
|
||||
/**
|
||||
* Sets Twig's environment.
|
||||
*
|
||||
* @param \Twig_Environment $environment
|
||||
*/
|
||||
public function setEnvironment(\Twig_Environment $environment);
|
||||
}
|
|
@ -30,7 +30,7 @@ class FormThemeNode extends \Twig_Node
|
|||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('echo $this->env->getExtension(\'form\')->setTheme(')
|
||||
->write('echo $this->env->getExtension(\'form\')->renderer->setTheme(')
|
||||
->subcompile($this->getNode('form'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('resources'))
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
namespace Symfony\Bridge\Twig\Tests\Extension;
|
||||
|
||||
use Symfony\Bridge\Twig\Extension\FormExtension;
|
||||
use Symfony\Bridge\Twig\Form\TwigRenderer;
|
||||
use Symfony\Bridge\Twig\Form\TwigRendererEngine;
|
||||
use Symfony\Bridge\Twig\Extension\TranslationExtension;
|
||||
use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator;
|
||||
use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
|
||||
|
@ -20,6 +22,9 @@ use Symfony\Component\Form\Tests\AbstractDivLayoutTest;
|
|||
|
||||
class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
|
||||
{
|
||||
/**
|
||||
* @var FormExtension
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
protected function setUp()
|
||||
|
@ -42,20 +47,23 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
|
|||
|
||||
parent::setUp();
|
||||
|
||||
$loader = new StubFilesystemLoader(array(
|
||||
__DIR__.'/../../../../../../src/Symfony/Bridge/Twig/Resources/views/Form',
|
||||
__DIR__,
|
||||
));
|
||||
|
||||
$this->extension = new FormExtension($this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'), array(
|
||||
$rendererEngine = new TwigRendererEngine(array(
|
||||
'form_div_layout.html.twig',
|
||||
'custom_widgets.html.twig',
|
||||
));
|
||||
$renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
|
||||
|
||||
$this->extension = new FormExtension($renderer);
|
||||
|
||||
$loader = new StubFilesystemLoader(array(
|
||||
__DIR__.'/../../Resources/views/Form',
|
||||
__DIR__,
|
||||
));
|
||||
|
||||
$environment = new \Twig_Environment($loader, array('strict_variables' => true));
|
||||
$environment->addExtension($this->extension);
|
||||
$environment->addExtension(new TranslationExtension(new StubTranslator()));
|
||||
$environment->addGlobal('global', '');
|
||||
$environment->addExtension($this->extension);
|
||||
|
||||
$this->extension->initRuntime($environment);
|
||||
}
|
||||
|
@ -99,37 +107,37 @@ class FormExtensionDivLayoutTest extends AbstractDivLayoutTest
|
|||
|
||||
protected function renderEnctype(FormView $view)
|
||||
{
|
||||
return (string) $this->extension->renderEnctype($view);
|
||||
return (string) $this->extension->renderer->renderEnctype($view);
|
||||
}
|
||||
|
||||
protected function renderLabel(FormView $view, $label = null, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderLabel($view, $label, $vars);
|
||||
return (string) $this->extension->renderer->renderLabel($view, $label, $vars);
|
||||
}
|
||||
|
||||
protected function renderErrors(FormView $view)
|
||||
{
|
||||
return (string) $this->extension->renderErrors($view);
|
||||
return (string) $this->extension->renderer->renderErrors($view);
|
||||
}
|
||||
|
||||
protected function renderWidget(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderWidget($view, $vars);
|
||||
return (string) $this->extension->renderer->renderWidget($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRow(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderRow($view, $vars);
|
||||
return (string) $this->extension->renderer->renderRow($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRest(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderRest($view, $vars);
|
||||
return (string) $this->extension->renderer->renderRest($view, $vars);
|
||||
}
|
||||
|
||||
protected function setTheme(FormView $view, array $themes)
|
||||
{
|
||||
$this->extension->setTheme($view, $themes);
|
||||
$this->extension->renderer->setTheme($view, $themes);
|
||||
}
|
||||
|
||||
public static function themeBlockInheritanceProvider()
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
namespace Symfony\Bridge\Twig\Tests\Extension;
|
||||
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Bridge\Twig\Form\TwigRenderer;
|
||||
use Symfony\Bridge\Twig\Form\TwigRendererEngine;
|
||||
use Symfony\Bridge\Twig\Extension\FormExtension;
|
||||
use Symfony\Bridge\Twig\Extension\TranslationExtension;
|
||||
use Symfony\Component\Form\Tests\AbstractTableLayoutTest;
|
||||
|
@ -20,6 +22,9 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
|
|||
|
||||
class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
|
||||
{
|
||||
/**
|
||||
* @var FormExtension
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
protected function setUp()
|
||||
|
@ -42,20 +47,23 @@ class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
|
|||
|
||||
parent::setUp();
|
||||
|
||||
$loader = new StubFilesystemLoader(array(
|
||||
__DIR__.'/../../../../../../src/Symfony/Bridge/Twig/Resources/views/Form',
|
||||
__DIR__,
|
||||
));
|
||||
|
||||
$this->extension = new FormExtension($this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'), array(
|
||||
$rendererEngine = new TwigRendererEngine(array(
|
||||
'form_table_layout.html.twig',
|
||||
'custom_widgets.html.twig',
|
||||
));
|
||||
$renderer = new TwigRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
|
||||
|
||||
$this->extension = new FormExtension($renderer);
|
||||
|
||||
$loader = new StubFilesystemLoader(array(
|
||||
__DIR__.'/../../Resources/views/Form',
|
||||
__DIR__,
|
||||
));
|
||||
|
||||
$environment = new \Twig_Environment($loader, array('strict_variables' => true));
|
||||
$environment->addExtension($this->extension);
|
||||
$environment->addExtension(new TranslationExtension(new StubTranslator()));
|
||||
$environment->addGlobal('global', '');
|
||||
$environment->addExtension($this->extension);
|
||||
|
||||
$this->extension->initRuntime($environment);
|
||||
}
|
||||
|
@ -69,36 +77,36 @@ class FormExtensionTableLayoutTest extends AbstractTableLayoutTest
|
|||
|
||||
protected function renderEnctype(FormView $view)
|
||||
{
|
||||
return (string) $this->extension->renderEnctype($view);
|
||||
return (string) $this->extension->renderer->renderEnctype($view);
|
||||
}
|
||||
|
||||
protected function renderLabel(FormView $view, $label = null, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderLabel($view, $label, $vars);
|
||||
return (string) $this->extension->renderer->renderLabel($view, $label, $vars);
|
||||
}
|
||||
|
||||
protected function renderErrors(FormView $view)
|
||||
{
|
||||
return (string) $this->extension->renderErrors($view);
|
||||
return (string) $this->extension->renderer->renderErrors($view);
|
||||
}
|
||||
|
||||
protected function renderWidget(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderWidget($view, $vars);
|
||||
return (string) $this->extension->renderer->renderWidget($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRow(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderRow($view, $vars);
|
||||
return (string) $this->extension->renderer->renderRow($view, $vars);
|
||||
}
|
||||
|
||||
protected function renderRest(FormView $view, array $vars = array())
|
||||
{
|
||||
return (string) $this->extension->renderRest($view, $vars);
|
||||
return (string) $this->extension->renderer->renderRest($view, $vars);
|
||||
}
|
||||
|
||||
protected function setTheme(FormView $view, array $themes)
|
||||
{
|
||||
$this->extension->setTheme($view, $themes);
|
||||
$this->extension->renderer->setTheme($view, $themes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
<parameter key="templating.helper.code.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\CodeHelper</parameter>
|
||||
<parameter key="templating.helper.translator.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper</parameter>
|
||||
<parameter key="templating.helper.form.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper</parameter>
|
||||
<parameter key="templating.form.engine.class">Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine</parameter>
|
||||
<parameter key="templating.form.renderer.class">Symfony\Component\Form\FormRenderer</parameter>
|
||||
<parameter key="templating.globals.class">Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables</parameter>
|
||||
<parameter key="templating.asset.path_package.class">Symfony\Bundle\FrameworkBundle\Templating\Asset\PathPackage</parameter>
|
||||
<parameter key="templating.asset.url_package.class">Symfony\Component\Templating\Asset\UrlPackage</parameter>
|
||||
|
@ -96,11 +98,19 @@
|
|||
|
||||
<service id="templating.helper.form" class="%templating.helper.form.class%">
|
||||
<tag name="templating.helper" alias="form" />
|
||||
<argument type="service" id="templating.form.renderer" />
|
||||
</service>
|
||||
|
||||
<service id="templating.form.engine" class="%templating.form.engine.class%" public="false">
|
||||
<argument type="service" id="templating.engine.php" />
|
||||
<argument type="service" id="form.csrf_provider" on-invalid="null" />
|
||||
<argument>%templating.helper.form.resources%</argument>
|
||||
</service>
|
||||
|
||||
<service id="templating.form.renderer" class="%templating.form.renderer.class%" public="false">
|
||||
<argument type="service" id="templating.form.engine" />
|
||||
<argument type="service" id="form.csrf_provider" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="templating.globals" class="%templating.globals.class%">
|
||||
<argument type="service" id="service_container" />
|
||||
</service>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<input type="checkbox"
|
||||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
<?php if ($value): ?> value="<?php echo $view->escape($value) ?>"<?php endif ?>
|
||||
<?php if ($checked): ?> checked="checked"<?php endif ?>
|
||||
/>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('choice_widget_options') ?>
|
||||
<?php echo $view['form']->block('choice_widget_options') ?>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php if ($expanded): ?>
|
||||
<?php echo $view['form']->renderBlock('choice_widget_expanded') ?>
|
||||
<?php echo $view['form']->block('choice_widget_expanded') ?>
|
||||
<?php else: ?>
|
||||
<?php echo $view['form']->renderBlock('choice_widget_collapsed') ?>
|
||||
<?php echo $view['form']->block('choice_widget_collapsed') ?>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<select
|
||||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
<?php if ($multiple): ?> multiple="multiple"<?php endif ?>
|
||||
>
|
||||
<?php if (null !== $empty_value): ?><option value=""><?php echo $view->escape($view['translator']->trans($empty_value, array(), $translation_domain)) ?></option><?php endif; ?>
|
||||
<?php if (count($preferred_choices) > 0): ?>
|
||||
<?php echo $view['form']->renderBlock('choice_widget_options', array('options' => $preferred_choices)) ?>
|
||||
<?php echo $view['form']->block('choice_widget_options', array('options' => $preferred_choices)) ?>
|
||||
<?php if (count($choices) > 0 && null !== $separator): ?>
|
||||
<option disabled="disabled"><?php echo $separator ?></option>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
<?php echo $view['form']->renderBlock('choice_widget_options', array('options' => $choices)) ?>
|
||||
<?php echo $view['form']->block('choice_widget_options', array('options' => $choices)) ?>
|
||||
</select>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<div <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php foreach ($form as $child): ?>
|
||||
<?php echo $view['form']->widget($child) ?>
|
||||
<?php echo $view['form']->label($child) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('widget_container_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_container_attributes') ?>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php if ($widget == 'single_text'): ?>
|
||||
<?php echo $view['form']->renderBlock('form_widget_simple'); ?>
|
||||
<?php echo $view['form']->block('form_widget_simple'); ?>
|
||||
<?php else: ?>
|
||||
<div <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<div <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php echo str_replace(array('{{ year }}', '{{ month }}', '{{ day }}'), array(
|
||||
$view['form']->widget($form['year']),
|
||||
$view['form']->widget($form['month']),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php if ($widget == 'single_text'): ?>
|
||||
<?php echo $view['form']->renderBlock('form_widget_simple'); ?>
|
||||
<?php echo $view['form']->block('form_widget_simple'); ?>
|
||||
<?php else: ?>
|
||||
<div <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<div <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php echo $view['form']->widget($form['date']).' '.$view['form']->widget($form['time']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : 'email')) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : 'email')) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_enctype') ?>
|
||||
<?php echo $view['form']->block('form_enctype') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_errors') ?>
|
||||
<?php echo $view['form']->block('form_errors') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_label') ?>
|
||||
<?php echo $view['form']->block('form_label') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_rest') ?>
|
||||
<?php echo $view['form']->block('form_rest') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_row') ?>
|
||||
<?php echo $view['form']->block('form_row') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_rows') ?>
|
||||
<?php echo $view['form']->block('form_rows') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple') ?>
|
||||
<?php echo $view['form']->block('form_widget_simple') ?>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php if ($compound): ?>
|
||||
<?php echo $view['form']->renderBlock('form_widget_compound')?>
|
||||
<?php echo $view['form']->block('form_widget_compound')?>
|
||||
<?php else: ?>
|
||||
<?php echo $view['form']->renderBlock('form_widget_simple')?>
|
||||
<?php echo $view['form']->block('form_widget_simple')?>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<div <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php if (!$form->hasParent() && $errors): ?>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
|
@ -6,6 +6,6 @@
|
|||
</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
<?php echo $view['form']->renderBlock('form_rows') ?>
|
||||
<?php echo $view['form']->block('form_rows') ?>
|
||||
<?php echo $view['form']->rest($form) ?>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<input
|
||||
type="<?php echo isset($type) ? $view->escape($type) : 'text' ?>"
|
||||
<?php if (!empty($value)): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
|
||||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
/>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "hidden")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "hidden")) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "number")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "number")) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo str_replace('{{ widget }}', $view['form']->renderBlock('form_widget_simple'), $money_pattern) ?>
|
||||
<?php echo str_replace('{{ widget }}', $view['form']->block('form_widget_simple'), $money_pattern) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "text")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "text")) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "password")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "password")) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "text")) ?> %
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "text")) ?> %
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<input type="radio"
|
||||
<?php echo $view['form']->renderBlock('widget_attributes') ?>
|
||||
<?php echo $view['form']->block('widget_attributes') ?>
|
||||
value="<?php echo $view->escape($value) ?>"
|
||||
<?php if ($checked): ?> checked="checked"<?php endif ?>
|
||||
/>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_rows') ?>
|
||||
<?php echo $view['form']->block('form_rows') ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "search")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "search")) ?>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<textarea <?php echo $view['form']->renderBlock('widget_attributes') ?>><?php echo $view->escape($value) ?></textarea>
|
||||
<textarea <?php echo $view['form']->block('widget_attributes') ?>><?php echo $view->escape($value) ?></textarea>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php if ($widget == 'single_text'): ?>
|
||||
<?php echo $view['form']->renderBlock('form_widget_simple'); ?>
|
||||
<?php echo $view['form']->block('form_widget_simple'); ?>
|
||||
<?php else: ?>
|
||||
<div <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<div <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php
|
||||
// There should be no spaces between the colons and the widgets, that's why
|
||||
// this block is written in a single PHP tag
|
||||
|
|
|
@ -1 +1 @@
|
|||
<?php echo $view['form']->renderBlock('form_widget_simple', array('type' => isset($type) ? $type : "url")) ?>
|
||||
<?php echo $view['form']->block('form_widget_simple', array('type' => isset($type) ? $type : "url")) ?>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<table <?php echo $view['form']->renderBlock('widget_container_attributes') ?>>
|
||||
<table <?php echo $view['form']->block('widget_container_attributes') ?>>
|
||||
<?php if (!$form->hasParent()): ?>
|
||||
<?php echo $view['form']->errors($form) ?>
|
||||
<?php endif ?>
|
||||
<?php echo $view['form']->renderBlock('form_rows') ?>
|
||||
<?php echo $view['form']->block('form_rows') ?>
|
||||
<?php echo $view['form']->rest($form) ?>
|
||||
</table>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
|
||||
|
||||
use Symfony\Component\Templating\Helper\Helper;
|
||||
use Symfony\Component\Form\FormRendererInterface;
|
||||
use Symfony\Component\Form\FormViewInterface;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
|
@ -28,77 +29,34 @@ use Symfony\Component\Form\Util\FormUtil;
|
|||
class FormHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* @var EngineInterface
|
||||
* @var FormRendererInterface
|
||||
*/
|
||||
private $engine;
|
||||
private $renderer;
|
||||
|
||||
/**
|
||||
* @var CsrfProviderInterface
|
||||
* @param FormRendererInterface $renderer
|
||||
*/
|
||||
private $csrfProvider;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $blockHierarchyMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $currentHierarchyLevelMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $variableMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $stack = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $defaultThemes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $themes = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $templateCache = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $templateHierarchyLevelCache = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param EngineInterface $engine The templating engine
|
||||
* @param CsrfProviderInterface $csrfProvider The CSRF provider
|
||||
* @param array $defaultThemes An array of theme names
|
||||
*/
|
||||
public function __construct(EngineInterface $engine, CsrfProviderInterface $csrfProvider = null, array $defaultThemes = array())
|
||||
public function __construct(FormRendererInterface $renderer)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
$this->csrfProvider = $csrfProvider;
|
||||
$this->defaultThemes = $defaultThemes;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
public function isChoiceGroup($label)
|
||||
{
|
||||
return FormUtil::isChoiceGroup($label);
|
||||
return $this->renderer->isChoiceGroup($label);
|
||||
}
|
||||
|
||||
public function isChoiceSelected(FormViewInterface $view, ChoiceView $choice)
|
||||
{
|
||||
return FormUtil::isChoiceSelected($choice->getValue(), $view->getVar('value'));
|
||||
return $this->renderer->isChoiceSelected($view, $choice);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,9 +69,7 @@ class FormHelper extends Helper
|
|||
*/
|
||||
public function setTheme(FormViewInterface $view, $themes)
|
||||
{
|
||||
$this->themes[$view->getVar('full_block_name')] = (array) $themes;
|
||||
$this->templateCache = array();
|
||||
$this->templateHierarchyLevelCache = array();
|
||||
$this->renderer->setTheme($view, $themes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +85,7 @@ class FormHelper extends Helper
|
|||
*/
|
||||
public function enctype(FormViewInterface $view)
|
||||
{
|
||||
return $this->renderSection($view, 'enctype');
|
||||
return $this->renderer->renderEnctype($view);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,44 +102,40 @@ class FormHelper extends Helper
|
|||
* <?php echo view['form']->widget(array('separator' => '+++++)) ?>
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the widget
|
||||
* @param array $variables Additional variables passed to the template
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function widget(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'widget', $variables);
|
||||
return $this->renderer->renderWidget($view, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the entire form field "row".
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the row
|
||||
* @param array $variables Additional variables passed to the template
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function row(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'row', $variables);
|
||||
return $this->renderer->renderRow($view, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the label of the given view.
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the label
|
||||
* @param string $label The label
|
||||
* @param array $variables Additional variables passed to the template
|
||||
* @param string $label The label
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function label(FormViewInterface $view, $label = null, array $variables = array())
|
||||
{
|
||||
if ($label !== null) {
|
||||
$variables += array('label' => $label);
|
||||
}
|
||||
|
||||
return $this->renderSection($view, 'label', $variables);
|
||||
return $this->renderer->renderLabel($view, $label, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,20 +147,49 @@ class FormHelper extends Helper
|
|||
*/
|
||||
public function errors(FormViewInterface $view)
|
||||
{
|
||||
return $this->renderSection($view, 'errors');
|
||||
return $this->renderer->renderErrors($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders views which have not already been rendered.
|
||||
*
|
||||
* @param FormViewInterface $view The parent view
|
||||
* @param array $variables An array of variables
|
||||
* @param array $variables An array of variables
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function rest(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'rest', $variables);
|
||||
return $this->renderer->renderRest($view, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of {@link block()}
|
||||
*
|
||||
* @param string $block The name of the block to render.
|
||||
* @param array $variables The variable to pass to the template.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @deprecated Deprecated since version 2.1, to be removed in 2.3. Use
|
||||
* {@link block()} instead.
|
||||
*/
|
||||
public function renderBlock($block, array $variables = array())
|
||||
{
|
||||
return $this->block($block, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a block of the template.
|
||||
*
|
||||
* @param string $block The name of the block to render.
|
||||
* @param array $variables The variable to pass to the template.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function block($block, array $variables = array())
|
||||
{
|
||||
return $this->renderer->renderBlock($block, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,304 +219,11 @@ class FormHelper extends Helper
|
|||
*/
|
||||
public function csrfToken($intention)
|
||||
{
|
||||
if (!$this->csrfProvider instanceof CsrfProviderInterface) {
|
||||
throw new \BadMethodCallException('CSRF token can only be generated if a CsrfProviderInterface is injected in the constructor.');
|
||||
}
|
||||
|
||||
return $this->csrfProvider->generateCsrfToken($intention);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* 1. This function first looks for a block named "_<view id>_<section>",
|
||||
* 2. if such a block is not found the function will look for a block named
|
||||
* "<type name>_<section>",
|
||||
* 3. the type name is recursively replaced by the parent type name until a
|
||||
* corresponding block is found
|
||||
*
|
||||
* @param FormViewInterface $view The form view
|
||||
* @param string $section The section to render (i.e. 'row', 'widget', 'label', ...)
|
||||
* @param array $variables Additional variables
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @throws FormException if no template block exists to render the given section of the view
|
||||
*/
|
||||
protected function renderSection(FormViewInterface $view, $section, array $variables = array())
|
||||
{
|
||||
$renderOnlyOnce = in_array($section, array('row', 'widget'));
|
||||
|
||||
if ($renderOnlyOnce && $view->isRendered()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// The cache key for storing the variables and types
|
||||
$mapKey = $view->getVar('full_block_name') . '_' . $section;
|
||||
|
||||
// In templates, we have to deal with two kinds of block hierarchies:
|
||||
//
|
||||
// +---------+ +---------+
|
||||
// | Theme B | -------> | Theme A |
|
||||
// +---------+ +---------+
|
||||
//
|
||||
// form_widget -------> form_widget
|
||||
// ^
|
||||
// |
|
||||
// choice_widget -----> choice_widget
|
||||
//
|
||||
// The first kind of hierarchy is the theme hierarchy. This allows to
|
||||
// override the block "choice_widget" from Theme A in the extending
|
||||
// Theme B. This kind of inheritance needs to be supported by the
|
||||
// template engine and, for example, offers "parent()" or similar
|
||||
// functions to fall back from the custom to the parent implementation.
|
||||
//
|
||||
// The second kind of hierarchy is the form type hierarchy. This allows
|
||||
// to implement a custom "choice_widget" block (no matter in which theme),
|
||||
// or to fallback to the block of the parent type, which would be
|
||||
// "form_widget" in this example (again, no matter in which theme).
|
||||
// If the designer wants to explicitely fallback to "form_widget" in his
|
||||
// custom "choice_widget", for example because he only wants to wrap
|
||||
// a <div> around the original implementation, he can simply call the
|
||||
// widget() function again to render the block for the parent type.
|
||||
//
|
||||
// The second kind is implemented in the following blocks.
|
||||
if (!isset($this->blockHierarchyMap[$mapKey])) {
|
||||
// INITIAL CALL
|
||||
// Calculate the hierarchy of template blocks and start on
|
||||
// the bottom level of the hierarchy (= "_<id>_<section>" block)
|
||||
$blockHierarchy = array_map(function ($type) use ($section) {
|
||||
return $type . '_' . $section;
|
||||
}, $view->getVar('types'));
|
||||
$blockHierarchy[] = $view->getVar('full_block_name') . '_' . $section;
|
||||
$currentHierarchyLevel = count($blockHierarchy) - 1;
|
||||
|
||||
// The default variable scope contains all view variables, merged with
|
||||
// the variables passed explicitely to the helper
|
||||
$variables = array_replace_recursive($view->getVars(), $variables);
|
||||
} else {
|
||||
// RECURSIVE CALL
|
||||
// If a block recursively calls renderSection() again, resume rendering
|
||||
// using the parent type in the hierarchy.
|
||||
$blockHierarchy = $this->blockHierarchyMap[$mapKey];
|
||||
$currentHierarchyLevel = $this->currentHierarchyLevelMap[$mapKey] - 1;
|
||||
|
||||
// Reuse the current scope and merge it with the explicitely passed variables
|
||||
$variables = array_replace_recursive($this->variableMap[$mapKey], $variables);
|
||||
}
|
||||
|
||||
$cacheKey = $view->getVar('full_block_name');
|
||||
$block = $blockHierarchy[$currentHierarchyLevel];
|
||||
|
||||
// Populate the cache if the template for the block is not known yet
|
||||
if (!isset($this->templateCache[$cacheKey][$block])) {
|
||||
$this->loadTemplateForBlockHierarchy($view, $blockHierarchy, $currentHierarchyLevel);
|
||||
}
|
||||
|
||||
// Escape if no template exists for this block
|
||||
if (!$this->templateCache[$cacheKey][$block]) {
|
||||
throw new FormException(sprintf(
|
||||
'Unable to render the form as none of the following blocks exist: "%s".',
|
||||
implode('", "', array_reverse($blockHierarchy))
|
||||
));
|
||||
}
|
||||
|
||||
// If $block was previously rendered manually with renderBlock(), the template
|
||||
// is cached but the hierarchy level is not. In this case, we know that the block
|
||||
// exists at this very hierarchy level (renderBlock() does not traverse the hierarchy)
|
||||
// so we can just set it.
|
||||
if (!isset($this->templateHierarchyLevelCache[$cacheKey][$block])) {
|
||||
$this->templateHierarchyLevelCache[$cacheKey][$block] = $currentHierarchyLevel;
|
||||
}
|
||||
|
||||
// In order to make recursive calls possible, we need to store the block hierarchy,
|
||||
// the current level of the hierarchy and the variables so that this method can
|
||||
// resume rendering one level higher of the hierarchy when it is called recursively.
|
||||
//
|
||||
// We need to store these values in maps (associative arrays) because within a
|
||||
// call to widget() another call to widget() can be made, but for a different view
|
||||
// object. These nested calls should not override each other.
|
||||
$this->blockHierarchyMap[$mapKey] = $blockHierarchy;
|
||||
$this->currentHierarchyLevelMap[$mapKey] = $this->templateHierarchyLevelCache[$cacheKey][$block];
|
||||
$this->variableMap[$mapKey] = $variables;
|
||||
|
||||
// We also need to store the view and the variables so that we can render custom
|
||||
// blocks with renderBlock() using the same themes and variables as in the outer
|
||||
// block.
|
||||
//
|
||||
// A stack is sufficient for this purpose, because renderBlock() always accesses
|
||||
// the immediate next outer scope, which is always stored at the end of the stack.
|
||||
$this->stack[] = array($view, $variables);
|
||||
|
||||
// Do the rendering
|
||||
$html = $this->engine->render($this->templateCache[$cacheKey][$block], $variables);
|
||||
|
||||
// Clear the stack
|
||||
array_pop($this->stack);
|
||||
|
||||
// Clear the maps
|
||||
unset($this->blockHierarchyMap[$mapKey]);
|
||||
unset($this->currentHierarchyLevelMap[$mapKey]);
|
||||
unset($this->variableMap[$mapKey]);
|
||||
|
||||
if ($renderOnlyOnce) {
|
||||
$view->setRendered();
|
||||
}
|
||||
|
||||
return trim($html);
|
||||
}
|
||||
|
||||
public function renderBlock($block, $variables = array())
|
||||
{
|
||||
if (0 == count($this->stack)) {
|
||||
throw new FormException('This method should only be called while rendering a form element.');
|
||||
}
|
||||
|
||||
list($view, $scopeVariables) = end($this->stack);
|
||||
|
||||
$cacheKey = $view->getVar('full_block_name');
|
||||
|
||||
if (!isset($this->templateCache[$cacheKey][$block]) && !$this->loadTemplateForBlock($view, $block)) {
|
||||
throw new FormException(sprintf('No block "%s" found while rendering the form.', $block));
|
||||
}
|
||||
|
||||
$variables = array_replace_recursive($scopeVariables, $variables);
|
||||
|
||||
return trim($this->engine->render($this->templateCache[$cacheKey][$block], $variables));
|
||||
return $this->renderer->renderCsrfToken($intention);
|
||||
}
|
||||
|
||||
public function humanize($text)
|
||||
{
|
||||
return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text))));
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache with the template for a specific level of a block hierarchy.
|
||||
*
|
||||
* For example, the block hierarchy could be:
|
||||
*
|
||||
* <code>
|
||||
* form_widget
|
||||
* choice_widget
|
||||
* entity_widget
|
||||
* </code
|
||||
*
|
||||
* When loading this hierarchy at index 1, the method first tries to find the
|
||||
* block "choice_widget" in any of the themes assigned to $view. If nothing is
|
||||
* found, it then continues to look for "form_widget" and so on.
|
||||
*
|
||||
* This method both stores the template name and the level in the hierarchy at
|
||||
* which the template was found in the cache. In the above example, if the
|
||||
* template "MyBundle:choice_widget.html.php" was found at level 1, this template
|
||||
* and the level "1" are stored. The stored level helps to resume rendering
|
||||
* in recursive calls, where the parent block needs to be rendered (here the
|
||||
* block "form_widget" at level 0).
|
||||
*
|
||||
* @param FormViewInterface $view The form view for finding the applying themes.
|
||||
* @param array $blockHierarchy The block hierarchy, with the most specific block name at the end.
|
||||
* @param integer $currentLevel The level in the block hierarchy that should be loaded.
|
||||
*
|
||||
* @return Boolean True if the cache could be populated successfully, false otherwise.
|
||||
*/
|
||||
private function loadTemplateForBlockHierarchy(FormViewInterface $view, array $blockHierarchy, $currentLevel)
|
||||
{
|
||||
$cacheKey = $view->getVar('full_block_name');
|
||||
$block = $blockHierarchy[$currentLevel];
|
||||
|
||||
// Try to find a template for that block
|
||||
if ($this->loadTemplateForBlock($view, $block)) {
|
||||
// If loadTemplateForBlock() returns true, it was able to populate the
|
||||
// cache. The only missing thing is to set the hierarchy level at which
|
||||
// the template was found.
|
||||
$this->templateHierarchyLevelCache[$cacheKey][$block] = $currentLevel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($currentLevel > 0) {
|
||||
$parentLevel = $currentLevel - 1;
|
||||
$parentBlock = $blockHierarchy[$parentLevel];
|
||||
|
||||
if ($this->loadTemplateForBlockHierarchy($view, $blockHierarchy, $parentLevel)) {
|
||||
// Cache the shortcuts for further accesses
|
||||
$this->templateCache[$cacheKey][$block] = $this->templateCache[$cacheKey][$parentBlock];
|
||||
$this->templateHierarchyLevelCache[$cacheKey][$block] = $this->templateHierarchyLevelCache[$cacheKey][$parentBlock];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the result for further accesses
|
||||
$this->templateCache[$cacheKey][$block] = false;
|
||||
$this->templateHierarchyLevelCache[$cacheKey][$block] = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache with the template for a given block name.
|
||||
*
|
||||
* The template is first searched in all the themes assigned to $view. If nothing
|
||||
* is found, the search is continued in the themes of the parent view. Once arrived
|
||||
* at the root view, if still nothing has been found, the default themes stored
|
||||
* in this class are searched.
|
||||
*
|
||||
* @param FormViewInterface $view The form view for finding the applying themes.
|
||||
* @param string $block The name of the block to load.
|
||||
*
|
||||
* @return Boolean True if the cache could be populated successfully, false otherwise.
|
||||
*/
|
||||
private function loadTemplateForBlock(FormViewInterface $view, $block)
|
||||
{
|
||||
// Recursively try to find the block in the themes assigned to $view,
|
||||
// then of its parent form, then of the parent form of the parent and so on.
|
||||
// When the root form is reached in this recursion, also the default
|
||||
// themes are taken into account.
|
||||
$cacheKey = $view->getVar('full_block_name');
|
||||
|
||||
// Check the default themes once we reach the root form without success
|
||||
$themes = $view->hasParent() ? array() : $this->defaultThemes;
|
||||
|
||||
// Add the themes that have been registered for that specific element
|
||||
if (isset($this->themes[$cacheKey])) {
|
||||
$themes = array_merge($themes, $this->themes[$cacheKey]);
|
||||
}
|
||||
|
||||
// Check each theme whether it contains the searched block
|
||||
for ($i = count($themes) - 1; $i >= 0; --$i) {
|
||||
if ($this->engine->exists($templateName = $themes[$i] . ':' . $block . '.html.php')) {
|
||||
$this->templateCache[$cacheKey][$block] = $templateName;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not find anything in the themes of the current view, proceed
|
||||
// with the themes of the parent view
|
||||
if ($view->hasParent()) {
|
||||
$parentCacheKey = $view->getParent()->getVar('full_block_name');
|
||||
|
||||
if (!isset($this->templateCache[$parentCacheKey][$block])) {
|
||||
$this->loadTemplateForBlock($view->getParent(), $block);
|
||||
}
|
||||
|
||||
// If a template exists in the parent themes, cache that template name
|
||||
// for the current theme as well to speed up further accesses
|
||||
if ($this->templateCache[$parentCacheKey][$block]) {
|
||||
$this->templateCache[$cacheKey][$block] = $this->templateCache[$parentCacheKey][$block];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache that we didn't find anything to speed up further accesses
|
||||
$this->templateCache[$cacheKey][$block] = false;
|
||||
|
||||
return false;
|
||||
return $this->renderer->humanize($text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,11 @@ use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper;
|
|||
use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Templating\PhpEngine;
|
||||
use Symfony\Component\Templating\Loader\FilesystemLoader;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\FormRenderer;
|
||||
use Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine;
|
||||
use Symfony\Component\Form\Tests\AbstractDivLayoutTest;
|
||||
|
||||
class FormHelperDivLayoutTest extends AbstractDivLayoutTest
|
||||
|
@ -34,8 +36,10 @@ class FormHelperDivLayoutTest extends AbstractDivLayoutTest
|
|||
$loader = new FilesystemLoader(array());
|
||||
$engine = new PhpEngine($templateNameParser, $loader);
|
||||
$engine->addGlobal('global', '');
|
||||
$rendererEngine = new TemplatingRendererEngine($engine, array('FrameworkBundle:Form'));
|
||||
$renderer = new FormRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
|
||||
|
||||
$this->helper = new FormHelper($engine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'), array('FrameworkBundle:Form'));
|
||||
$this->helper = new FormHelper($renderer);
|
||||
|
||||
$engine->setHelpers(array(
|
||||
$this->helper,
|
||||
|
|
|
@ -16,9 +16,11 @@ use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper;
|
|||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\FormRenderer;
|
||||
use Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine;
|
||||
use Symfony\Component\Form\Tests\AbstractTableLayoutTest;
|
||||
use Symfony\Component\Templating\PhpEngine;
|
||||
use Symfony\Component\Templating\Loader\FilesystemLoader;
|
||||
use Symfony\Component\Form\Tests\AbstractTableLayoutTest;
|
||||
|
||||
class FormHelperTableLayoutTest extends AbstractTableLayoutTest
|
||||
{
|
||||
|
@ -34,11 +36,13 @@ class FormHelperTableLayoutTest extends AbstractTableLayoutTest
|
|||
$loader = new FilesystemLoader(array());
|
||||
$engine = new PhpEngine($templateNameParser, $loader);
|
||||
$engine->addGlobal('global', '');
|
||||
|
||||
$this->helper = new FormHelper($engine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'), array(
|
||||
$rendererEngine = new TemplatingRendererEngine($engine, array(
|
||||
'FrameworkBundle:Form',
|
||||
'FrameworkBundle:FormTable'
|
||||
));
|
||||
$renderer = new FormRenderer($rendererEngine, $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'));
|
||||
|
||||
$this->helper = new FormHelper($renderer);
|
||||
|
||||
$engine->setHelpers(array(
|
||||
$this->helper,
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
<parameter key="twig.extension.routing.class">Symfony\Bridge\Twig\Extension\RoutingExtension</parameter>
|
||||
<parameter key="twig.extension.yaml.class">Symfony\Bridge\Twig\Extension\YamlExtension</parameter>
|
||||
<parameter key="twig.extension.form.class">Symfony\Bridge\Twig\Extension\FormExtension</parameter>
|
||||
<parameter key="twig.form.engine.class">Symfony\Bridge\Twig\Form\TwigRendererEngine</parameter>
|
||||
<parameter key="twig.form.renderer.class">Symfony\Bridge\Twig\Form\TwigRenderer</parameter>
|
||||
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
|
||||
<parameter key="twig.exception_listener.class">Symfony\Component\HttpKernel\EventListener\ExceptionListener</parameter>
|
||||
</parameters>
|
||||
|
@ -75,10 +77,18 @@
|
|||
|
||||
<service id="twig.extension.form" class="%twig.extension.form.class%" public="false">
|
||||
<tag name="twig.extension" />
|
||||
<argument type="service" id="form.csrf_provider" on-invalid="null" />
|
||||
<argument type="service" id="twig.form.renderer" />
|
||||
</service>
|
||||
|
||||
<service id="twig.form.engine" class="%twig.form.engine.class%" public="false">
|
||||
<argument>%twig.form.resources%</argument>
|
||||
</service>
|
||||
|
||||
<service id="twig.form.renderer" class="%twig.form.renderer.class%" public="false">
|
||||
<argument type="service" id="twig.form.engine" />
|
||||
<argument type="service" id="form.csrf_provider" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="twig.translation.extractor" class="%twig.translation.extractor.class%">
|
||||
<argument type="service" id="twig" />
|
||||
<tag name="translation.extractor" alias="twig" />
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
<?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\Component\Form;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FormRendererEngineInterface}.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class AbstractRendererEngine implements FormRendererEngineInterface
|
||||
{
|
||||
/**
|
||||
* The variable in {@link FormViewInterface} used as cache key.
|
||||
*/
|
||||
const CACHE_KEY_VAR = 'full_block_name';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultThemes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $themes = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $resources = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $resourceHierarchyLevels = array();
|
||||
|
||||
/**
|
||||
* Creates a new renderer engine.
|
||||
*
|
||||
* @param array $defaultThemes The default themes. The type of these
|
||||
* themes is open to the implementation.
|
||||
*/
|
||||
public function __construct(array $defaultThemes = array())
|
||||
{
|
||||
$this->defaultThemes = $defaultThemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTheme(FormViewInterface $view, $themes)
|
||||
{
|
||||
$cacheKey = $view->getVar(self::CACHE_KEY_VAR);
|
||||
|
||||
// Do not cast, as casting turns objects into arrays of properties
|
||||
$this->themes[$cacheKey] = is_array($themes) ? $themes : array($themes);
|
||||
$this->resources[$cacheKey] = array();
|
||||
$this->resourceHierarchyLevels[$cacheKey] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResourceForBlock(FormViewInterface $view, $block)
|
||||
{
|
||||
$cacheKey = $view->getVar(self::CACHE_KEY_VAR);
|
||||
|
||||
if (!isset($this->resources[$cacheKey][$block])) {
|
||||
$this->loadResourceForBlock($cacheKey, $view, $block);
|
||||
}
|
||||
|
||||
return $this->resources[$cacheKey][$block];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResourceForBlockHierarchy(FormViewInterface $view, array $blockHierarchy, $hierarchyLevel)
|
||||
{
|
||||
$cacheKey = $view->getVar(self::CACHE_KEY_VAR);
|
||||
$block = $blockHierarchy[$hierarchyLevel];
|
||||
|
||||
if (!isset($this->resources[$cacheKey][$block])) {
|
||||
$this->loadResourceForBlockHierarchy($cacheKey, $view, $blockHierarchy, $hierarchyLevel);
|
||||
}
|
||||
|
||||
return $this->resources[$cacheKey][$block];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResourceHierarchyLevel(FormViewInterface $view, array $blockHierarchy, $hierarchyLevel)
|
||||
{
|
||||
$cacheKey = $view->getVar(self::CACHE_KEY_VAR);
|
||||
$block = $blockHierarchy[$hierarchyLevel];
|
||||
|
||||
if (!isset($this->resources[$cacheKey][$block])) {
|
||||
$this->loadResourceForBlockHierarchy($cacheKey, $view, $blockHierarchy, $hierarchyLevel);
|
||||
}
|
||||
|
||||
// If $block was previously rendered loaded with loadTemplateForBlock(), the template
|
||||
// is cached but the hierarchy level is not. In this case, we know that the block
|
||||
// exists at this very hierarchy level, so we can just set it.
|
||||
if (!isset($this->resourceHierarchyLevels[$cacheKey][$block])) {
|
||||
$this->resourceHierarchyLevels[$cacheKey][$block] = $hierarchyLevel;
|
||||
}
|
||||
|
||||
return $this->resourceHierarchyLevels[$cacheKey][$block];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache with the resource for a given block name.
|
||||
*
|
||||
* @see getResourceForBlock()
|
||||
*
|
||||
* @param string $cacheKey The cache key of the form view.
|
||||
* @param FormViewInterface $view The form view for finding the applying themes.
|
||||
* @param string $block The name of the block to load.
|
||||
*
|
||||
* @return Boolean True if the resource could be loaded, false otherwise.
|
||||
*/
|
||||
abstract protected function loadResourceForBlock($cacheKey, FormViewInterface $view, $block);
|
||||
|
||||
/**
|
||||
* Loads the cache with the resource for a specific level of a block hierarchy.
|
||||
*
|
||||
* @see getResourceForBlockHierarchy()
|
||||
*
|
||||
* @param string $cacheKey The cache key used for storing the
|
||||
* resource.
|
||||
* @param FormViewInterface $view The form view for finding the applying
|
||||
* themes.
|
||||
* @param array $blockHierarchy The block hierarchy, with the most
|
||||
* specific block name at the end.
|
||||
* @param integer $hierarchyLevel The level in the block hierarchy that
|
||||
* should be loaded.
|
||||
*
|
||||
* @return Boolean True if the resource could be loaded, false otherwise.
|
||||
*/
|
||||
private function loadResourceForBlockHierarchy($cacheKey, FormViewInterface $view, array $blockHierarchy, $hierarchyLevel)
|
||||
{
|
||||
$block = $blockHierarchy[$hierarchyLevel];
|
||||
|
||||
// Try to find a template for that block
|
||||
if ($this->loadResourceForBlock($cacheKey, $view, $block)) {
|
||||
// If loadTemplateForBlock() returns true, it was able to populate the
|
||||
// cache. The only missing thing is to set the hierarchy level at which
|
||||
// the template was found.
|
||||
$this->resourceHierarchyLevels[$cacheKey][$block] = $hierarchyLevel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($hierarchyLevel > 0) {
|
||||
$parentLevel = $hierarchyLevel - 1;
|
||||
$parentBlock = $blockHierarchy[$parentLevel];
|
||||
|
||||
// The next two if statements contain slightly duplicated code. This is by intention
|
||||
// and tries to avoid execution of unnecessary checks in order to increase performance.
|
||||
|
||||
if (isset($this->resources[$cacheKey][$parentBlock])) {
|
||||
// It may happen that the parent block is already loaded, but its level is not.
|
||||
// In this case, the parent block must have been loaded by loadResourceForBlock(),
|
||||
// which does not check the hierarchy of the block. Subsequently the block must have
|
||||
// been found directly on the parent level.
|
||||
if (!isset($this->resourceHierarchyLevels[$cacheKey][$parentBlock])) {
|
||||
$this->resourceHierarchyLevels[$cacheKey][$parentBlock] = $parentLevel;
|
||||
}
|
||||
|
||||
// Cache the shortcuts for further accesses
|
||||
$this->resources[$cacheKey][$block] = $this->resources[$cacheKey][$parentBlock];
|
||||
$this->resourceHierarchyLevels[$cacheKey][$block] = $this->resourceHierarchyLevels[$cacheKey][$parentBlock];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->loadResourceForBlockHierarchy($cacheKey, $view, $blockHierarchy, $parentLevel)) {
|
||||
// Cache the shortcuts for further accesses
|
||||
$this->resources[$cacheKey][$block] = $this->resources[$cacheKey][$parentBlock];
|
||||
$this->resourceHierarchyLevels[$cacheKey][$block] = $this->resourceHierarchyLevels[$cacheKey][$parentBlock];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the result for further accesses
|
||||
$this->resources[$cacheKey][$block] = false;
|
||||
$this->resourceHierarchyLevels[$cacheKey][$block] = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -31,7 +31,6 @@ CHANGELOG
|
|||
* ArrayToChoicesTransformer to ChoicesToValuesTransformer
|
||||
* ScalarToChoiceTransformer to ChoiceToValueTransformer
|
||||
to be consistent with the naming in ChoiceListInterface.
|
||||
* [BC BREAK] removed FormUtil::toArrayKey() and FormUtil::toArrayKeys().
|
||||
They were merged into ChoiceList and have no public equivalent anymore.
|
||||
* choice fields now throw a FormException if neither the "choices" nor the
|
||||
"choice_list" option is set
|
||||
|
@ -172,4 +171,11 @@ CHANGELOG
|
|||
* ChoiceType now caches its created choice lists to improve performance
|
||||
* [BC BREAK] Rows of a collection field cannot be themed individually anymore. All rows in the collection
|
||||
field now have the same block names, which contains "entry" where it previously contained the row index.
|
||||
* [BC BREAK] When registering a type through the DI extension, the tag alias has to match the actual type name.
|
||||
* [BC BREAK] When registering a type through the DI extension, the tag alias has to match the actual type name.
|
||||
* added FormRendererInterface, FormRendererEngineInterface and implementations of these interfaces
|
||||
* [BC BREAK] removed the following methods from FormUtil:
|
||||
* `toArrayKey`
|
||||
* `toArrayKeys`
|
||||
* `isChoiceGroup`
|
||||
* `isChoiceSelected`
|
||||
* added method `block` to FormHelper and deprecated `renderBlock` instead
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<?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\Component\Form\Extension\Templating;
|
||||
|
||||
use Symfony\Component\Form\AbstractRendererEngine;
|
||||
use Symfony\Component\Form\FormViewInterface;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class TemplatingRendererEngine extends AbstractRendererEngine
|
||||
{
|
||||
/**
|
||||
* @var EngineInterface
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
public function __construct(EngineInterface $engine, array $defaultThemes = array())
|
||||
{
|
||||
parent::__construct($defaultThemes);
|
||||
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderBlock(FormViewInterface $view, $resource, $block, array $variables = array())
|
||||
{
|
||||
return trim($this->engine->render($resource, $variables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache with the resource for a given block name.
|
||||
*
|
||||
* This implementation tries to load as few blocks as possible, since each block
|
||||
* is represented by a template on the file system.
|
||||
*
|
||||
* @see getResourceForBlock()
|
||||
*
|
||||
* @param string $cacheKey The cache key of the form view.
|
||||
* @param FormViewInterface $view The form view for finding the applying themes.
|
||||
* @param string $block The name of the block to load.
|
||||
*
|
||||
* @return Boolean True if the resource could be loaded, false otherwise.
|
||||
*/
|
||||
protected function loadResourceForBlock($cacheKey, FormViewInterface $view, $block)
|
||||
{
|
||||
// Recursively try to find the block in the themes assigned to $view,
|
||||
// then of its parent form, then of the parent form of the parent and so on.
|
||||
// When the root form is reached in this recursion, also the default
|
||||
// themes are taken into account.
|
||||
|
||||
// Check each theme whether it contains the searched block
|
||||
if (isset($this->themes[$cacheKey])) {
|
||||
for ($i = count($this->themes[$cacheKey]) - 1; $i >= 0; --$i) {
|
||||
if ($this->loadResourceFromTheme($cacheKey, $block, $this->themes[$cacheKey][$i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the default themes once we reach the root form without success
|
||||
if (!$view->hasParent()) {
|
||||
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
|
||||
if ($this->loadResourceFromTheme($cacheKey, $block, $this->defaultThemes[$i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not find anything in the themes of the current view, proceed
|
||||
// with the themes of the parent view
|
||||
if ($view->hasParent()) {
|
||||
$parentCacheKey = $view->getParent()->getVar(self::CACHE_KEY_VAR);
|
||||
|
||||
if (!isset($this->resources[$parentCacheKey][$block])) {
|
||||
$this->loadResourceForBlock($parentCacheKey, $view->getParent(), $block);
|
||||
}
|
||||
|
||||
// If a template exists in the parent themes, cache that template
|
||||
// for the current theme as well to speed up further accesses
|
||||
if ($this->resources[$parentCacheKey][$block]) {
|
||||
$this->resources[$cacheKey][$block] = $this->resources[$parentCacheKey][$block];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache that we didn't find anything to speed up further accesses
|
||||
$this->resources[$cacheKey][$block] = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the resource for a block from a theme.
|
||||
*
|
||||
* @param string $cacheKey The cache key for storing the resource.
|
||||
* @param string $block The name of the block to load a resource for.
|
||||
* @param mixed $theme The theme to load the block from.
|
||||
*
|
||||
* @return Boolean True if the resource could be loaded, false otherwise.
|
||||
*/
|
||||
protected function loadResourceFromTheme($cacheKey, $block, $theme)
|
||||
{
|
||||
if ($this->engine->exists($templateName = $theme . ':' . $block . '.html.php')) {
|
||||
$this->resources[$cacheKey][$block] = $templateName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
<?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\Component\Form;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
|
||||
|
||||
/**
|
||||
* Renders a form into HTML using a rendering engine.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormRenderer implements FormRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var FormRendererEngineInterface
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
/**
|
||||
* @var CsrfProviderInterface
|
||||
*/
|
||||
private $csrfProvider;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $blockHierarchyMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $hierarchyLevelMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $variableMap = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $stack = array();
|
||||
|
||||
public function __construct(FormRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
$this->csrfProvider = $csrfProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEngine()
|
||||
{
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTheme(FormViewInterface $view, $themes)
|
||||
{
|
||||
$this->engine->setTheme($view, $themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderEnctype(FormViewInterface $view)
|
||||
{
|
||||
return $this->renderSection($view, 'enctype');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderRow(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'row', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderRest(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'rest', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderWidget(FormViewInterface $view, array $variables = array())
|
||||
{
|
||||
return $this->renderSection($view, 'widget', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderErrors(FormViewInterface $view)
|
||||
{
|
||||
return $this->renderSection($view, 'errors');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderLabel(FormViewInterface $view, $label = null, array $variables = array())
|
||||
{
|
||||
if ($label !== null) {
|
||||
$variables += array('label' => $label);
|
||||
}
|
||||
|
||||
return $this->renderSection($view, 'label', $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderCsrfToken($intention)
|
||||
{
|
||||
if (null === $this->csrfProvider) {
|
||||
throw new \BadMethodCallException('CSRF token can only be generated if a CsrfProviderInterface is injected in the constructor.');
|
||||
}
|
||||
|
||||
return $this->csrfProvider->generateCsrfToken($intention);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderBlock($block, array $variables = array())
|
||||
{
|
||||
if (0 == count($this->stack)) {
|
||||
throw new FormException('This method should only be called while rendering a form element.');
|
||||
}
|
||||
|
||||
list($view, $scopeVariables) = end($this->stack);
|
||||
|
||||
$resource = $this->engine->getResourceForBlock($view, $block);
|
||||
|
||||
if (!$resource) {
|
||||
throw new FormException(sprintf('No block "%s" found while rendering the form.', $block));
|
||||
}
|
||||
|
||||
$variables = array_replace_recursive($scopeVariables, $variables);
|
||||
|
||||
return $this->engine->renderBlock($view, $resource, $block, $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isChoiceGroup($choice)
|
||||
{
|
||||
return is_array($choice) || $choice instanceof \Traversable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isChoiceSelected(FormViewInterface $view, ChoiceView $choice)
|
||||
{
|
||||
$value = $view->getVar('value');
|
||||
$choiceValue = $choice->getValue();
|
||||
|
||||
if (is_array($value)) {
|
||||
return false !== array_search($choiceValue, $value, true);
|
||||
}
|
||||
|
||||
return $choiceValue === $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function humanize($text)
|
||||
{
|
||||
return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given section of a form view.
|
||||
*
|
||||
* @param FormViewInterface $view The form view.
|
||||
* @param string $section The name of the section to render.
|
||||
* @param array $variables The variables to pass to the template.
|
||||
*
|
||||
* @return string The HTML markup.
|
||||
*
|
||||
* @throws Exception\FormException If no fitting template was found.
|
||||
*/
|
||||
protected function renderSection(FormViewInterface $view, $section, array $variables = array())
|
||||
{
|
||||
$renderOnlyOnce = in_array($section, array('row', 'widget'));
|
||||
|
||||
if ($renderOnlyOnce && $view->isRendered()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// The cache key for storing the variables and types
|
||||
$mapKey = $uniqueBlockName = $view->getVar('full_block_name') . '_' . $section;
|
||||
|
||||
// In templates, we have to deal with two kinds of block hierarchies:
|
||||
//
|
||||
// +---------+ +---------+
|
||||
// | Theme B | -------> | Theme A |
|
||||
// +---------+ +---------+
|
||||
//
|
||||
// form_widget -------> form_widget
|
||||
// ^
|
||||
// |
|
||||
// choice_widget -----> choice_widget
|
||||
//
|
||||
// The first kind of hierarchy is the theme hierarchy. This allows to
|
||||
// override the block "choice_widget" from Theme A in the extending
|
||||
// Theme B. This kind of inheritance needs to be supported by the
|
||||
// template engine and, for example, offers "parent()" or similar
|
||||
// functions to fall back from the custom to the parent implementation.
|
||||
//
|
||||
// The second kind of hierarchy is the form type hierarchy. This allows
|
||||
// to implement a custom "choice_widget" block (no matter in which theme),
|
||||
// or to fallback to the block of the parent type, which would be
|
||||
// "form_widget" in this example (again, no matter in which theme).
|
||||
// If the designer wants to explicitely fallback to "form_widget" in his
|
||||
// custom "choice_widget", for example because he only wants to wrap
|
||||
// a <div> around the original implementation, he can simply call the
|
||||
// widget() function again to render the block for the parent type.
|
||||
//
|
||||
// The second kind is implemented in the following blocks.
|
||||
if (!isset($this->blockHierarchyMap[$mapKey])) {
|
||||
// INITIAL CALL
|
||||
// Calculate the hierarchy of template blocks and start on
|
||||
// the bottom level of the hierarchy (= "_<id>_<section>" block)
|
||||
$blockHierarchy = array();
|
||||
foreach ($view->getVar('types') as $type) {
|
||||
$blockHierarchy[] = $type . '_' . $section;
|
||||
}
|
||||
$blockHierarchy[] = $uniqueBlockName;
|
||||
$hierarchyLevel = count($blockHierarchy) - 1;
|
||||
|
||||
// The default variable scope contains all view variables, merged with
|
||||
// the variables passed explicitely to the helper
|
||||
$variables = array_replace_recursive($view->getVars(), $variables);
|
||||
} else {
|
||||
// RECURSIVE CALL
|
||||
// If a block recursively calls renderSection() again, resume rendering
|
||||
// using the parent type in the hierarchy.
|
||||
$blockHierarchy = $this->blockHierarchyMap[$mapKey];
|
||||
$hierarchyLevel = $this->hierarchyLevelMap[$mapKey] - 1;
|
||||
|
||||
// Reuse the current scope and merge it with the explicitely passed variables
|
||||
$variables = array_replace_recursive($this->variableMap[$mapKey], $variables);
|
||||
}
|
||||
|
||||
// Load the resource where this block can be found
|
||||
$resource = $this->engine->getResourceForBlockHierarchy($view, $blockHierarchy, $hierarchyLevel);
|
||||
|
||||
// Update the current hierarchy level to the one at which the resource was
|
||||
// found. For example, if looking for "choice_widget", but only a resource
|
||||
// is found for its parent "form_widget", then the level is updated here
|
||||
// to the parent level.
|
||||
$hierarchyLevel = $this->engine->getResourceHierarchyLevel($view, $blockHierarchy, $hierarchyLevel);
|
||||
|
||||
// The actually existing block name in $resource
|
||||
$block = $blockHierarchy[$hierarchyLevel];
|
||||
|
||||
// Escape if no resource exists for this block
|
||||
if (!$resource) {
|
||||
throw new FormException(sprintf(
|
||||
'Unable to render the form as none of the following blocks exist: "%s".',
|
||||
implode('", "', array_reverse($blockHierarchy))
|
||||
));
|
||||
}
|
||||
|
||||
// In order to make recursive calls possible, we need to store the block hierarchy,
|
||||
// the current level of the hierarchy and the variables so that this method can
|
||||
// resume rendering one level higher of the hierarchy when it is called recursively.
|
||||
//
|
||||
// We need to store these values in maps (associative arrays) because within a
|
||||
// call to widget() another call to widget() can be made, but for a different view
|
||||
// object. These nested calls should not override each other.
|
||||
$this->blockHierarchyMap[$mapKey] = $blockHierarchy;
|
||||
$this->hierarchyLevelMap[$mapKey] = $hierarchyLevel;
|
||||
$this->variableMap[$mapKey] = $variables;
|
||||
|
||||
// We also need to store the view and the variables so that we can render custom
|
||||
// blocks with renderBlock() using the same themes and variables as in the outer
|
||||
// block.
|
||||
//
|
||||
// A stack is sufficient for this purpose, because renderBlock() always accesses
|
||||
// the immediate next outer scope, which is always stored at the end of the stack.
|
||||
$this->stack[] = array($view, $variables);
|
||||
|
||||
// Do the rendering
|
||||
$html = $this->engine->renderBlock($view, $resource, $block, $variables);
|
||||
|
||||
// Clear the stack
|
||||
array_pop($this->stack);
|
||||
|
||||
// Clear the maps
|
||||
unset($this->blockHierarchyMap[$mapKey]);
|
||||
unset($this->hierarchyLevelMap[$mapKey]);
|
||||
unset($this->variableMap[$mapKey]);
|
||||
|
||||
if ($renderOnlyOnce) {
|
||||
$view->setRendered();
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<?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\Component\Form;
|
||||
|
||||
/**
|
||||
* Adapter for rendering form templates with a specific templating engine.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormRendererEngineInterface
|
||||
{
|
||||
/**
|
||||
* Sets the theme(s) to be used for rendering a view and its children.
|
||||
*
|
||||
* @param FormViewInterface $view The view to assign the theme(s) to.
|
||||
* @param mixed $themes The theme(s). The type of these themes
|
||||
* is open to the implementation.
|
||||
*/
|
||||
public function setTheme(FormViewInterface $view, $themes);
|
||||
|
||||
/**
|
||||
* Returns the resource for a block name.
|
||||
*
|
||||
* The resource is first searched in the themes attached to $view, then
|
||||
* in the themes of its parent view and so on, until a resource was found.
|
||||
*
|
||||
* The type of the resource is decided by the implementation. The resource
|
||||
* is later passed to {@link renderBlock()} by the rendering algorithm.
|
||||
*
|
||||
* @param FormViewInterface $view The view for determining the used themes.
|
||||
* First the themes attached directly to the
|
||||
* view with {@link setTheme()} are considered,
|
||||
* then the ones of its parent etc.
|
||||
* @param string $block The name of the block to render.
|
||||
*
|
||||
* @return mixed The renderer resource or false, if none was found.
|
||||
*/
|
||||
public function getResourceForBlock(FormViewInterface $view, $block);
|
||||
|
||||
/**
|
||||
* Returns the resource for a block hierarchy.
|
||||
*
|
||||
* A block hierarchy is an array which starts with the root of the hierarchy
|
||||
* and continues with the child of that root, the child of that child etc.
|
||||
* The following is an example for a block hierarchy:
|
||||
*
|
||||
* <code>
|
||||
* form_widget
|
||||
* text_widget
|
||||
* url_widget
|
||||
* </code>
|
||||
*
|
||||
* In this example, "url_widget" is the most specific block, while the other
|
||||
* blocks are its ancestors in the hierarchy.
|
||||
*
|
||||
* The second parameter $hierarchyLevel determines the level of the hierarchy
|
||||
* that should be rendered. For example, if $hierarchyLevel is 2 for the
|
||||
* above hierarchy, the engine will first look for the block "url_widget",
|
||||
* then, if that does not exist, for the block "text_widget" etc.
|
||||
*
|
||||
* The type of the resource is decided by the implementation. The resource
|
||||
* is later passed to {@link renderBlock()} by the rendering algorithm.
|
||||
*
|
||||
* @param FormViewInterface $view The view for determining the used
|
||||
* themes. First the themes attached
|
||||
* directly to the view with
|
||||
* {@link setTheme()} are considered,
|
||||
* then the ones of its parent etc.
|
||||
* @param array $blockHierarchy The block name hierarchy, with
|
||||
* the root block at the beginning.
|
||||
* @param integer $hierarchyLevel The level in the hierarchy at
|
||||
* which to start looking. Level 0
|
||||
* indicates the root block, i.e.
|
||||
* the first element of $blockHierarchy.
|
||||
*
|
||||
* @return mixed The renderer resource or false, if none was found.
|
||||
*/
|
||||
public function getResourceForBlockHierarchy(FormViewInterface $view, array $blockHierarchy, $hierarchyLevel);
|
||||
|
||||
/**
|
||||
* Returns the hierarchy level at which a resource can be found.
|
||||
*
|
||||
* A block hierarchy is an array which starts with the root of the hierarchy
|
||||
* and continues with the child of that root, the child of that child etc.
|
||||
* The following is an example for a block hierarchy:
|
||||
*
|
||||
* <code>
|
||||
* form_widget
|
||||
* text_widget
|
||||
* url_widget
|
||||
* </code>
|
||||
*
|
||||
* The second parameter $hierarchyLevel determines the level of the hierarchy
|
||||
* that should be rendered.
|
||||
*
|
||||
* If we call this method with the hierarchy level 2, the engine will first
|
||||
* look for a resource for block "url_widget". If such a resource exists,
|
||||
* the method returns 2. Otherwise it tries to find a resource for block
|
||||
* "text_widget" (at level 1) and, again, returns 1 if a resource was found.
|
||||
* The method continues to look for resources until the root level was
|
||||
* reached and nothing was found. In this case false is returned.
|
||||
*
|
||||
* The type of the resource is decided by the implementation. The resource
|
||||
* is later passed to {@link renderBlock()} by the rendering algorithm.
|
||||
*
|
||||
* @param FormViewInterface $view The view for determining the used
|
||||
* themes. First the themes attached
|
||||
* directly to the view with
|
||||
* {@link setTheme()} are considered,
|
||||
* then the ones of its parent etc.
|
||||
* @param array $blockHierarchy The block name hierarchy, with
|
||||
* the root block at the beginning.
|
||||
* @param integer $hierarchyLevel The level in the hierarchy at
|
||||
* which to start looking. Level 0
|
||||
* indicates the root block, i.e.
|
||||
* the first element of $blockHierarchy.
|
||||
*
|
||||
* @return integer|Boolean The hierarchy level or false, if no resource was found.
|
||||
*/
|
||||
public function getResourceHierarchyLevel(FormViewInterface $view, array $blockHierarchy, $hierarchyLevel);
|
||||
|
||||
/**
|
||||
* Renders a block in the given renderer resource.
|
||||
*
|
||||
* The resource can be obtained by calling {@link getResourceForBlock()}
|
||||
* or {@link getResourceForBlockHierarchy()}. The type of the resource is
|
||||
* decided by the implementation.
|
||||
*
|
||||
* @param FormViewInterface $view The view to render.
|
||||
* @param mixed $resource The renderer resource.
|
||||
* @param string $block The name of the block to render.
|
||||
* @param array $variables The variables to pass to the template.
|
||||
*
|
||||
* @return string The HTML markup.
|
||||
*/
|
||||
public function renderBlock(FormViewInterface $view, $resource, $block, array $variables = array());
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
<?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\Component\Form;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
|
||||
/**
|
||||
* Renders a form into HTML.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormRendererInterface
|
||||
{
|
||||
/**
|
||||
* Returns the engine used by this renderer.
|
||||
*
|
||||
* @return FormRendererEngineInterface The renderer engine.
|
||||
*/
|
||||
public function getEngine();
|
||||
|
||||
/**
|
||||
* Sets the theme(s) to be used for rendering a view and its children.
|
||||
*
|
||||
* @param FormViewInterface $view The view to assign the theme(s) to.
|
||||
* @param mixed $themes The theme(s). The type of these themes
|
||||
* is open to the implementation.
|
||||
*/
|
||||
public function setTheme(FormViewInterface $view, $themes);
|
||||
|
||||
/**
|
||||
* Renders the HTML enctype in the form tag, if necessary.
|
||||
*
|
||||
* Example usage templates:
|
||||
*
|
||||
* <form action="..." method="post" <?php echo $renderer->renderEnctype($form) ?>>
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the encoding type
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderEnctype(FormViewInterface $view);
|
||||
|
||||
/**
|
||||
* Renders the entire row for a form field.
|
||||
*
|
||||
* A row typically contains the label, errors and widget of a field.
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the row
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderRow(FormViewInterface $view, array $variables = array());
|
||||
|
||||
/**
|
||||
* Renders views which have not already been rendered.
|
||||
*
|
||||
* @param FormViewInterface $view The parent view
|
||||
* @param array $variables An array of variables
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderRest(FormViewInterface $view, array $variables = array());
|
||||
|
||||
/**
|
||||
* Renders the HTML for a given view.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* <?php echo $renderer->renderWidget($form) ?>
|
||||
*
|
||||
* You can pass options during the call:
|
||||
*
|
||||
* <?php echo $renderer->renderWidget($form, array('attr' => array('class' => 'foo'))) ?>
|
||||
*
|
||||
* <?php echo $renderer->renderWidget($form, array('separator' => '+++++)) ?>
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the widget
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderWidget(FormViewInterface $view, array $variables = array());
|
||||
|
||||
/**
|
||||
* Renders the errors of the given view.
|
||||
*
|
||||
* @param FormViewInterface $view The view to render the errors for
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderErrors(FormViewInterface $view);
|
||||
|
||||
/**
|
||||
* Renders the label of the given view.
|
||||
*
|
||||
* @param FormViewInterface $view The view for which to render the label
|
||||
* @param string $label The label
|
||||
* @param array $variables Additional variables passed to the template
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderLabel(FormViewInterface $view, $label = null, array $variables = array());
|
||||
|
||||
/**
|
||||
* Renders a named block of the form theme.
|
||||
*
|
||||
* @param string $block The name of the block.
|
||||
* @param array $variables The variables to pass to the template.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*/
|
||||
public function renderBlock($block, array $variables = array());
|
||||
|
||||
/**
|
||||
* Renders a CSRF token.
|
||||
*
|
||||
* Use this helper for CSRF protection without the overhead of creating a
|
||||
* form.
|
||||
*
|
||||
* <code>
|
||||
* <input type="hidden" name="token" value="<?php $renderer->renderCsrfToken('rm_user_'.$user->getId()) ?>">
|
||||
* </code>
|
||||
*
|
||||
* Check the token in your action using the same intention.
|
||||
*
|
||||
* <code>
|
||||
* $csrfProvider = $this->get('form.csrf_provider');
|
||||
* if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) {
|
||||
* throw new \RuntimeException('CSRF attack detected.');
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param string $intention The intention of the protected action
|
||||
*
|
||||
* @return string A CSRF token
|
||||
*/
|
||||
public function renderCsrfToken($intention);
|
||||
|
||||
/**
|
||||
* Returns whether the given choice is a group.
|
||||
*
|
||||
* @param mixed $choice A choice
|
||||
*
|
||||
* @return Boolean Whether the choice is a group
|
||||
*/
|
||||
public function isChoiceGroup($choice);
|
||||
|
||||
/**
|
||||
* Returns whether the given choice is selected.
|
||||
*
|
||||
* @param FormViewInterface $view The view of the choice field
|
||||
* @param ChoiceView $choice The choice to check
|
||||
*
|
||||
* @return Boolean Whether the choice is selected
|
||||
*/
|
||||
public function isChoiceSelected(FormViewInterface $view, ChoiceView $choice);
|
||||
|
||||
/**
|
||||
* Makes a technical name human readable.
|
||||
*
|
||||
* Sequences of underscores are replaced by single spaces. The first letter
|
||||
* of the resulting string is capitalized, while all other letters are
|
||||
* turned to lowercase.
|
||||
*
|
||||
* @param string $text The text to humanize.
|
||||
*
|
||||
* @return string The humanized text.
|
||||
*/
|
||||
public function humanize($text);
|
||||
}
|
|
@ -545,7 +545,7 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
|
|||
]
|
||||
/following-sibling::div
|
||||
[
|
||||
./label
|
||||
./label[.="child"]
|
||||
/following-sibling::div
|
||||
[
|
||||
./div
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<?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\Component\Form;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
|
||||
class FormRendererTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $engine;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $csrfProvider;
|
||||
|
||||
/**
|
||||
* @var FormRenderer
|
||||
*/
|
||||
private $renderer;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->engine = $this->getMock('Symfony\Component\Form\FormRendererEngineInterface');
|
||||
$this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface');
|
||||
$this->renderer = new FormRenderer($this->engine, $this->csrfProvider);
|
||||
}
|
||||
|
||||
public function isChoiceGroupProvider()
|
||||
{
|
||||
return array(
|
||||
array(false, 0),
|
||||
array(false, '0'),
|
||||
array(false, '1'),
|
||||
array(false, 1),
|
||||
array(false, ''),
|
||||
array(false, null),
|
||||
array(false, true),
|
||||
|
||||
array(true, array()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isChoiceGroupProvider
|
||||
*/
|
||||
public function testIsChoiceGroup($expected, $value)
|
||||
{
|
||||
$this->assertSame($expected, $this->renderer->isChoiceGroup($value));
|
||||
}
|
||||
|
||||
public function testIsChoiceGroupPart2()
|
||||
{
|
||||
$this->assertTrue($this->renderer->isChoiceGroup(new \SplFixedArray(1)));
|
||||
}
|
||||
|
||||
public function isChoiceSelectedProvider()
|
||||
{
|
||||
// The commented cases should not be necessary anymore, because the
|
||||
// choice lists should assure that both values passed here are always
|
||||
// strings
|
||||
return array(
|
||||
// array(true, 0, 0),
|
||||
array(true, '0', '0'),
|
||||
array(true, '1', '1'),
|
||||
// array(true, false, 0),
|
||||
// array(true, true, 1),
|
||||
array(true, '', ''),
|
||||
// array(true, null, ''),
|
||||
array(true, '1.23', '1.23'),
|
||||
array(true, 'foo', 'foo'),
|
||||
array(true, 'foo10', 'foo10'),
|
||||
array(true, 'foo', array(1, 'foo', 'foo10')),
|
||||
|
||||
array(false, 10, array(1, 'foo', 'foo10')),
|
||||
array(false, 0, array(1, 'foo', 'foo10')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isChoiceSelectedProvider
|
||||
*/
|
||||
public function testIsChoiceSelected($expected, $choice, $value)
|
||||
{
|
||||
$view = new FormView('name');
|
||||
$view->setVar('value', $value);
|
||||
$choice = new ChoiceView($choice, $choice . ' label');
|
||||
|
||||
$this->assertSame($expected, $this->renderer->isChoiceSelected($view, $choice));
|
||||
}
|
||||
}
|
|
@ -15,65 +15,6 @@ use Symfony\Component\Form\Util\FormUtil;
|
|||
|
||||
class FormUtilTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function isChoiceGroupProvider()
|
||||
{
|
||||
return array(
|
||||
array(false, 0),
|
||||
array(false, '0'),
|
||||
array(false, '1'),
|
||||
array(false, 1),
|
||||
array(false, ''),
|
||||
array(false, null),
|
||||
array(false, true),
|
||||
|
||||
array(true, array()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isChoiceGroupProvider
|
||||
*/
|
||||
public function testIsChoiceGroup($expected, $value)
|
||||
{
|
||||
$this->assertSame($expected, FormUtil::isChoiceGroup($value));
|
||||
}
|
||||
|
||||
public function testIsChoiceGroupPart2()
|
||||
{
|
||||
$this->assertTrue(FormUtil::isChoiceGroup(new \SplFixedArray(1)));
|
||||
}
|
||||
|
||||
public function isChoiceSelectedProvider()
|
||||
{
|
||||
// The commented cases should not be necessary anymore, because the
|
||||
// choice lists should assure that both values passed here are always
|
||||
// strings
|
||||
return array(
|
||||
// array(true, 0, 0),
|
||||
array(true, '0', '0'),
|
||||
array(true, '1', '1'),
|
||||
// array(true, false, 0),
|
||||
// array(true, true, 1),
|
||||
array(true, '', ''),
|
||||
// array(true, null, ''),
|
||||
array(true, '1.23', '1.23'),
|
||||
array(true, 'foo', 'foo'),
|
||||
array(true, 'foo10', 'foo10'),
|
||||
array(true, 'foo', array(1, 'foo', 'foo10')),
|
||||
|
||||
array(false, 10, array(1, 'foo', 'foo10')),
|
||||
array(false, 0, array(1, 'foo', 'foo10')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider isChoiceSelectedProvider
|
||||
*/
|
||||
public function testIsChoiceSelected($expected, $choice, $value)
|
||||
{
|
||||
$this->assertSame($expected, FormUtil::isChoiceSelected($choice, $value));
|
||||
}
|
||||
|
||||
public function singularifyProvider()
|
||||
{
|
||||
// see http://english-zone.com/spelling/plurals.html
|
||||
|
|
|
@ -191,35 +191,6 @@ abstract class FormUtil
|
|||
return $plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given choice is a group.
|
||||
*
|
||||
* @param mixed $choice A choice
|
||||
*
|
||||
* @return Boolean Whether the choice is a group
|
||||
*/
|
||||
public static function isChoiceGroup($choice)
|
||||
{
|
||||
return is_array($choice) || $choice instanceof \Traversable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given choice is selected.
|
||||
*
|
||||
* @param mixed $choice The choice
|
||||
* @param mixed $value the value
|
||||
*
|
||||
* @return Boolean Whether the choice is selected
|
||||
*/
|
||||
public static function isChoiceSelected($choice, $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return false !== array_search($choice, $value, true);
|
||||
}
|
||||
|
||||
return $choice === $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given data is empty.
|
||||
*
|
||||
|
|
Reference in New Issue