我想创建一个动态模板定位器。基本上,当满足某些全局条件时,我希望优先考虑某些bundle中的模板。类似于bundle继承的工作方式,但是是运行时动态的。
应该是这样工作的:我有一些类别每个类别都有自己的bundle。当要渲染模板时,我会检查当前设置的类别,并在该类别的bundle中搜索该模板。如果没有找到,则退回到内置机制。
我想重写Symfony'Bundle'FrameworkBundle'Templating'Loader'TemplateLocator
,但这似乎有点粗俗。如果可能的话,我想模仿bundle继承,但是我找不到它是在哪里实现的。
你会如何处理这个问题?TemplateLocator
是注入我的逻辑的最好地方吗?
这可能不是最漂亮的解决方案,但它很有魅力。
我子类化并重载了TemplateLocator
服务:
class CategoryAwareTemplateLocator extends TemplateLocator
{
/**
* @var string
*/
protected $categoryBundle = null;
/**
* @var string
*/
protected $defaultBundle = null;
/**
* {@inheritDoc}
*/
public function __construct(FileLocatorInterface $locator, $cacheDir = null)
{
parent::__construct($locator, $cacheDir);
}
/**
* {@inheritDoc}
*/
public function locate($template, $currentPath = null, $first = true)
{
if(!$template instanceof TemplateReferenceInterface) {
throw new 'InvalidArgumentException('The template must be an instance of TemplateReferenceInterface.');
}
$templateParameters = $template->all();
$templateBundle = array_key_exists('bundle', $templateParameters) ? $templateParameters['bundle'] : null;
if(
null !== $this->categoryBundle &&
$templateBundle === $this->defaultBundle /* Override only defaultBundle templates */
) {
/* Try our category bundle first */
$categoryTemplate = clone $template;
$categoryTemplate->set('bundle', $this->categoryBundle);
try {
return parent::locate($categoryTemplate, $currentPath, $first);
} catch('InvalidArgumentException $exception) {
/* Just fall through to default mechanism */
}
}
return parent::locate($template, $currentPath, $first);
}
/**
* @param string $defaultBundle
* @param string $categoryBundle
*/
public function setCategoryBundle($defaultBundle, $categoryBundle)
{
$this->categoryBundle = $categoryBundle;
$this->defaultBundle = $defaultBundle;
}
}
所需的包名是在运行时通过setter注入的(它在编译时不知道)。这也有点难看。
伟大的事情是,这适用于所有的小枝标签(include
, use
,…)。唯一的问题是你不能访问你正在重写的"默认"模板。如果您尝试从覆盖它的模板中执行此操作,您将获得嵌套级别(无限递归)错误。
这不是一个大问题,因为你可以巧妙地使用组合(将模板分成更小的模板)从基本模板中"继承"。