Symfony 添加默认模板搜索路径
Symfony add default template search path
我的问题与 this question 类似,但略有不同,由于该问题没有得到解答,我想我会再试一次。
我知道 Symfony 会首先在 app/Resources/FooBundle/views
中查找模板,然后在 FooBundle/Resources/views
中查找模板。我想 "inject" 两者之间的另一个位置,例如
app/Resources/FooBundle/views
BarBundle/Resources/FooBundle/views
FooBundle/Resources/views
我尝试覆盖 \Symfony\Component\HttpKernel\Config\FileLocator
服务并在那里添加路径,但这没有用。我也在 controller::render 方法中尝试了答案 here 但没有成功。 (建议的 'native' 方法不起作用,因为路径来自动态设置。)
作为次要说明,我需要能够在以后(可能是 onRequest)而不是在创建容器时添加此路径。因此,在控制器中使用 EventListener、自定义服务方法或调用是合适的。
解决这个问题的部分挑战是意识到(至少在 Symfony 2.7 中)至少有两种方法来指定模板名称:'old way'(因为没有更好的名称)和后来实现的 'name-spaced' 方法。例如
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');
}
}
这两种方法会导致不同的模板位置搜索,因此必须在两个不同的地方修改代码。
为了修改'old'方式,我覆盖了Kernel::locateResource()方法
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];
}
为了修改命名空间的方式,我添加了一个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')),
);
}
}
我希望这可以帮助任何正在寻找类似解决方案的人。
我的问题与 this question 类似,但略有不同,由于该问题没有得到解答,我想我会再试一次。
我知道 Symfony 会首先在 app/Resources/FooBundle/views
中查找模板,然后在 FooBundle/Resources/views
中查找模板。我想 "inject" 两者之间的另一个位置,例如
app/Resources/FooBundle/views
BarBundle/Resources/FooBundle/views
FooBundle/Resources/views
我尝试覆盖 \Symfony\Component\HttpKernel\Config\FileLocator
服务并在那里添加路径,但这没有用。我也在 controller::render 方法中尝试了答案 here 但没有成功。 (建议的 'native' 方法不起作用,因为路径来自动态设置。)
作为次要说明,我需要能够在以后(可能是 onRequest)而不是在创建容器时添加此路径。因此,在控制器中使用 EventListener、自定义服务方法或调用是合适的。
解决这个问题的部分挑战是意识到(至少在 Symfony 2.7 中)至少有两种方法来指定模板名称:'old way'(因为没有更好的名称)和后来实现的 'name-spaced' 方法。例如
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');
}
}
这两种方法会导致不同的模板位置搜索,因此必须在两个不同的地方修改代码。
为了修改'old'方式,我覆盖了Kernel::locateResource()方法
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];
}
为了修改命名空间的方式,我添加了一个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')),
);
}
}
我希望这可以帮助任何正在寻找类似解决方案的人。