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

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.

Related

Overriding the way Twig finds the views?

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.

Symfony2 bundle as a plugin

Consider situation where I have some default bundle that hypothetically create some empty page with menu on left and some content (if there is any). Then I create a new bundle and I normally turn it on in AppKernel. Now should magic start: bundle by his own (no need to add any options in default bundle etc.) hooks up and creates his menu entry (and if chosen, renders his content). How should I do this, is there any proper way to do this? What if I want to have multiple "hooks", for example, adding also new form in user profile edit, or adding new tab on some other place?
I'm thinking about looking for some "initialize bundle event" that I could listen to and pass data thru it. But maybe there is better solution. I would love to see your ideas :)
Looking at the initializeBundles method of the Kernel, it doesn't look easy or intended to dynamically add bundles during the bootup process.
However, the AppKernel.php file is on the forefront, it is an override of Kernel and can be customized to supply a dynamic set of bundles to the implemented registerBundles method.
You will need to make sure the imported content is properly added to the autoloader, but avoid modifying the distribution source at runtime, try to make it as imported as possible.
I don't want to go into great detail on the technicalities as I have not done this myself and it will require a lot of experimentation. I do know that Drupal 8 uses Symfony2 and has its own plugin system, but I don't think it takes bundles as plugins.
If you manage to pull this off I suspect it will allow 100% integration between the application and the plugins, but just be aware that it also allows 100% overriding access to said plugins.

Where do my non-action/controller classes go in a bundle?

I'm really new to Symfony 2, coming from CI and trying to get me head around where the correct places for everything should go. I've got a bundle that takes care of the few main page types I have, but here's one page element that I use in multiple pages, that can have different configurations for each page.
The logical way around this (as far as I can see) is to have a single class somewhere that all the pages can use... this isn't to be accessed by users so shouldn't go in the controller I'm guessing but where should I put this class?
Make a 'core' type folder within my bundle. Are there naming best practices for this?
Should it go in the vendor folder or is this just for third party bundles?
Make another bundle to somehow use this code... seems a bit overkill for one or two classes?
other?
I guess what you need is to create a service. You can create your own class that has it's own logic and retrieve it using the service container in the controller. The following example is available at Symfony Service Container Docs
$mailer = $this->get('my_mailer');
$mailer->send('ryan#foobar.net', ...);
To make that class available you have to add it in the service.yml file of your bundle like this:
services:
my_mailer:
class: "%my_mailer.class%"
arguments: ["%my_mailer.transport%"]
You can add any other service or parameter to your class via the arguments
More info here: http://symfony.com/doc/current/book/service_container.html

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]

Zend framework Internal structure modification?

What class(FrontController , Bootstrap, Dispacher....) sets up the default structure path in ZF?
There is no single instance that has all the paths. Each component has it's own defaults, e.g. the FrontController knows that the controller directory should be named controllers, but it doesn't know how to make a full path from it (Dispatcher does it) or where to find the Action Helpers. That's defined in ActionHelper Broker. Consequently, Zend_View_Abstract holds the paths for View filters, helpers and scripts, etc.
Like #Pascal mentioned in his comment, you should not modify ZF at it's core. You will lose the changes once you update to a newer version anyway. Configure the paths through the API in your bootstrap or through the application.ini instead.
Actually it's the dispatcher's job to find the requested action controller.
So you'll have to extend either Zend_Controller_Dispatcher_Abstract or Zend_Controller_Dispatcher_Standard or even create a completely new one based on Zend_Controller_Dispatcher_Interface to fit your requirements.
But be aware that you'll have to change the way Zend_Controller_Action_Helper_ViewRenderer tries to find the required view files, too.

Categories