Symfony add default template search path

妖精的绣舞 提交于 2019-12-25 06:38:14

问题


My question is similar to this question but slightly different and since that question wasn't answered, I thought I would try again.

I know Symfony will look first in app/Resources/FooBundle/views and then second in FooBundle/Resources/views for templates. I would like to "inject" another location between the two, e.g.

  1. app/Resources/FooBundle/views
  2. BarBundle/Resources/FooBundle/views
  3. FooBundle/Resources/views

I've tried overriding the \Symfony\Component\HttpKernel\Config\FileLocator service and added a path there, but this didn't work. I've also tried the answer here in the controller::render method without success. (the 'native' method suggested there doesn't work because the path comes from a dynamic setting.)

As a secondary note, I need to be able to add this path later (maybe onRequest) instead of at container creation. So an EventListener, custom service method or call in the controller would be appropriate.


回答1:


Part of the challenge in solving this problem was realizing that (at least in Symfony 2.7) there are at least two ways to specify a template name: the 'old way' (for lack of a better name) and the 'name-spaced' method which was implemented later. e.g.

class MyController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
    // the 'old way'
    public function barAction()
    {
        return $this->render('FooBundle:User:foo.html.twig');
    }
    // namespaced
    public function fooAction()
    {
        return $this->render('@Foo/User/foo.html.twig');
    }
}

These two methods result in different template location searches and therefore code had to be modified in two different places.

In order to modify the 'old' way, I overrode the Kernel::locateResource() method

public function locateResource($name, $dir = null, $first = true)
{
    $customBundle = $this->container->get('my_custom_bundle');
    $locations = parent::locateResource($name, $dir, false);
    if ($locations && (false !== strpos($locations[0], $dir))) {
        // if found in $dir (typically app/Resources) return it immediately.
        return $locations[0];
    }

    // add custom path to template locator
    // this method functions if the controller uses `@Template` or `FooBundle:Foo:index.html.twig` naming scheme
    if ($customBundle && (false === strpos($name, $customBundle->getName()))) {
        // do not add override path to files from same bundle
        $customPath = $customBundle->getPath() . '/Resources';
        return parent::locateResource($name, $customPath, true);
    }
    return $locations[0];
}

In order to modify the namespace way, I added a ControllerListener

class OverrideListener implements EventSubscriberInterface
{
    private $loader;
    private $customBundle;

    function __construct(\Twig_Loader_Filesystem $loader, $customBundle)
    {
        $this->loader = $loader;
        $this->customBundle = $customBundle;
    }

    /**
     * @param FilterControllerEvent $event
     * @throws \Twig_Error_Loader
     */
    public function setUpOverrides(FilterControllerEvent $event)
    {
        // add custom path to template locator
        // This 'twig.loader' functions only when @Bundle/template (name-spaced) name-scheme is used
        $controller = $event->getController()[0];
        if ($controller instanceof AbstractController) {
            $bundleName = $controller->getName(); // my AbstractController adds a getName method returning the BundleName
            if ($this->customBundle) {
                $overridePath = $this->customBundle->getPath() . '/Resources/' . $bundleName . '/views';
                if (is_readable($overridePath)) {
                    $paths = $this->loader->getPaths($bundleName);
                    // inject overridePath before the original path in the array
                    array_splice($paths, count($paths) - 1, 0, array($overridePath));
                    $this->loader->setPaths($paths, $bundleName);
                }
            }
        }
    }

    public static function getSubscribedEvents()
    {
        return array(
            KernelEvents::CONTROLLER => array(array('setUpOverrides')),
        );
    }
}

I hope this helps anyone coming along looking for a similar solution.



来源:https://stackoverflow.com/questions/32075460/symfony-add-default-template-search-path

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!