Skip to content
Snippets Groups Projects
Commit 126aab49 authored by Pierre Dureau's avatar Pierre Dureau
Browse files

Merge branch '3449653-2.0.0-beta5-replace-component' into '2.0.x'

First proposal for include

See merge request !262
parents 6c48c665 398d67c9
No related tags found
No related merge requests found
Pipeline #341425 failed
......@@ -22,11 +22,6 @@ use Twig\TwigFunction;
*/
class ComponentNodeVisitor implements NodeVisitorInterface {
/**
* Node name: expr.
*/
const NODE_NAME_EXPR = 'expr';
/**
* The component plugin manager.
*/
......
<?php
namespace Drupal\ui_patterns\Template;
use Drupal\Core\Template\ComponentNodeVisitor as CoreComponentNodeVisitor;
use Drupal\Core\Theme\ComponentPluginManager;
use Twig\Environment;
use Twig\Node\EmbedNode;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\IncludeNode;
use Twig\Node\Node;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TwigFunction;
/**
* Provides a IncludeNodeVisitor to change the generated parse-tree.
*
* @internal
*/
class IncludeNodeVisitor implements NodeVisitorInterface {
/**
* The component plugin manager.
*/
protected ComponentPluginManager $componentManager;
/**
* Constructs a new ComponentNodeVisitor object.
*
* @param \Drupal\Core\Theme\ComponentPluginManager $component_plugin_manager
* The component plugin manager.
*/
public function __construct(ComponentPluginManager $component_plugin_manager) {
$this->componentManager = $component_plugin_manager;
}
/**
* {@inheritdoc}
*/
public function enterNode(Node $node, Environment $env): Node {
return $node;
}
/**
* {@inheritdoc}
*/
public function leaveNode(Node $node, Environment $env): ?Node {
if (!$node instanceof IncludeNode) {
return $node;
}
if ($node instanceof EmbedNode) {
// @todo Embed is a different beast.
return $node;
}
$template = $node->getNode("expr")->getAttribute("value");
if (!preg_match('/^[a-z]([a-zA-Z0-9_-]*[a-zA-Z0-9])*:[a-z]([a-zA-Z0-9_-]*[a-zA-Z0-9])*$/', $template)) {
return $node;
}
$line = $node->getTemplateLine();
$arguments = new Node([
$node->getNode("expr"),
$node->getNode('variables'),
]);
$function = new FunctionExpression(
new TwigFunction(
"include",
["Twig\Extension\CoreExtension", "include"],
),
$arguments,
$line,
);
return $function;
}
/**
* {@inheritdoc}
*/
public function getPriority(): int {
$priority = &drupal_static(__METHOD__);
if (!isset($priority)) {
$original_node_visitor = new CoreComponentNodeVisitor($this->componentManager);
// Ensure that this component node visitor's priority is higher than
// core's node visitor class for components, because this class has to run
// core's class.
$priority = $original_node_visitor->getPriority() + 1;
}
return is_numeric($priority) ? (int) $priority : 0;
}
}
......@@ -5,7 +5,9 @@ declare(strict_types=1);
namespace Drupal\ui_patterns\Template;
use Drupal\ui_patterns\ComponentPluginManager;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\Extension\CoreExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
......@@ -41,6 +43,7 @@ class TwigExtension extends AbstractExtension {
public function getNodeVisitors(): array {
return [
new ComponentNodeVisitor($this->componentManager),
new IncludeNodeVisitor($this->componentManager),
];
}
......@@ -49,6 +52,7 @@ class TwigExtension extends AbstractExtension {
*/
public function getFunctions() {
return [
new TwigFunction('include', [$this, 'include'], ['needs_environment' => TRUE, 'needs_context' => TRUE, 'is_safe' => ['all']]),
new TwigFunction('component', [$this, 'renderComponent']),
new TwigFunction('_ui_patterns_preprocess_props', [$this, 'preprocessProps'], ['needs_context' => TRUE]),
];
......@@ -64,6 +68,40 @@ class TwigExtension extends AbstractExtension {
];
}
/**
* Renders a template.
*
* A hack of Twig\Extension\CoreExtension::include().
* If the template filepath is a component ID, call the renderer service
* instead of the normal include function.
*/
public function include(Environment $env, $context, $template, $variables = [], $withContext = TRUE, $ignoreMissing = FALSE, $sandboxed = FALSE): array|string {
if (!preg_match('/^[a-z]([a-zA-Z0-9_-]*[a-zA-Z0-9])*:[a-z]([a-zA-Z0-9_-]*[a-zA-Z0-9])*$/', $template)) {
return CoreExtension::include($env, $context, $template, $variables, $withContext, $ignoreMissing, $sandboxed);
}
if ($withContext) {
$variables = array_merge($context, $variables);
}
$component = $this->componentManager->find($template);
$definition = $component->getPluginDefinition();
$slots = [];
$props = [];
foreach ($variables as $variable => $value) {
if (isset($definition["slots"][$variable])) {
$slots[$variable] = $value;
}
elseif (isset($definition["props"]["properties"][$variable])) {
$props[$variable] = $value;
}
}
return [
"#type" => "component",
"#component" => $template,
"#slots" => $slots,
"#props" => $props,
];
}
/**
* Render given component.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment