Check if template exists before rendering
Asked Answered
V

7

45

is there a way to check if twig template exists before calling to render? A try catch block seems not to work, at least in dev environment, and plus, I prefer a check than the cost of an exception.

This class TwigEngine has an exists() method, but didn't find examples on use.

Varney answered 25/5, 2013 at 16:5 Comment(0)
O
68

The service holding the twig engine if configured as default is 'templating'.

Inside your Controller do the following:

if ( $this->get('templating')->exists('AcmeDemoBundle:Foo:bar.html.twig') ) {
     // ...
}

The alternative would be catching exception the render() method throws like this:

 try {
      $this->get('templating')->render('AcmeDemoBundle:Foo:bar.html.twig')
  } catch (\Exception $ex) {
     // your conditional code here.
  }

In a normal controller ...

$this->render('...')

is only an alias for ...

$this->container->get('templating')->renderResponse($view, $parameters, $response);

... while ...

$this->get('...') 

... is an alias for

$this->container->get('...')

Have a look at Symfony\FrameworkBundle\Controller\Controller.

Outman answered 25/5, 2013 at 16:10 Comment(2)
This is no longer the recommended answer, please check javiers answer below.Pah
This works for Symfony 3.4.* $this->container->get('templating')->exists('@YourBundle/..Path../twigName.html.twig') if there is not $this->container use $this->getContainer()->... instead.Shawanda
D
40

The templating service will be removed in future Symfony versions. The future-proof solution based on the twig service is:

if ($this->get('twig')->getLoader()->exists('AcmeDemoBundle:Foo:bar.html.twig')) {
    // ...
}
Discursive answered 6/2, 2017 at 20:58 Comment(3)
Do you have any reference to support your statement that the templating service will be removed in future Symfony versions?Oneeyed
It should be reference enouhh that Javier is working for SensioLabs ;)Kirovabad
An announcement about deprecating templating: symfony.com/blog/…Sucre
A
28

In case you need to check for template existance from inside twig templates you have to use the array include methods, as described in the documentation:

{% include ['page_detailed.html', 'page.html'] %}
Alon answered 12/1, 2015 at 22:21 Comment(2)
This is pretty handy on the template end. Can have an empty template to safely do nothing when tempate is not there {% include ['page_detailed.html', 'page.html', 'empty-catch-all.html'] %}Omphale
Is there a way to include the template if already not exists only ?Sulfanilamide
K
26

Maybe also an option:

{% include 'AcmeDemoBundle:Foo:bar.html.twig' ignore missing %}

The ignore missing addition tells twig to simply do nothing, when the template is not found.

Khadijahkhai answered 20/9, 2016 at 13:41 Comment(3)
I can't imagine a situation where this would be useful, but it's still a very interesting thing to know of.Lottielotto
it is in cases of dynamically loaded templates. Like if you want to include a specific template by a given name and this template with that name doesnt exist and you don't want a 500 come up than this is a possible optionKhadijahkhai
Thanks - using this to load in templates dynamically as per comment above.Helvetia
M
2

You can do it this way using dependency injection:

use Symfony\Component\Templating\EngineInterface;

public function fooAction(EngineInterface $templeEngine)
{
    if ($templeEngine->exists("@App/bar/foo.html.twig")) {
        // ...
    }
    // ...
}

Tested with Symfony 3.4.

Marchland answered 18/1, 2018 at 2:36 Comment(1)
in the way i have seen with latest flex you dont need the @App reference, if the templates are in their default location just the relative path is enough ie i have templates/Email/campaign.html.twig and @App/Email/campaign.html.twig does not work just Email/campaign.html.twig worksBackstop
A
2

You can use dependency injection to get the Environment that stores the Twig configuration. Like this (in controller):

/**
 * @Route("/{path}")
 */
public function index($path, Twig\Environment $env)
{
    if (!$env->getLoader()->exists('pages/'.$path.'.html.twig')) {
        return $this->render('pages/404.html.twig');
    }
}
Ambrosio answered 17/10, 2019 at 7:31 Comment(0)
P
0

I quite often create abstraction of my controllers, for that I need template overriding. The easiest way for me was to override render template to allow for an array:

    /**
     * @param array<int,string>|string $view
     * @param array<string,mixed> $parameters
     *
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    protected function render(array|string $view, array $parameters = [], Response $response = null): Response
    {
        if (!$this->container->has('twig')) {
            throw new \LogicException(
                'You cannot use the "renderView" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".'
            );
        }
        /**
         * @var Environment $twig
         */
        $twig = $this->container->get('twig');

        if (\is_array($view)) {
            foreach ($view as $template) {
                if ($twig->getLoader()->exists($template)) {
                    return parent::render($template, $parameters, $response);
                }
            }
            throw new \BadMethodCallException(sprintf('Templates %s don\'t exist.', implode(' and ', $view)));
        }

        return parent::render($view, $parameters, $response); // TODO: Change the autogenerated stub
    }

Overriding default functions by some will be frowned upon. If that's an issue for you you can easily rename the function.

Punch answered 27/11, 2023 at 18:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.