Overriding the way Twig finds the views? - php

I'm new to Symfony, and so far I just love it!
Right now, I'm playing around with different personal hobby projects and I've come across something I do not seem to be able to wrap my head around.
I'm running Symfony 3.3.6 right now. I'm trying to kind of add a theme functionality to my project. Right now, I've got my Bundles that got their own views, which I can overwrite inside of app/Resources/views/. So right now, out of the box Symfony kind of supports a base view, with overwriting possibility.
What I would like to achieve is one more level of fallback.
I would like to make twig FIRST look into a theme folder, where the [THEME] would be a config.
1) Look for file inside of /app/theme/[THEME]/resources/view
2) Look for file inside of /app/theme/[THEME]/resources/[BUNDLE]/view
I would also like this to happen from inside a bundle, for example ThemeBundle.
Basically I would like to have a Bundle, that would take of the logic for Twig on how to find the views. It's really important that all the code, right now, would still work. So I do not wish to add a a path variable or something similar, but I would actually like to modify the behavior of TwigBundle from my own Bundle!
Is this sort of thing possible?

Twig has a built-in feature to achieve what you want called namespaces.
Twig will always load the first template that exists in a namespace, starting from the first configured path.
Configure the paths in your app/config/config.yml like this:
twig:
# ...
paths:
'%kernel.project_dir%/vendor/acme/themes/theme1': theme
'%kernel.project_dir%/vendor/acme/themes/theme2': theme
'%kernel.project_dir%/vendor/acme/themes/common': theme
Then use the namespace in your template like this:
{{ include('#theme/header.twig') }}
See the documentation chapter Multiple Paths per Namespace.
In order to dynamically add more paths living inside bundle directories you can use a compiler pass inside the corresponding bundle to add them.

Related

Proper dev. approach for overriding Twig templates

I recently decided to take a look at Sylius, since I love the idea of a developer-friendly Symfony2 project.
I tried to look through the various documentation articles, but I didn't seem to be able to find the answer for a very fundamental question that I have: what is the recommended way to start development on a new web-store, which will include (at the very least), the ability to implement one's own HTML template designs, and still be able to easily upgrade Sylius to future versions?
The best approach that I was able to come up with is to create a new bundle (in my case, named WebBundle) which is based on the default SyliusWebBundle. Here's the problem. In order to get the bare minimum of allowing Sylius to use the templates in my bundle, rather than the default one, I had to go through many hoops. Here are several things I have done so far:
Copied the contents of the original Controller directory from SyliusWebBundle. Changed return values to use WebBundle rather than SyliusWebBundle a part of the string in the argument to $this->render(), as well as the class namespaces.
Copied the YAML files in the Resources/config/routing directory from SyliusWebBundle to my bundle. Changed SyliusWebBundle references in the YAML files, similar to the above.
Added new sections to app/config/config.yml, specifically this part (intended to override the contents of addCheckoutSection() in Sylius\Bundle\CoreBundle\DependencyInjection\Configuration):
sylius_core:
# ...
checkout:
steps:
security:
template: 'WebBundle:Frontend/Checkout/Step:security.html.twig'
addressing:
template: 'WebBundle:Frontend/Checkout/Step:addressing.html.twig'
shipping:
template: 'WebBundle:Frontend/Checkout/Step:shipping.html.twig'
payment:
template: 'WebBundle:Frontend/Checkout/Step:payment.html.twig'
finalize:
template: 'WebBundle:Frontend/Checkout/Step:finalize.html.twig'
I have a lot more work in changing all the default controller references in the YAML files in Resources/config/routing/frontend directory, but before I proceed onward, I need to know if this is the correct approach, or if I'm going down the wrong path.
My goal is to make the store as easy to upgrade as possible with new releases of Sylius, so I'd like to avoid modifying core library files, and instead selectively overriding functionality using my own bundles, as needed.
However, Sylius currently doesn't yet appear to be "geared" towards this approach, unless I missed something.
The fact that I had to override functionality from more than one bundle (CoreBundle as well as WebBundle, per the above YAML section), made me pause with my current approach. I hope someone might be able to steer me in the right direction.
you can override all templates in the app folder (this is part of symfony and works with all bundles):
app/Resources/SyliusWebBundle/views/Frontend/Checkout/Step/
security.html.twig
addressing.html.twig
shipping.html.twig
payment.html.twig
finalize.html.twig

Add new Twig Loader at the top of the Twig Loader chain

