Skip to content

Commit

Permalink
feature #22610 [Form] [TwigBridge] Added option to disable usage of d…
Browse files Browse the repository at this point in the history
…efault themes when rendering a form (emodric)

This PR was merged into the 3.4 branch.

Discussion
----------

[Form] [TwigBridge] Added option to disable usage of default themes when rendering a form

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | N/A
| License       | MIT
| Doc PR        | symfony/symfony-docs#8495

This adds a possibility to use `only` keyword in `form_theme` tag to disable usage of globally defined form themes, e.g.

`{% form_theme form with ['common.html.twig', 'form/fields.html.twig'] only %}`

Otherwise, in order to completely control the rendering of the forms (for example in custom admin interfaces), one would need to use a form theme which has all the possible twig blocks defined to prevent globally defined themes to interfere with the rendering.

`only` keyword is already used when including a Twig template to transfer only the variables which are explicitly defined in the `include` tag, so it seemed natural to use it here too.

This, of course, means that the user will need to manually `use` all of the templates that are required to render the form, including `form_div_layout.html.twig`

This issue is described in details over at Symfony Demo repo: symfony/demo#515

TODO:

- [x] submit changes to the documentation

Commits
-------

e0681f9 [Form] [TwigBridge] Added option to disable usage of default themes when rendering a form
  • Loading branch information
fabpot committed Oct 13, 2017
2 parents 3f0a3f5 + e0681f9 commit a6aa9ec
Show file tree
Hide file tree
Showing 22 changed files with 107 additions and 43 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Bridge/Twig/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
3.4.0
-----

* added an `only` keyword to `form_theme` tag to disable usage of default themes when rendering a form
* deprecated `Symfony\Bridge\Twig\Form\TwigRenderer`
* deprecated `DebugCommand::set/getTwigEnvironment`. Pass an instance of
`Twig\Environment` as first argument of the constructor instead
Expand Down
8 changes: 5 additions & 3 deletions src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,11 @@ protected function loadResourceForBlockName($cacheKey, FormView $view, $blockNam

// Check the default themes once we reach the root view without success
if (!$view->parent) {
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
$this->loadResourcesFromTheme($cacheKey, $this->defaultThemes[$i]);
// CONTINUE LOADING (see doc comment)
if (!isset($this->useDefaultThemes[$cacheKey]) || $this->useDefaultThemes[$cacheKey]) {
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
$this->loadResourcesFromTheme($cacheKey, $this->defaultThemes[$i]);
// CONTINUE LOADING (see doc comment)
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/Symfony/Bridge/Twig/Node/FormThemeNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
*/
class FormThemeNode extends Node
{
public function __construct(Node $form, Node $resources, $lineno, $tag = null)
public function __construct(Node $form, Node $resources, $lineno, $tag = null, $only = false)
{
parent::__construct(array('form' => $form, 'resources' => $resources), array(), $lineno, $tag);
parent::__construct(array('form' => $form, 'resources' => $resources), array('only' => (bool) $only), $lineno, $tag);
}

public function compile(Compiler $compiler)
Expand All @@ -44,6 +44,8 @@ public function compile(Compiler $compiler)
->subcompile($this->getNode('form'))
->raw(', ')
->subcompile($this->getNode('resources'))
->raw(', ')
->raw(false === $this->getAttribute('only') ? 'true' : 'false')
->raw(");\n");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}

public static function themeBlockInheritanceProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->renderer->renderBlock($view, 'form_end', $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}
}
25 changes: 23 additions & 2 deletions src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function testConstructor()

$this->assertEquals($form, $node->getNode('form'));
$this->assertEquals($resources, $node->getNode('resources'));
$this->assertFalse($node->getAttribute('only'));
}

public function testCompile()
Expand All @@ -60,7 +61,17 @@ public function testCompile()

$this->assertEquals(
sprintf(
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, array(0 => "tpl1", 1 => "tpl2"));',
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, array(0 => "tpl1", 1 => "tpl2"), true);',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);

$node = new FormThemeNode($form, $resources, 0, null, true);

$this->assertEquals(
sprintf(
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, array(0 => "tpl1", 1 => "tpl2"), false);',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
Expand All @@ -72,7 +83,17 @@ public function testCompile()

$this->assertEquals(
sprintf(
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, "tpl1");',
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, "tpl1", true);',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
);

$node = new FormThemeNode($form, $resources, 0, null, true);

$this->assertEquals(
sprintf(
'$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, "tpl1", false);',
$this->getVariableGetter('form')
),
trim($compiler->compile($node)->getSource())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ public function getTestsForFormTheme()
'form_theme'
),
),
array(
'{% form_theme form with ["tpl1", "tpl2"] only %}',
new FormThemeNode(
new NameExpression('form', 1),
new ArrayExpression(array(
new ConstantExpression(0, 1),
new ConstantExpression('tpl1', 1),
new ConstantExpression(1, 1),
new ConstantExpression('tpl2', 1),
), 1),
1,
'form_theme',
true
),
),
);
}
}
7 changes: 6 additions & 1 deletion src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,15 @@ public function parse(Token $token)
$stream = $this->parser->getStream();

$form = $this->parser->getExpressionParser()->parseExpression();
$only = false;

if ($this->parser->getStream()->test(Token::NAME_TYPE, 'with')) {
$this->parser->getStream()->next();
$resources = $this->parser->getExpressionParser()->parseExpression();

if ($this->parser->getStream()->nextIf(Token::NAME_TYPE, 'only')) {
$only = true;
}
} else {
$resources = new ArrayExpression(array(), $stream->getCurrent()->getLine());
do {
Expand All @@ -50,7 +55,7 @@ public function parse(Token $token)

$stream->expect(Token::BLOCK_END_TYPE);

return new FormThemeNode($form, $resources, $lineno, $this->getTag());
return new FormThemeNode($form, $resources, $lineno, $this->getTag(), $only);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ public function getName()
*
* The theme format is "<Bundle>:<Controller>".
*
* @param FormView $view A FormView instance
* @param string|array $themes A theme or an array of theme
* @param FormView $view A FormView instance
* @param string|array $themes A theme or an array of theme
* @param bool $useDefaultThemes If true, will use default themes defined in the renderer
*/
public function setTheme(FormView $view, $themes)
public function setTheme(FormView $view, $themes, $useDefaultThemes = true)
{
$this->renderer->setTheme($view, $themes);
$this->renderer->setTheme($view, $themes, $useDefaultThemes);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->engine->get('form')->end($view, $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->engine->get('form')->setTheme($view, $themes);
$this->engine->get('form')->setTheme($view, $themes, $useDefaultThemes);
}

public static function themeBlockInheritanceProvider()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ protected function renderEnd(FormView $view, array $vars = array())
return (string) $this->engine->get('form')->end($view, $vars);
}

protected function setTheme(FormView $view, array $themes)
protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true)
{
$this->engine->get('form')->setTheme($view, $themes);
$this->engine->get('form')->setTheme($view, $themes, $useDefaultThemes);
}
}
10 changes: 9 additions & 1 deletion src/Symfony/Component/Form/AbstractRendererEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface
*/
protected $themes = array();

/**
* @var array
*/
protected $useDefaultThemes = array();

/**
* @var array
*/
Expand All @@ -57,13 +62,16 @@ public function __construct(array $defaultThemes = array())
/**
* {@inheritdoc}
*/
public function setTheme(FormView $view, $themes)
public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */)
{
$cacheKey = $view->vars[self::CACHE_KEY_VAR];

// Do not cast, as casting turns objects into arrays of properties
$this->themes[$cacheKey] = is_array($themes) ? $themes : array($themes);

$args = func_get_args();
$this->useDefaultThemes[$cacheKey] = isset($args[2]) ? (bool) $args[2] : true;

// Unset instead of resetting to an empty array, in order to allow
// implementations (like TwigRendererEngine) to check whether $cacheKey
// is set at all.
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Form/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ CHANGELOG
* added `DebugCommand`
* deprecated `ChoiceLoaderInterface` implementation in `TimezoneType`
* added options "input" and "regions" to `TimezoneType`
* added an option to ``Symfony\Component\Form\FormRendererEngineInterface::setTheme()`` and
``Symfony\Component\Form\FormRendererInterface::setTheme()`` to disable usage of default themes when rendering a form

3.3.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ protected function loadResourceForBlockName($cacheKey, FormView $view, $blockNam

// Check the default themes once we reach the root form without success
if (!$view->parent) {
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->defaultThemes[$i])) {
return true;
if (!isset($this->useDefaultThemes[$cacheKey]) || $this->useDefaultThemes[$cacheKey]) {
for ($i = count($this->defaultThemes) - 1; $i >= 0; --$i) {
if ($this->loadResourceFromTheme($cacheKey, $blockName, $this->defaultThemes[$i])) {
return true;
}
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/Component/Form/FormRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ public function getEngine()
/**
* {@inheritdoc}
*/
public function setTheme(FormView $view, $themes)
public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */)
{
$this->engine->setTheme($view, $themes);
$args = func_get_args();
$this->engine->setTheme($view, $themes, isset($args[2]) ? (bool) $args[2] : true);
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/Symfony/Component/Form/FormRendererEngineInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ interface FormRendererEngineInterface
/**
* Sets the theme(s) to be used for rendering a view and its children.
*
* @param FormView $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.
* @param FormView $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.
* @param bool $useDefaultThemes If true, will use default themes specified
* in the engine, will be added to the interface in 4.0
*/
public function setTheme(FormView $view, $themes);
public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */);

/**
* Returns the resource for a block name.
Expand Down
10 changes: 6 additions & 4 deletions src/Symfony/Component/Form/FormRendererInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ public function getEngine();
/**
* Sets the theme(s) to be used for rendering a view and its children.
*
* @param FormView $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.
* @param FormView $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.
* @param bool $useDefaultThemes If true, will use default themes specified
* in the renderer, will be added to the interface in 4.0
*/
public function setTheme(FormView $view, $themes);
public function setTheme(FormView $view, $themes /*, $useDefaultThemes = true */);

/**
* Renders a named block of the form theme.
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ abstract protected function renderStart(FormView $view, array $vars = array());

abstract protected function renderEnd(FormView $view, array $vars = array());

abstract protected function setTheme(FormView $view, array $themes);
abstract protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true);

public function testLabel()
{
Expand Down

0 comments on commit a6aa9ec

Please sign in to comment.