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

时间:2014-08-08 09:46:25

标签: php symfony templating

我想创建一个动态模板定位器。 基本上,我希望在满足某些全局条件时优先考虑某些包中的模板。类似于bundle继承的工作方式,但是运行时动态。

它应该像这样工作: 我有类别'并且每个类别都有自己的捆绑。当要呈现模板时,我会检查当前设置的类别,并在此类别的包中搜索此模板。如果没有找到,则回退到内置机制。

我虽然重写Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator,但这看起来有些过时。如果可能的话,我想模仿bundle继承,但我找不到它的实现位置。

你会怎么做? TemplateLocator是注入逻辑的最佳位置吗?

1 个答案:

答案 0 :(得分:0)

这可能不是最漂亮的解决方案,但它就像一个魅力。

我将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注入所需的包名称(它在编译时'中未知)。这也有点难看。

最棒的是,这适用于所有树枝标记(includeuse,...)。唯一的问题是您无法访问'默认'模板你压倒一切。如果您尝试从覆盖它的模板执行此操作,您将获得嵌套级别(无限递归)错误。

这不是一件大事,因为你可以巧妙地继承'使用组合从基础模板(将模板拆分成较小的模板)。