Twig - display template names if debug mode is enabled
Asked Answered
V

4

5

I'm using Twig lately and I was wondering if it is possible to output the template names which are loaded on the page. The best way I can think of is to display the name above the template itself as a html comment.

<!-- start @default/_components/_wrapper/form-wrapper.html.twig -->
    <form>
       ...
    </form>
<!-- end @default/_components/_wrapper/form-wrapper.html.twig -->

I know that I can get the template name by inserting {{ _self.templateName }} but I don't like to add it to every template or partial.

The solution should work for {% include %}, {% use %} etc and it would also be nice if it just happens when debug mode is enabled.

I tried to write an extension but no matter how I put it, I have to make some kind of call in each template.

The reason behind this is I'm trying to reduce the time searching for the templates somebody else implemented since the project is getting bigger and bigger.

Note: I'm NOT using Symfony.

Thanks in advance, any help is appreciated!

Vorous answered 2/2, 2016 at 14:48 Comment(0)
V
3

Thanks to @DarkBee I was pointed to the right direction and ended up using this: I created a debug-template.class.php with the following content:

<?php

abstract class DebugTemplate extends Twig_Template {

    public function display(array $context, array $blocks = array())
    {
        // workaround - only add the html comment when the partial is loaded with @
        if(substr($this->getTemplateName(),0,1) == '@') {
            echo '<!-- START: ' . $this->getTemplateName() . ' -->';
        }

        $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));

        if(substr($this->getTemplateName(),0,1) == '@') {
            echo '<!-- END: ' . $this->getTemplateName() . ' -->';
        }

    }
}
?>

Then I took my index.php and added

require_once 'vendor/twig/twig/lib/Twig/TemplateInterface.php';
require_once 'vendor/twig/twig/lib/Twig/Template.php';

and added the DebugTemplate class

$twig = new Twig_Environment($loader, array(
    'cache' => false,
    'base_template_class' => 'DebugTemplate'
));

The result is just what I want and looks like this

<!-- START: @default/_components/panel.html.twig -->
        <div class="panel panel-default">
<!-- END: @default/_components/panel.html.twig -->
Vorous answered 22/2, 2016 at 9:4 Comment(1)
Exactly the same thing can be done in Symfony, it can be found here.Jorgejorgensen
I
2

The easiest way to achieve this is using your own Template class

<?php
    namespace My\ProjectName\Here;

    abstract class Template extends \Twig_Template {

        public function render(array $context) {
            $level = ob_get_level();
            ob_start();
            try {
                $this->display($context);
            } catch (Exception $e) {
                while (ob_get_level() > $level) {
                    ob_end_clean();
                }
                throw $e;
            }
            $content = ob_get_clean();
            return '<!-- Template  start : ' . $this->getTemplateName() . ' -->'. $content.'<!-- Template  end : ' . $this->getTemplateName() . ' -->';     
        }

And register it into Twig

<?php   
    $loader = new Twig_Loader_Filesystem(__DIR__ . '/../CMS4U/Views');
    $twig = new Twig_Environment($loader, array(
        'base_template_class'   => '\My\ProjectName\Here\Template',
    ));
Imam answered 5/2, 2016 at 8:56 Comment(1)
Thanks @Imam ! I tried that and it kind of worked, but only the whole page template rendered the html comment. I used the display function of Twig_Template instead - that workes like a charm. The only "flaw" to this is that the page template name is echoed along with the included / embedded templates each time.Vorous
S
2

Thanks Frank Hofmann yet another usefull feature is to render existing block begin/end within template as well. Beware that not all twig templates are purely HTML. HTML comments will break CSS or JS code. This can be avoided for example using naming conventions...

<?php
abstract class App_Twig_DebugTemplate extends Twig_Template
{
    public function display(array $context, array $blocks = [])
    {
        $this->_renderComment('BEGIN TEMPLATE: ' . $this->getTemplateName());
        $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
        $this->_renderComment('END TEMPLATE: ' . $this->getTemplateName());
    }

    public function displayBlock($name, array $context, array $blocks = [], $useBlocks = true)
    {
        $this->_renderComment('BEGIN BLOCK: ' . $name);
        parent::displayBlock($name, $context, $blocks, $useBlocks);
        $this->_renderComment('END BLOCK: ' . $name);
    }

    /**
     * @param string $comment
     */
    private function _renderComment($comment)
    {
        $extension = pathinfo($this->getTemplateName(), PATHINFO_EXTENSION);
        if (in_array($extension, [
            'css',
            'js',
        ])) {
            echo '/* ' . $comment . ' */';
        } else {
            echo '<!-- ' . $comment . ' -->';
        }
    }
}
Splitting answered 6/3, 2019 at 16:38 Comment(0)
L
0

This is for symfony 6 twig 3 with possible to ignore template names

<?php


use Twig\Environment;
use Twig\Node\BlockNode;
use Twig\Node\BodyNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\TextNode;
use Twig\NodeVisitor\AbstractNodeVisitor;

class DebugCommentNodeVisitor extends AbstractNodeVisitor
{

    protected function doEnterNode(Node $node, Environment $env)
    {
        return $node;
        $excludedBlockNames = [
            'sonata_type_model_autocomplete_ajax_request_parameters',
            'sonata_type_model_autocomplete_dropdown_item_format',
            'sonata_type_model_autocomplete_selection_format',
            'batch_javascript',
        ];

        if ($node instanceof ModuleNode) {
            $templateName = $node->getTemplateName();
            $extension = pathinfo($templateName, PATHINFO_EXTENSION);

            $node->setNode('body', new BodyNode([
                new TextNode($this->createComment('BEGIN MODULE TEMPLATE: ' . $templateName, $extension), 0),
                $node->getNode('body'),
                new TextNode($this->createComment('END MODULE TEMPLATE: ' . $templateName, $extension), 0),
            ]));
        } elseif ($node instanceof BlockNode) {
            $name = $node->getAttribute('name');
            $sourceContext = $node->getSourceContext();
            if ($sourceContext) {
                $sourceContextName = $sourceContext->getName();
                $extension = pathinfo($sourceContextName, PATHINFO_EXTENSION);

                if (!str_contains($name, 'attributes') && !in_array($name, $excludedBlockNames)) {
                    $commentBegin = $this->createComment("BEGIN BLOCK TEMPLATE: $name, SOURCE CONTEXT: $sourceContextName", $extension);
                    $commentEnd = $this->createComment("END BLOCK TEMPLATE: $name, SOURCE CONTEXT: $sourceContextName", $extension);

                    $node->setNode('body', new BodyNode([
                        new TextNode($commentBegin, 0),
                        $node->getNode('body'),
                        new TextNode($commentEnd, 0),
                    ]));
                }
            }
        }

        return $node;
    }

  

    protected function doLeaveNode(Node $node, Environment $env)
    {
        return $node;
    }

    public function getPriority(): int
    {
        return 0;
    }

    private function createComment(string $comment, string $extension = 'html'): string
    {
        return match ($extension) {
            'css', 'js' => '/* ' . $comment . ' */',
            default => '<!-- ' . $comment . ' -->',
        };
    }
}
Longhorn answered 28/4, 2023 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.