简体   繁体   English

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

[英]How to create a dynamic template chooser in Symfony2?

I would like to create a dynamic template locator. 我想创建一个动态模板定位器。 Basically, I would like to prioritize templates from certain bundle when some global conditions are met. 基本上,当某些全局条件满足时,我想对特定捆绑包中的模板进行优先排序。 Similar to how bundle inheritance works, but runtime-dynamic. 与捆绑继承类似,但是运行时动态。

It should work like this: I've got kind of 'categories' and each category has its own bundle. 它应该像这样工作:我有一种“类别”,每个类别都有自己的捆绑包。 When a template is to be rendered I would check which category is currently set and search for this template in this category's bundle. 当要渲染模板时,我将检查当前设置的类别,然后在该类别的捆绑包中搜索此模板。 If not found then fallback to built-in mechanism. 如果未找到,则回退到内置机制。

I though about overriding Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocator , but this seems a bit hackish. 我虽然要重写Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocator ,但这似乎有点骇人听闻。 I would like to mimick the bundle inheritance if possible, but I cannot find where it is implemented. 如果可能,我想模仿捆绑继承,但是我找不到实现的地方。

How would you approach this? 您将如何处理? Is TemplateLocator the best place to inject my logic? TemplateLocator是插入我的逻辑的最佳位置吗?

This may not be the prettiest solution but it works like a charm. 这可能不是最漂亮的解决方案,但它就像一个魅力。

I subclassed and overloaded the TemplateLocator service: 我继承并重载了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;
    }
}

The desired bundle name is injected at runtime via setter (it's not known at 'compile-time'). 所需的包名称在运行时通过setter注入(在“编译时”未知)。 This is a bit ugly too. 这也有点丑陋。

The great thing is that this works for all twig tags ( include , use , ...). 很棒的是,这适用于所有树枝标签( includeuse ,...)。 The only problem is that you cannot access the 'default' template you are overriding. 唯一的问题是您无法访问要覆盖的“默认”模板。 If you try to do it from the template that overrides it you will get nesting level (infinite recursion) error. 如果尝试从覆盖它的模板中执行此操作,则会出现嵌套级别(无限递归)错误。

This is not a biggie since you can cleverly 'inherit' from the base templates using composition (splitting the templates into smaller ones). 这不是什么大问题,因为您可以使用合成技巧巧妙地从基础模板“继承”(将模板拆分成较小的模板)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM