Twig und Plugins

Aus Shopware kennt man das Prinzip, dass man beim Erweitern von Templates einfach "parent:" angeben muss und es wird immer das vorher gehende Template mit dem selben Pfad erweitert. So kann man ein Template mehrmals durch eine unbekannte Anzahl von Plugins erweitern lassen. Twig will aber immer einen Namespace haben. Also muss man heraus finden, mit welchen Plugin man anfängt und welches Plugin dann auf das aktuelle folgt oder man auf das Basis-Template gehen muss. Ich hab mich von der Shopware 6 Implementierung inspirieren lassen und ein kleines Beispiel gebaut, bei dem man die ein Template erweitern kann und die Plugin-Namespaces immer dynamisch ergänzt werden.

Die Verzeichnisstruktur des Beispiels ist sehr einfach:

bbcode-image


Diese drei Templates leiten von einander ab:

{% base_extends "index.twig" %}
{% block blubb %}
{{ parent() }}
3<br/>
{% endblock %}



{% base_extends "index.twig" %}
{% block blubb %}
2<br/>
{% endblock %}



<strong>
{% block blubb %}
1
{% endblock %}
</strong>


Am Ende wollen wir folgendes Ergebnis haben:

<strong>
2<br/>
3<br/>
</strong>


Die Basislogik habe ich in einer einfachen Function zusammen gefasst. Hier wird entweder das Plugin heraus gesucht mit dem angefangen werden muss oder das Plugin, das auf das aktuelle folgt und auch dieses Template mitbringt.


function findNewBase($template, $list = [], $currentBase = null) {
$result = 'base';
$found = $currentBase == null; //if null, took the first one
foreach($list as $key => $path) {
if($key == $currentBase) {
$found = true;
}
else if ($found && file_exists($path . '/' . $template)) {
$result = $key;
break;
}
}
return $result;
}


Die Integration wird über ein Token-Parser implementiert.


final class ExtTokenParser extends AbstractTokenParser {
/**
* @var Parser
*/
protected $parser;

private $list = [];

public function __construct(array $list)
{
$this->list = $list;
}

public function getTag(): string
{
return 'base_extends';
}

/**
* @return Node
*/
public function parse(Token $token)
{
$stream = $this->parser->getStream();
$source = $stream->getSourceContext()->getName();
$template = $stream->next()->getValue();

$parts = preg_split("/\//i", preg_replace("/@/", '', $source));
$newBase = findNewBase($template, $this->list, $parts[0]);
$parent = '@' . $newBase . '/' . $template;

$stream->next();

$stream->injectTokens([
new Token(Token::BLOCK_START_TYPE, '', 2),
new Token(Token::NAME_TYPE, 'extends', 2),
new Token(Token::STRING_TYPE, $parent, 2),
new Token(Token::BLOCK_END_TYPE, '', 2),
]);

return new Node();
}
}


Das eigentliche Beispiel sieht dann so aus:

$list = [
'plugin2' => 'templates/path3',
'plugin1' => 'templates/path2',
'base' => 'templates/path1'
];

$loader = new \Twig\Loader\FilesystemLoader();
foreach($list as $plugin => $path) {
$loader->addPath($path, $plugin); //plugin as namespace
}

$twig = new \Twig\Environment($loader);
$twig->addTokenParser(new ExtTokenParser($list));
echo $twig->render('@' . findNewBase('index.twig', $list) . '/index.twig', []);


Und am Ende kommt raus:

bbcode-image


Jetzt fehlt nur noch eine passende include-Funktion und man kann sich selbst ein System bauen, desen Templates sich über Plugins ohne Probleme erweitern lassen. Ich arbeite daran....

Edit: Die vollständige Implementierung mit extends und include ist jetzt auf GitHub zu finden.
User annonyme 2020-01-23 19:12

write comment:
Nine + = 14

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?