如何在Symfony2中创建动态模板选择器


How to create a dynamic template chooser in Symfony2?

我想创建一个动态模板定位器。基本上,当满足某些全局条件时,我希望优先考虑某些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,…)。唯一的问题是你不能访问你正在重写的"默认"模板。如果您尝试从覆盖它的模板中执行此操作,您将获得嵌套级别(无限递归)错误。

这不是一个大问题,因为你可以巧妙地使用组合(将模板分成更小的模板)从基本模板中"继承"。