I have making a custom twig tag called "story_get_adjacent" to get the next/prev articles based on the input id. But for the life of me I cant get the actual data from the object pulled into the tag for look up. it always gives me back the name not the data. I know this can be done because i tested it with the set tag and it returns the data not the name. Thoughts????
Object on page
Object >>
Title = "This is a test story"
StoryID = 1254
Content ....
tag usage Example:
{% story_get_adjacent Object.StoryID as adjacent %}
Twig Extension:
class Story_Get_Adjacent_TokenParser extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$parser = $this->parser; //story_get_adjacent
$stream = $parser->getStream(); // space
$value = $parser->getExpressionParser()->parseExpression(); //story id
$names = array();
try {
$as = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); //as
$ObjName = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); //object name
array_push($names, $ObjName);
} catch (Exception $e) {
throw new Exception( 'error: ' . $e);
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Story_Get_Adjacent_Node($names, $value, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'story_get_adjacent';
}
}
Twig Extension:
class Story_Get_Adjacent_Node extends Twig_Node
{
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
{
parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
}
public function compile (Twig_Compiler $compiler)
{
$Name = $this->getAttribute('name')[0];
$StoryAutoID = $this->getNode('value')->getAttribute('value');
$compiler->addDebugInfo($this);
$compiler->write('$context[\''. $Name .'\'] = NewsController::TwigStoryGetAdjacent("'.$StoryAutoID.'");')->raw("
");
}
}
The problem is located in the compiler, when you try to access the value attribute of your expression.
'
) when you call TwigStoryGetAdjacent
.Try with the following class:
<?php
class Story_Get_Adjacent_Node extends Twig_Node
{
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
{
parent::__construct(array ('value' => $value), array ('name' => $name), $line, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$Name = reset($this->getAttribute('name'));
$compiler->addDebugInfo($this);
$compiler
->write("\$context['$Name'] = NewsController::TwigStoryGetAdjacent(")
->subcompile($this->getNode('value'))
->write(");")
->raw("
")
;
}
}
test.php
<?php
require(__DIR__ . '/vendor/autoload.php');
require("TestExtension.php");
require("TestTokenParser.php");
require("TestNode.php");
class NewsController
{
static public function TwigStoryGetAdjacent($id)
{
return "I return the story ID = {$id}.";
}
}
$loader = new Twig_Loader_Filesystem('./');
$twig = new Twig_Environment($loader, array (
'cache' => __DIR__ . '/gen'
));
$object = new \stdClass;
$object->StoryID = 42;
$twig->addExtension(new Test_Extension());
echo $twig->render("test.twig", array ('Object' => $object));
test.twig
{% story_get_adjacent Object.StoryID as adjacent %}
{{ adjacent }}
TestExtension.php
<?php
class Test_Extension extends \Twig_Extension
{
public function getTokenParsers()
{
return array (
new Test_TokenParser(),
);
}
public function getName()
{
return 'test';
}
}
TestTokenParser.php
<?php
class Test_TokenParser extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$parser = $this->parser; //story_get_adjacent
$stream = $parser->getStream(); // space
$value = $parser->getExpressionParser()->parseExpression(); //story id
$names = array();
try {
$as = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); //as
$ObjName = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); //object name
array_push($names, $ObjName);
} catch (Exception $e) {
throw new Exception( 'error: ' . $e);
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Test_Node($names, $value, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'story_get_adjacent';
}
}
TestNode.php
<?php
class Test_Node extends Twig_Node
{
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
{
parent::__construct(array ('value' => $value), array ('name' => $name), $line, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$Name = reset($this->getAttribute('name'));
$compiler->addDebugInfo($this);
$compiler
->write("\$context['$Name'] = NewsController::TwigStoryGetAdjacent(")
->subcompile($this->getNode('value'))
->write(");")
->raw("
")
;
}
}
Run
$ composer require "twig/twig" "~1.0"
$ php test.php
Result
I return the story ID = 42.
Bonus The compiled doDisplay method corresponding to the template
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
$context['adjacent'] = NewsController::TwigStoryGetAdjacent($this->getAttribute((isset($context["Object"]) ? $context["Object"] : null), "StoryID", array()) );
// line 2
echo twig_escape_filter($this->env, (isset($context["adjacent"]) ? $context["adjacent"] : null), "html", null, true);
}
Alain Tiemblo solves your problem. However, why do you make your life so hard? Why don't you simply use a twig function?
{% set adjacent = story_get_adjacent(Object.StoryID) %}
class StoryExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
\Twig_SimpleFunction('story_get_adjacent', array($this, 'getAdjacent'), array('is_safe' => array('html'))),
);
}
public function getAdjacent($storyId)
{
return NewsController::TwigStoryGetAdjacent($storyId);
}
public function getName()
{
return 'story';
}
}
(the is_safe
option tells twig it's safe HTML, so you don't have to disable escaping when using {{ story_get_adjacent(Object.StoryID) }}
).