I want to create a new TwigLoader class and have it trigger before Symfony's TwigLoader class.
This Stackoverflow question/answer was helpful however I cannot define a weight therefore my custom loader triggers only if Symfony's loader doesn't find the template.
I want that logic to be contained within my bundle so I don't want to:
define my custom TwigLoader in app/config.yml
override Symfony's TwigLoader
Currently, the Symfony Twig loader either fall back on the default Twig loading mechanism or if the path is something like #Bundle:Controller:Action.format.engine will try to load the file #Bundle/Resources/views/Controller/Action.html.twig.
In my case, I want to be able to map #Bundle:Controller:Action.format.engine to a completely different path and if that path doesn't match an existing file then it should fall back on the default behavior (Symfony's Twig Loader).
There are probably other ways to tackle the problem but it seemed to be the most appropriate way to accomplish the task.
I am building a BaseThemeBundle which I'll use on my open source forum project and I want to be able to easily isolate themes from bundles and potentially do all kind of cool stuff. :)
So far I've created a custom Twig loader that looks like Symfony's Twig loader. And I've created a custom Template locator.
Any ideas, solutions or improvements?
For this to work with all the templating engines, i think you should create a new Template Locator (maybe extending Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator) and overwrite in the DI container the default one by setting the alias "templating.locator" to your newly created Template Locator.
To make your template locator class to look first in the folders you want, you have to create a file locator (Symfony\Component\Config\FileLocatorInterface) for the new paths and inject it into your Template Locator class.

Zend Framework 2 - Set Template Path for Module

I googled alot and couldn't come up with an answer...
I'm using the tutorial-skeleton application. It automatically includes under 'view/album/album' the html files corresponding to my actions like add or index.
I'm using a submodule and the standard loading won't find my html-files. I followed this guide for setting a custom template path. This works for the index because here I use a ViewModel instance.
But my add/delete/edit actions just return an array like this one.
Is there a way to tell Zend that it should use a different directory to look for the views?
PS: I also tried this injectTemplate approach but no luck. It just sets the Controller namespace/path which is ok in my case.
This was an project specific issue...
I used MasterData as top namespace. When creating the directory tree in my module\MasterData\view I wrote masterdata instead of master-data. This caused the not finding of my views.
A dumb one... I know.

ZF2: Why does partial() look in the Application module instead in Admin?

Simple question, but can't figure it out.
Why when calling $this->partial() in a view in Zend Framework 2 inside a view in the Admin module, it looks for the file in Application module??
Here's a stack:
include( '/.../module/Admin/view/admin/pages/index.phtml' )
include( '/.../module/Application/view/partials/pagination.phtml' )
I don't really understand what this include stuff is that you do...
You have two ways to assign templates for partials. I always suggest going the fully qualified way, as this is the fastest way possible, too.
$this->partial('NAMESPACE / CONTROLLERNAME / ACTIONNAME', array(/** key value pairs*/));
When you only use ACTIONNAME instead of the fully qualified template name, then the renderer will look inside your current module and sarch for actionname.phtml in said module.
You have to use the fully qualified template name in order to load partials from another module.
As mentioned previously though, ALWAYS go fully qualified. It's a lot faster ;)

Symfony - What is required to create a plugin?

I've been going through the Symfony documentation in order to find out how to create a plugin. However, the two tutorials seem to give a lot of extra information (for example models etc).
What I'd like to know is, what is the absolute minimum requirement in order to get a controller and template working from a plugin directory?
For example, just an index action and a corresponding 'Hello World' template.
Also, is the routing for this automatic or do I have to manually change something?
Any advice appreciated.
Thanks.
To do what youre askign you would need the following:
MyPlugin/
modules/
my_module/
actions/
actions.class.php
templates/
indexSuccess.php
You would then need to enable the plugin in you ProjectConfiguration and also enable the module in your settings.yml for any apps you want to use it.
Routing is not automatic. You need to add routes manually to routing.yml or you can create a listener and appends/prepends the routes when routing.load_configuration is fired. USing the second option would also imply creating a PluginConfiguration class where you listeners connect to the event via the event dispatcher.
Basically a Plugin follows the same basic structure as an application - except pretty much everything is optional. Whether or not you need to do somethign really depends on what your plugin does. Also you might want to take a look at using sfTaskExtraPlugin it has a task for generating a basic plugin skeleton and a plugin module skeleton.
some examples
enable the plugin in you ProjectConfiguration
go to 'core\config\ProjectConfiguration.class.php' and add next code in setup()
$this->enablePlugins('MyPlugin');
enable the module in your settings.yml
all:
.settings:
enabled_modules: [my_module]

Categories