Issue #2114563 by joelpittet, martin107, Cottser: Remove TwigReference with the help of 'without' filter replacing 'show'/'hide' functions.

8.0.x
Nathaniel Catchpole 2014-03-07 18:29:14 +00:00
parent d8c718b2f6
commit 7eda0ecba5
24 changed files with 181 additions and 629 deletions

View File

@ -125,7 +125,6 @@ class CoreServiceProvider implements ServiceProviderInterface {
// When in the installer, twig_cache must be FALSE until we know the
// files folder is writable.
'cache' => drupal_installation_attempted() ? FALSE : settings()->get('twig_cache', TRUE),
'base_template_class' => 'Drupal\Core\Template\TwigTemplate',
// @todo Remove in followup issue
// @see http://drupal.org/node/1712444.
'autoescape' => FALSE,

View File

@ -21,25 +21,24 @@ class TwigExtension extends \Twig_Extension {
// @todo re-add unset => twig_unset if this is really needed
return array(
// @todo Remove URL function once http://drupal.org/node/1778610 is resolved.
'url' => new \Twig_Function_Function('url'),
// These functions will receive a TwigReference object, if a render array is detected
'hide' => new TwigReferenceFunction('twig_hide'),
'render_var' => new TwigReferenceFunction('twig_render_var'),
'show' => new TwigReferenceFunction('twig_show'),
new \Twig_SimpleFunction('url', 'url'),
// This function will receive a renderable array, if an array is detected.
new \Twig_SimpleFunction('render_var', 'twig_render_var'),
);
}
public function getFilters() {
return array(
't' => new \Twig_Filter_Function('t'),
'trans' => new \Twig_Filter_Function('t'),
new \Twig_SimpleFilter('t', 't'),
new \Twig_SimpleFilter('trans', 't'),
// The "raw" filter is not detectable when parsing "trans" tags. To detect
// which prefix must be used for translation (@, !, %), we must clone the
// "raw" filter and give it identifiable names. These filters should only
// be used in "trans" tags.
// @see TwigNodeTrans::compileString()
'passthrough' => new \Twig_Filter_Function('twig_raw_filter'),
'placeholder' => new \Twig_Filter_Function('twig_raw_filter'),
new \Twig_SimpleFilter('passthrough', 'twig_raw_filter'),
new \Twig_SimpleFilter('placeholder', 'twig_raw_filter'),
new \Twig_SimpleFilter('without', 'twig_without'),
);
}
@ -53,8 +52,6 @@ class TwigExtension extends \Twig_Extension {
public function getTokenParsers() {
return array(
new TwigFunctionTokenParser('hide'),
new TwigFunctionTokenParser('show'),
new TwigTransTokenParser(),
);
}

View File

@ -1,29 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\Core\Template\TwigNodeExpressionNameReference
*/
namespace Drupal\Core\Template;
/**
* A class that defines a reference to the name for all nodes that represent
* expressions.
*
* @see core\vendor\twig\twig\lib\Twig\Node\Expression\Name.php
*/
class TwigNodeExpressionNameReference extends \Twig_Node_Expression_Name {
/**
* Overrides Twig_Node_Expression_Name::compile().
*/
public function compile(\Twig_Compiler $compiler) {
$name = $this->getAttribute('name');
$compiler
->raw('$this->getContextReference($context, ')
->string($name)
->raw(')');
}
}

View File

@ -19,36 +19,9 @@ namespace Drupal\Core\Template;
class TwigNodeVisitor implements \Twig_NodeVisitorInterface {
/**
* TRUE when this node is a function getting arguments by reference.
*
* For example: 'hide' or 'render' are such functions.
*
* @var bool
*/
protected $isReference = FALSE;
/**
* Implements Twig_NodeVisitorInterface::enterNode().
* {@inheritdoc}
*/
function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
if ($node instanceof \Twig_Node_Expression_Function) {
$name = $node->getAttribute('name');
$func = $env->getFunction($name);
// Optimization: Do not support nested functions.
if ($this->isReference && $func instanceof \Twig_Function_Function) {
$this->isReference = FALSE;
}
if ($func instanceof TwigReferenceFunction) {
// We need to create a TwigReference
$this->isReference = TRUE;
}
}
if ($node instanceof \Twig_Node_Print) {
// Our injected render_var needs arguments passed by reference -- in case of render array
$this->isReference = TRUE;
}
return $node;
}
@ -62,26 +35,14 @@ class TwigNodeVisitor implements \Twig_NodeVisitorInterface {
*/
function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
if ($node instanceof \Twig_Node_Print) {
$this->isReference = FALSE;
$class = get_class($node);
$line = $node->getLine();
return new $class(
new \Twig_Node_Expression_Function('render_var', new \Twig_Node(array($node->getNode('expr'))), $node->getLine()),
$node->getLine()
new \Twig_Node_Expression_Function('render_var', new \Twig_Node(array($node->getNode('expr'))), $line),
$line
);
}
if ($this->isReference) {
if ($node instanceof \Twig_Node_Expression_Name) {
$name = $node->getAttribute('name');
return new TwigNodeExpressionNameReference($name, $node->getLine());
}
elseif ($node instanceof \Twig_Function_Function) {
// Do something!
$this->isReference = FALSE;
}
}
return $node;
}

View File

@ -1,131 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\Core\Template\TwigReference.
*/
namespace Drupal\Core\Template;
/**
* A class used to pass variables by reference while they are used in twig.
*
* This is done by saving a reference to the original render array within a
* TwigReference via the setReference() method like this:
* @code
* $obj = new TwigReference();
* $obj->setReference($variable);
* @endcode
*
* When a TwigReference is accessed via the offsetGet method the resulting
* reference is again wrapped within a TwigReference. Therefore references to
* render arrays within render arrays are also retained.
*
* To unwrap TwigReference objects the reference can be retrieved out of the
* object by calling the getReference() method like this:
* @code
* $variable = &$obj->getReference();
* @endcode
* This allows render(), hide() and show() to access the original variable and
* change it. The process of unwrapping and passing by reference to this
* functions is done transparently by the TwigReferenceFunctions helper class.
*
* @see TwigReferenceFunction
* @see TwigReferenceFunctions
*/
class TwigReference extends \ArrayObject {
/**
* Holds an internal reference to the original array.
*
* @var array
*/
protected $writableRef = array();
/**
* Constructs a \Drupal\Core\Template\TwigReference object.
*
* The argument to the constructor is ignored as it is not safe that this will
* always be a reference.
*
* To set a reference use:
* @code
* $obj = new TwigReference();
* $obj->setReference($variable);
* @endcode
*
* @param $array
* The array parameter is ignored and not passed to the parent
*/
public function __construct($array = NULL) {
parent::__construct();
}
/**
* Sets a reference in the internal storage.
*
* @param $array
* The array to set as internal reference.
*/
public function setReference(&$array) {
$this->exchangeArray($array);
$this->writableRef = &$array;
}
/**
* Gets a reference to the internal storage.
*
* Should be called like:
* @code
* $reference = &$obj->getReference();
* @endcode
*
* @return
* Returns the stored internal reference.
*/
public function &getReference() {
return $this->writableRef;
}
/**
* Sets offset in internal reference and internal storage to value.
*
* This is just for completeness, but should never be used, because
* twig cannot set properties and should not.
*
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset
* The offset to assign the value to.
* @param mixed $value
* The value to set.
*/
public function offsetSet($offset, $value) {
$this->writableRef[$offset] = $value;
parent::offsetSet($offset, $value);
}
/**
* Retrieves offset from internal reference.
*
* In case of a render array, it is wrapped again within a TwigReference
* object.
*
* @param mixed $offset
* The offset to retrieve.
*
* @return mixed
* Returns a TwigReference object wrapping the array if the retrieved offset
* is a complex array (i.e. not an attribute). Else it returns the retrived
* offset directly.
*/
public function offsetGet($offset) {
if (!is_array($this->writableRef[$offset]) || $offset[0] == '#') {
return $this->writableRef[$offset];
}
// Wrap the returned array in a new TwigReference.
$x = clone $this; // clone is faster than new
$x->setReference($this->writableRef[$offset]);
return $x;
}
}

View File

@ -1,14 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\Core\Template\TwigReferenceFunction.
*/
namespace Drupal\Core\Template;
/**
* This class is used to create functions requiring references like 'hide'.
*/
class TwigReferenceFunction extends \Twig_Function_Function {
}

View File

@ -1,93 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\Core\Template\TwigReferenceFunctions.
*/
namespace Drupal\Core\Template;
/**
* A helper used to unwrap TwigReference objects transparently.
*
* This is providing a static magic function that makes it easier to unwrap
* TwigReference objects and pass variables by reference to show(), hide() and
* render().
*
* The problem is that twig passes variables only by value. The following is a
* simplified version of the generated code by twig when the property "links" of
* a render array stored in $content should be hidden:
* @code
* $_content_ = $content;
* hide(getAttribute($_content_, 'links'));
* @endcode
* As hide() is operating on a copy of the original array the hidden property
* is not set on the original $content variable.
*
* TwigReferenceFunctions can be used in combination with TwigReference to solve
* this problem:
* @code
* // Internally getContextReference returns the array wrapped in a
* // TwigReference if certain criteria are met
* function getContextReference(&$content) {
* $obj = new Drupal\Core\Template\TwigReference();
* $obj->setReference($content);
* return $obj;
* }
*
* // [...]
* // Simplified, generated twig code
* $_content_ = getContextReference($content);
*
* Drupal\Core\Template\TwigReferenceFunctions::hide(
* getAttribute($_content_, 'links')
* );
* @endcode
* A TwigReference object is passed to the __callStatic function of
* TwigReferenceFunctions. The method unwraps the TwigReference and calls the
* hide() method essentially with a reference to $content['links'].
*
* Therefore the hidden property is correctly set and a successive call to
* render() will not render the content twice.
*
* @see TwigReference
* @see TwigReferenceFunction
* @see TwigFactory
*
*/
class TwigReferenceFunctions {
/**
* Magic function to call functions called from twig templates with a
* reference to the original variable.
*
* This checks if the array provided by value is containing a reference to
* the original version. If yes it replaces the argument with its reference.
*
* @param $name
* The name of the function to call.
* @param $arguments
* The arguments to process and pass to the called function.
*
* @return mixed
* Returns the output of the called function.
*
* @see TwigReference
*/
public static function __callStatic($name, $arguments) {
foreach ($arguments as $key => $val) {
if (is_object($val) && $val instanceof TwigReference) {
$arguments[$key] = &$val->getReference();
}
}
// Needed to pass by reference -- could also restrict to maximum one
// argument instead
$args = array();
foreach ($arguments as $key => &$arg) {
$args[$key] = &$arg;
}
return call_user_func_array($name, $args);
}
}

View File

@ -1,89 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\Core\Template\TwigTemplate.
*/
namespace Drupal\Core\Template;
/**
* This is the base class for compiled Twig templates.
*/
abstract class TwigTemplate extends \Twig_Template {
/**
* A class used to pass variables by reference while they are used in Twig.
*/
protected $twig_reference = NULL;
/**
* List of the name of variables to be passed around as references.
*
* @var array
*/
protected $is_reference = array();
/**
* List of the name of variables to be passed around by value.
*
* @var array
*/
protected $is_no_reference = array();
/**
* @param array $context
* The variables available to the template.
* @param $item
* The name of the variable.
* @return mixed
* The requested variable.
*/
protected function getContextReference(&$context, $item) {
// Optimized version. NULL is a valid value for $context[$item], we only
// want to error if it hasn't been defined at all.
if (!isset($context[$item]) && !array_key_exists($item, $context)) {
// We don't want to throw an exception, but issue a warning instead.
// This is the easiest way to do so.
// @todo Decide based on prod vs. dev setting
$msg = new \Twig_Error(t('@item could not be found in _context', array('@item' => $item)));
trigger_error($msg->getMessage(), E_USER_WARNING);
return NULL;
}
// Return item instead of its reference inside a loop.
// @todo 'hide' and 'show' are not supported inside a loop for now.
// This should be a non-issue as soon as this lands:
// @see http://drupal.org/node/1922304
if (isset($context['_seq'])) {
return $context[$item];
}
// The first test also finds empty / null render arrays
if (!$context[$item] || isset($this->is_no_reference[$item])) {
return $context[$item];
}
if (isset($context['_references'][$item])) {
return $context['_references'][$item];
}
// @todo Check if this is a render array (existence of #theme?)
if ((!isset($this->is_reference[$item])) && ($context[$item] instanceof \TwigMarkup || !is_array($context[$item]))) {
$this->is_no_reference[$item] = TRUE;
return $context[$item];
}
if ($this->twig_reference == NULL) {
$this->twig_reference = new TwigReference();
}
$ref = clone $this->twig_reference; // clone is _much_ faster than new
$ref->setReference($context[$item]);
// Save that this is a reference
$context['_references'][$item] = $ref;
$this->is_reference[$item] = TRUE;
return $ref;
}
}

View File

@ -13,10 +13,9 @@
* @ingroup themeable
*/
#}
{% hide(form.place_blocks) %}
<div class="layout-block-list clearfix">
<div class="layout-region block-list-primary">
{{ form }}
{{ form|without('place_blocks') }}
</div>
<div class="layout-region block-list-secondary">
{{ form.place_blocks }}

View File

@ -7,8 +7,11 @@
* - author: Comment author. Can be a link or plain text.
* - content: The content-related items for the comment display. Use
* {{ content }} to print them all, or print a subset such as
* {{ content.field_example }}. Use hide(content.field_example) to temporarily
* suppress the printing of a given element.
* {{ content.field_example }}. Use the following code to temporarily suppress
* the printing of a given child element:
* @code
* {{ content|without('field_example') }}
* @endcode
* - created: Formatted date and time for when the comment was created.
* Preprocess functions can reformat it by calling format_date() with the
* desired parameters on the 'comment.created' variable.
@ -92,9 +95,7 @@
</footer>
<div{{ content_attributes }}>
{# We hide the links now so that we can render them later. #}
{% hide(content.links) %}
{{ content }}
{{ content|without('links') }}
{% if signature %}
<div class="user-signature">

View File

@ -15,11 +15,9 @@
* @ingroup themeable
*/
#}
{% hide(form.advanced) %}
{% hide(form.actions) %}
<div class="layout-node-form clearfix">
<div class="layout-region layout-region-node-main">
{{ form }}
{{ form|without('advanced', 'actions') }}
</div>
<div class="layout-region layout-region-node-secondary">
{{ form.advanced }}

View File

@ -18,8 +18,8 @@
* - label: The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {% hide(content.field_example) %} to temporarily suppress the printing
* of a given element.
* {{ content|without('field_example') %} to temporarily suppress the printing
* of a given child element.
* - user_picture: The node author's picture from user-picture.html.twig.
* - date: Formatted creation date. Preprocess functions can reformat it by
* calling format_date() with the desired parameters on
@ -93,9 +93,7 @@
{% endif %}
<div{{ content_attributes }}>
{# We hide links now so that we can render them later. #}
{% hide(content.links) %}
{{ content }}
{{ content|without('links') }}
</div>
{{ content.links }}

View File

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Theme\TwigFilterTest.
*/
namespace Drupal\system\Tests\Theme;
use Drupal\simpletest\WebTestBase;
/**
* Tests Drupal's Twig filters.
*/
class TwigFilterTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array(
'twig_theme_test',
);
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Twig Filters',
'description' => 'Test Drupal\'s Twig filters.',
'group' => 'Theme',
);
}
/**
* Test Twig "without" filter.
*/
public function testTwigWithoutFilter() {
$this->drupalGet('/twig-theme-test/filter');
$elements = array(
array(
'expected' => '<div><strong>No author:</strong> You can only find truth with logic if you have already found truth without it.1874-1936.</div>',
'message' => '"No author" was successfully rendered.',
),
array(
'expected' => '<div><strong>Complete quote after without:</strong> You can only find truth with logic if you have already found truth without it.Gilbert Keith Chesterton1874-1936.</div>',
'message' => '"Complete quote after without" was successfully rendered.',
),
array(
'expected' => '<div><strong>Only author:</strong> Gilbert Keith Chesterton.</div>',
'message' => '"Only author:" was successfully rendered.',
),
array(
'expected' => '<div><strong>No author or date:</strong> You can only find truth with logic if you have already found truth without it..</div>',
'message' => '"No author or date" was successfully rendered.',
),
array(
'expected' => '<div><strong>Only date:</strong> 1874-1936.</div>',
'message' => '"Only date" was successfully rendered.',
),
array(
'expected' => '<div><strong>Complete quote again for good measure:</strong> You can only find truth with logic if you have already found truth without it.Gilbert Keith Chesterton1874-1936.</div>',
'message' => '"Complete quote again for good measure" was successfully rendered.',
),
array(
'expected' => '<div><strong>Marked-up:</strong>
<blockquote>
<p>You can only find truth with logic if you have already found truth without it.</p>
<footer>
&ndash; <cite><a href="#">Gilbert Keith Chesterton</a> <em>(1874-1936)</em></cite>
</footer>
</blockquote>',
'message' => '"Marked-up quote" was successfully rendered.',
),
);
foreach ($elements as $element) {
$this->assertRaw($element['expected'], $element['message']);
}
}
}

View File

@ -1,18 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\system\Tests\Theme\TwigReferenceObjectTest.
*/
namespace Drupal\system\Tests\Theme;
/**
* TwigReferenceObjectTest class.
*/
class TwigReferenceObjectTest {
public function __construct($nid, $title) {
$this->nid = $nid;
$this->title = $title;
}
}

View File

@ -1,147 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\system\Tests\Theme\TwigReferenceUnitTest.
*/
namespace Drupal\system\Tests\Theme;
use Drupal\simpletest\UnitTestBase;
use Drupal\Core\Template\TwigReference;
use Drupal\Core\Template\TwigReferenceFunctions;
/**
* Unit tests for TwigReference class.
*/
class TwigReferenceUnitTest extends UnitTestBase {
public static function getInfo() {
return array(
'name' => 'Theme Twig References',
'description' => 'Tests TwigReference functions',
'group' => 'Theme',
);
}
function setUp() {
parent::setUp();
$this->variables = array(
'foo' => 'bar',
'baz' => array(
'foo' => '42',
'bar' => '23',
),
'node' => new TwigReferenceObjectTest(
42,
'test node'
)
);
}
/**
* Test function for TwigReference class
*/
function testTwigReference() {
// Create a new TwigReference wrapper
$wrapper = new TwigReference();
$wrapper->setReference($this->variables);
// Check that strings are returned as strings
$foo = $wrapper['foo'];
$this->assertEqual($foo, $this->variables['foo'], 'String returned from TwigReference is the same');
$this->assertTrue(is_string($foo), 'String returned from TwigReference is of type string');
// Check that arrays are wrapped again as TwigReference objects
$baz = $wrapper['baz'];
$this->assertTrue(is_object($baz), 'Array returned from TwigReference is of type object');
$this->assertTrue($baz instanceof TwigReference, 'Array returned from TwigReference is instance of TwigReference');
// Check that getReference is giving back a reference to the original array
$ref = &$baz->getReference();
$this->assertTrue(is_array($ref), 'getReference returns an array');
// Now modify $ref
$ref['#hidden'] = TRUE;
$this->assertEqual($ref['#hidden'], $this->variables['baz']['#hidden'], 'Property set on reference is passed to original array.');
$this->assertEqual($ref['#hidden'], $baz['#hidden'], 'Property set on reference is passed to wrapper.');
// Now modify $baz
$baz['hi'] = 'hello';
$this->assertEqual($baz['hi'], $this->variables['baz']['hi'], 'Property set on TwigReference object is passed to original array.');
$this->assertEqual($baz['hi'], $ref['hi'], 'Property set on TwigReference object is passed to reference.');
// Check that an object is passed through directly
$node = $wrapper['node'];
$this->assertTrue(is_object($node), 'Object returned from TwigReference is of type object');
$this->assertTrue($node instanceof TwigReferenceObjectTest, 'Object returned from TwigReference is instance of TwigReferenceObjectTest');
}
/**
* Test function for TwigReferenceFunctions class
*/
function testTwigReferenceFunctions() {
// Create wrapper
$content = &$this->variables;
// Use twig nomenclature
$context['content'] = $content;
// Twig converts {{ hide(content.baz) }} to the following code
// This will have failed, because getAttribute returns a value and not a reference
try {
if (isset($context["content"])) {
$_content_ = $context["content"];
}
else {
$_content_ = NULL;
}
TwigReferenceFunctions::hide($this->getAttribute($_content_, "baz"));
}
catch (Exception $e) {
// Catch the critical warning that a value was passed by reference
}
$this->assertFalse(isset($content['baz']['#printed']), 'baz is not hidden in content after hide() via value');
// Now lets do the same with some TwigReference magic!
$content_wrapper = new TwigReference();
$content_wrapper->setReference($content);
$context['content'] = $content_wrapper;
// Twig converts {{ hide(content.baz) }} to the following code
// This will succeed, because getAttribute returns a value, but it is an object
if (isset($context["content"])) {
$_content_ = $context["content"];
}
else {
$_content_ = NULL;
}
TwigReferenceFunctions::hide($this->getAttribute($_content_, "baz"));
$this->assertTrue(isset($content['baz']['#printed']), 'baz is hidden in content after hide() via TwigReference object');
$type = TwigReferenceFunctions::gettype($this->getAttribute($_content_, "baz"));
$this->assertEqual($type, 'array', 'Type returned via TwigReferenceFunctions:: is an array.');
$type = gettype($this->getAttribute($_content_, "baz"));
$this->assertEqual($type, 'object', 'Type returned without TwigReferenceFunctions:: is an object.');
}
/**
* Helper function to somehow simulate Twigs getAttribute function
*/
public function getAttribute($array, $offset) {
if (isset($array[$offset])) {
return $array[$offset];
}
return NULL;
}
}

View File

@ -8,7 +8,6 @@
namespace Drupal\twig_extension_test\TwigExtension;
use Drupal\Core\Template\TwigExtension;
use Drupal\Core\Template\TwigReferenceFunction;
/**
* A test Twig extension that adds a custom function and a custom filter.

View File

@ -28,4 +28,19 @@ class TwigThemeTestController {
);
}
/**
* Menu callback for filters in a Twig template.
*/
public function testFilterRender() {
return array(
'#theme' => 'twig_theme_test_filter',
'#quote' => array(
'content' => array('#markup' => 'You can only find truth with logic if you have already found truth without it.'),
'author' => array('#markup' => 'Gilbert Keith Chesterton'),
'date' => array('#markup' => '1874-1936'),
),
);
}
}

View File

@ -0,0 +1,14 @@
<div><strong>No author:</strong> {{ quote|without('author') }}.</div>
<div><strong>Complete quote after without:</strong> {{ quote }}.</div>
<div><strong>Only author:</strong> {{ quote.author }}.</div>
<div><strong>No author or date:</strong> {{ quote|without('date', 'author') }}.</div>
<div><strong>Only date:</strong> {{ quote.date }}.</div>
<div><strong>Complete quote again for good measure:</strong> {{ quote }}.</div>
<div><strong>Marked-up:</strong>
<blockquote>
<p>{{ quote.content }}</p>
<footer>
&ndash; <cite><a href="#">{{ quote.author }}</a> <em>({{ quote.date }})</em></cite>
</footer>
</blockquote>
</div>

View File

@ -4,6 +4,10 @@
* Implements hook_theme().
*/
function twig_theme_test_theme($existing, $type, $theme, $path) {
$items['twig_theme_test_filter'] = array(
'variables' => array('quote' => array()),
'template' => 'twig_theme_test.filter',
);
$items['twig_theme_test_php_variables'] = array(
'template' => 'twig_theme_test.php_variables',
);

View File

@ -11,3 +11,10 @@ twig_theme_test.trans:
_content: '\Drupal\twig_theme_test\TwigThemeTestController::transBlockRender'
requirements:
_access: 'TRUE'
twig_theme_test.filter:
path: '/twig-theme-test/filter'
defaults:
_content: '\Drupal\twig_theme_test\TwigThemeTestController::testFilterRender'
requirements:
_access: 'TRUE'

View File

@ -8,10 +8,10 @@
* - name: Name of the current term.
* - content: Items for the content of the term (fields and description).
* Use 'content' to print them all, or print a subset such as
* 'content.description'. Use the following code to temporarily suppress the
* printing of a given element:
* 'content.description'. Use the following code to exclude the
* printing of a given child element:
* @code
* {% hide(content.description) %}
* {{ content|without('description') }}
* @endcode
* - attributes: HTML attributes for the wrapper. The 'class' attribute
* contains the following classes by default:

View File

@ -7,8 +7,11 @@
* - author: Comment author. Can be a link or plain text.
* - content: The content-related items for the comment display. Use
* {{ content }} to print them all, or print a subset such as
* {{ content.field_example }}. Use hide(content.field_example) to temporarily
* suppress the printing of a given element.
* {{ content.field_example }}. Use the following code to temporarily suppress
* the printing of a given child element:
* @code
* {{ content|without('field_example') }}
* @endcode
* - created: Formatted date and time for when the comment was created.
* Preprocess functions can reformat it by calling format_date() with the
* desired parameters on the 'comment.created' variable.
@ -106,9 +109,7 @@
{{ title_suffix }}
<div{{ content_attributes }}>
{# We hide the links now so that we can render them later. #}
{% hide(content.links) %}
{{ content }}
{{ content|without('links') }}
</div> <!-- /.content -->
<footer class="comment-footer">

View File

@ -18,8 +18,8 @@
* - label: The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {% hide(content.field_example) %} to temporarily suppress the printing
* of a given element.
* {{ content|without('field_example') }} to exclude the printing of a
* given child element.
* - user_picture: The node author's picture from user-picture.html.twig.
* - date: Formatted creation date. Preprocess functions can reformat it by
* calling format_date() with the desired parameters on
@ -91,9 +91,7 @@
</header>
<div class="content clearfix"{{ content_attributes }}>
{# We hide links now so that we can render them later. #}
{% hide(content.links) %}
{{ content }}
{{ content|without('links') }}
</div>
{% if content.links %}

View File

@ -4,7 +4,6 @@
* @file
* Handles integration of Twig templates with the Drupal theme system.
*/
use Drupal\Core\Template\TwigReference;
/**
* Implements hook_theme().
@ -119,10 +118,6 @@ function twig_render_template($template_file, $variables) {
* @see TwigNodeVisitor
*/
function twig_render_var($arg) {
if ($arg instanceof TwigReference) {
$arg = &$arg->getReference();
}
// Check for numeric zero.
if ($arg === 0) {
return 0;
@ -154,27 +149,29 @@ function twig_render_var($arg) {
}
/**
* Wrapper around hide() that does not return the content.
* Removes child elements from a copy of the original array.
*
* @see hide
*/
function twig_hide($element) {
if ($element instanceof TwigReference) {
$element = &$element->getReference();
hide($element);
}
// @todo Add warning in else case
}
/**
* Wrapper around show() that does not return the content.
* Creates a copy of the renderable array and removes child elements by key
* specified throught filter's arguments. The copy can be printed without these
* elements. The original renderable array is still available and can be used
* to print child elements in their entirety in the twig template.
*
* @see show
* @param array $element
* The parent renderable array to exclude the child items.
* @param string[] $args, ...
* The string keys of $element to prevent printing.
*
* @return array
* The filtered renderable array.
*/
function twig_show($element) {
if ($element instanceof TwigReference) {
$element = &$element->getReference();
show($element);
function twig_without($element) {
$filtered_element = $element;
$args = func_get_args();
unset($args[0]);
foreach ($args as $arg) {
if (isset($filtered_element[$arg])) {
unset($filtered_element[$arg]);
}
}
// @todo Add warning in else case
return $filtered_element;
}