Open ivan-redooc opened 3 years ago
@samdark I think I can help. Already have an idea, I can share if you like.
Yes, please.
Because the core of ViewContextInterface is the function getViewPath()
we can use it as index of array of FilesystemLoader
.
So my idea (still to test) is:
/**
* @var FilesystemLoader[]
* @since
*/
protected $loaders=[];
//....
public function render($view, $file, $params)
{
$this->twig->addGlobal('this', $view);
if(isset($this->loaders[$view->context->getViewPath()])) {
// I reuse if already created
$loader = $this->loaders[$view->context->getViewPath()];
} else {
// just one time
$loader = new FilesystemLoader(dirname($file));
if ($view instanceof View) {
$this->addFallbackPaths($loader, $view->theme);
}
$this->addAliases($loader, Yii::$aliases);
}
$this->twig->setLoader($loader);
// Change lexer syntax (must be set after other settings)
if (!empty($this->lexerOptions)) {
$this->setLexerOptions($this->lexerOptions);
}
return $this->twig->render(pathinfo($file, PATHINFO_BASENAME), $params);
}
I am having the exact same issue. Did you ever find a solution?
I think I may have solved this by altering https://github.com/twigphp/Twig/tree/3.x/src/Template.php
Whenever we call loadTemplate() I am adding the path of the currently loading template to the list of paths to check
Before
protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
{
try {
if (\is_array($template)) {
return $this->env->resolveTemplate($template);
}
if ($template instanceof self || $template instanceof TemplateWrapper) {
return $template;
}
if ($template === $this->getTemplateName()) {
$class = static::class;
if (false !== $pos = strrpos($class, '___', -1)) {
$class = substr($class, 0, $pos);
}
} else {
$class = $this->env->getTemplateClass($template);
}
return $this->env->loadTemplate($class, $template, $index);
} catch (Error $e) {
if (!$e->getSourceContext()) {
$e->setSourceContext($templateName ? new Source('', $templateName) : $this->getSourceContext());
}
if ($e->getTemplateLine() > 0) {
throw $e;
}
if (!$line) {
$e->guess();
} else {
$e->setTemplateLine($line);
}
throw $e;
}
}
After (added 2 lines of code after the try block)
protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
{
try {
$source = $this->getSourceContext();
$this->env->getLoader()->addPath(dirname($source->getPath()));
if (\is_array($template)) {
return $this->env->resolveTemplate($template);
}
if ($template instanceof self || $template instanceof TemplateWrapper) {
return $template;
}
if ($template === $this->getTemplateName()) {
$class = static::class;
if (false !== $pos = strrpos($class, '___', -1)) {
$class = substr($class, 0, $pos);
}
} else {
$class = $this->env->getTemplateClass($template);
}
return $this->env->loadTemplate($class, $template, $index);
} catch (Error $e) {
if (!$e->getSourceContext()) {
$e->setSourceContext($templateName ? new Source('', $templateName) : $this->getSourceContext());
}
if ($e->getTemplateLine() > 0) {
throw $e;
}
if (!$line) {
$e->guess();
} else {
$e->setTemplateLine($line);
}
throw $e;
}
}
Should I open an issue upstream? Or can we override that functionality from within the yii2-twig implementation of twig?
$this->env->getLoader()->addPath(dirname($this->getSourceContext()->getPath()));
This one line inside loadTemplate() seems to do the trick ;)
Using a "render" inside a model break the ability of Twig to include afterwards includes
What steps will reproduce the problem?
File structure
Files
AlfaController.php
user.php
ViewContext.php
index.twig
profile.twig
footer.twig
What's expected?
A render like this
What do you get instead?
Error: \Twig\Error\LoaderError Message: Unable to find template "footer.twig" (looked into: frontend/views/templates, frontend/views). Throwing point: file: vendor/twig/twig/src/Loader/FilesystemLoader.php line: 227
Additional info
In funtion
render
ofvendor/yiisoft/yii2-twig/src/ViewRenderer.php
a new FilesystemLoader was set inside the $this->twig object, so the next call (by the include) can't find the TWIG in a different path ( in this casefrontend/views/templates
):The $context is not involved in this business-logic.
A possible solution (I'm adopting) is to define in main.php a second view with twig render in $app