Ask your Symfony questions! Pay money and get answers fast! (more info)

Themes in Symfony 2 Symfony

Hi I am trying to create themes for a multitenant application in Symfony2. This is simple to do with Twig:

$loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2))

But I'm not sure how to do this in Symfony2. I just need Symfony to check app/Resources/themes/{current theme} for the view.html.twig file, and then check the bundle for a default view.html.twig file.

I found the Liip Theme bundle but it is far more complex than I need (cookies, service config, etc). I just need to pass a theme value to Symfony's render function, and have Symfony check app/Resources/themes/{current theme} and then check the bundle Resources if there is no file in the theme. There's an element of this in the Liip bundle, but I'm not sure of the best way to implement this in a more direct way. Should I override the render function, or is there a way to set this through Twig in Symfony? Thanks for any help!

https://github.com/liip/LiipThemeBundle/blob/master/Locator/FileLocator.php

foreach ($bundles as $bundle) {
$checkPaths = array();
if ($dir) {
$checkPaths[] = $dir.'/themes/'.$this->activeTheme.'/'.$bundle->getName().$overridePath;
$checkPaths[] = $dir.'/'.$bundle->getName().$overridePath;
}
$checkPaths[] = $bundle->getPath() . '/Resources/themes/'.$this->activeTheme.substr($path, 15);
foreach ($checkPaths as $checkPath) {
if (file_exists($file = $checkPath)) {
if (null !== $resourceBundle) {
throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.',
$file,
$resourceBundle,
$checkPath
));
}

if ($first) {
return $file;
}
$files[] = $file;
}
}

Answers (2)

2011-08-02

Jakub Zalas answers:

What's wrong with setting a theme name on the active theme service?

$this->container->get('liip_theme.active_theme')->setName('my_theme');

You might overload render() method but what for? render() method is a shortcut for:

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

It operates on a templating service.

You might also use your own templating service which would accept liip_theme.active_theme service as a parameter. This way you could hide theme choosing details in the templating service. However, I don't see how it could be useful.

What exactly are you trying to achieve?

<strong>Edit: added more info about the way Symfony uses Twig.</strong>

Twig is integrated in Symfony with TwigBundle. All the services are defined in the vendor/symfony/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml. Reviewing this file should clarify a lot.

You'll notice that Symfony uses <em>Symfony\Bundle\TwigBundle\Loader\FilesystemLoader</em> class instead of <em>Twig_Loader_Filesystem</em> (actually first extends the later). To change this class you'd need to change the value of <em>twig.loader.class</em> parameter.

<strong>Option 1</strong>

One of your options might be changing the class back to <strong>Twig_Loader_Filesystem</strong> but I'm not sure if it's the best one.

You can try it if it's sufficient for you than it's the best (and simplest).

<strong>Option 2</strong>

Symfony's <em>FilesystemLoader</em> has different constructor than original <em>Twig_Loader_Filesystem</em>:

public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser);

the service definition looks like this:


<service id="twig.loader" class="%twig.loader.class%">
<argument type="service" id="templating.locator" />
<argument type="service" id="templating.name_parser" />
</service>


You can overload arguments passed to it by changing service definitions.

The service you're looking for is either templating.locator or file.locator.
You'll find their definitions in the <em>[[LINK href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml"]]vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/templating.xml[[/LINK]]</em> and <em>[[LINK href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml"]]vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml[[/LINK]]</em> files.

Did I help or do you need further info?


LearningSymfony comments:

Thank you for your reply. My goal is actually really simple, I just don't know how to best accomplish it in Symfony2.

When Symfony2 goes to render templates, it checks /app/Resources/views and then path/to/bundle/Resources/views/.

Instead, I want to check /app/Resources/themes/{some theme} and then the normal path/to/bundle/Resources/views/

Twig provides with this with "Twig_Loader_Filesystem" but I'm not sure how to set that value in Symfony.

Thanks!

2011-08-05

David Joos answers:

Hi LearningSymfony,

I'm no expert in Symfony2, as currently I mainly work on 1.3 and 1.4 projects and I'm still getting up-to-speed with this new toy myself... :-)

"I just need to pass a theme value to Symfony's render function, and have Symfony check app/Resources/themes/{current theme} and then check the bundle Resources if there is no file in the theme."
What about the following approach? Use your bundle templates:
{% extends current_theme.'::base.html.twig' %}
...where "current_theme" (eg. 'ThemesXBundle') is based upon a setting on eg. the user attribute? In that way your bundle template will be derived from a certain theme 'base', in this case ThemesXBundle's base.html.twig. This seems to be the easiest way to implement it, by actually not changing anything to the core code or setup.

Here's something interesting in the Symfony documentation as well: http://symfony.com/doc/2.0/book/templating.html#overriding-bundle-templates

Hope this helps!


LearningSymfony comments:

All of these ideas are good, but I think this is the most straightforward approach. Thank you!


LearningSymfony comments:

It isn't clear how to award the prize $. Ideally I could split it between both answers, both are thoughtful ways to solve the problem. I'm contacting site support on this, thanks.