Symfony2 twig 移动模板后备

Posted

技术标签:

【中文标题】Symfony2 twig 移动模板后备【英文标题】:Symfony2 twig mobile template fallback 【发布时间】:2013-12-06 11:47:50 【问题描述】:

如果不存在移动版本,我需要一种简单的方法来回退到默认模板。

通过一些正则表达式,我可以识别移动平台并希望使用以下模式呈现模板:

<template_name>.mobile.html.twig

但是如果这个模板不存在,我希望它自动回退:

<template_name>.html.twig

永远存在。

我尝试了这篇文章的几乎所有答案: Symfony 2 load different template depending on user agent properties 但没有成功。遗憾的是没有引用版本号。

目前我正在尝试复制和修改默认的树枝加载器。

顺便说一句,我想要实现的是通过添加同名模板并添加 .mobile 来为移动设备部署不同模板的可能性。

更新:

http://www.99bugs.com/handling-mobile-template-switching-in-symfony2/ 这也是一个很好的方法。它修改了请求对象的格式属性,当您没有在控制器中使用渲染函数(或注释)指定模板而只返回一个数组时,这会影响自动模板猜测。

生成的模板名称:

view/<controller>/<action>.<request format>.<engine>

因此您可以根据设备检测将请求格式从 html 切换为 mobile.html。 这样做的缺点是每个模板都需要一个 mobile.html 挂件(如果不需要,它可以只包含非移动版本)。

更新:

除了使用自定义模板提供程序之外,还可以使用hook into the kernel.view event。

【问题讨论】:

你可以创建一个监听器来捕捉不存在的模板错误。您可以通过使用不同的模板返回响应或重定向到仅限移动设备的 url 甚至在带有一些巧妙变量或 JS 的模板上在控制器级别执行此操作(我会在此处执行此操作)。您甚至可以在路由级别上实现它。您可以定义自定义加载器服务。可能性是无限的:) 【参考方案1】:

您可以创建一个服务来处理它,然后以与执行模板服务相同的方式使用它..

创建一个注入模板和请求服务的服务..

服务(YAML)

acme.templating: 
    class: Acme\AcmeBundle\Templating\TemplatingProvider
    scope: request
    arguments:
        - @templating
        - @request // I assume you use request in your platform decision logic,
                   // otherwise you don't needs this or the scope part
        - 'html' 

class TemplatingProvider

    private $fallback;
    private $platform;
    ... __construct($templating, $request, $fallback) etc

    private function setPlatform() ... Your platform decision logic

    private function getPlatform()
    
        if (null === $this->platform) 
            $this->setPlatform();
        

        return $this->platform;
    

    private function getTemplateName($name, $platform)
    
        if ($platform === 'html') 
            return $name;
        

        $template = explode('.', $name);

        $template = array_merge(
            array_slice($template, 0, -2),
            array($platform),
            array_slice($template, -2)
        );

        return implode('.', $template);
    

    public function renderResponse($name, array $parameters = array())
    
        $newname = $this->getTemplateName($name, $this->getPlatform());

        if ($this->templating->exists($newname)) 
            return $this->templating->render($newname);
        

        return $this->templating->renderResponse($this->getTemplateName(
                                                $name, $this->fallback));
    

然后你可以只调用你的模板服务而不是当前的..

return $this->container->get('acme.templating')
                          ->renderResponse('<template_name>.html.twig', array());

【讨论】:

我就是这么想的。谢谢! 嗯...当我尝试您的解决方案时,我有点不满意。当只在控制器中返回一个数组时,我真的很想获得基于控制器和动作名称的自动模板猜测。但我认为这种方式现在还可以。 您的意思是在为模板使用注解时? 是的,我刚刚遇到了另一个问题。 The controller must return a response (&lt;!DOCTYPE html&gt;... given TemplatingProvider 的渲染函数似乎没有返回响应对象,而是返回了计划 html 文本,尽管它返回了本机渲染函数的返回值。有什么想法吗? 好吧,好吧...渲染函数应该只返回 html。只有 aof 控制器中的快捷方式 $this->render 将其包装成响应。【参考方案2】:

你不能检查模板之前是否存在吗?

if ( $this->get('templating')->exists('<templatename>.html.twig') ) 
    //  return  this->render(yourtemplate)
 else 
    // return your default template

或:

您可以创建一个通用方法,插入到您的根控制器中,例如:

public function renderMobile($templateName, $params)

    $templateShortName = explode('.html.twig', $templateName)[0];
    $mobileName = $templateShortName.'.mobile.html.twig';
    if ( $this->get('templating')->exists($mobileName) ) 

        return $this->renderView($mobileName, $params);
     else 
        return $this->renderView($templateName, $params)
    

你可以这样做:

return $this->renderMobile('yourtemplate', [yourparams]);

【讨论】:

是的,这会起作用,但我正在寻找一种在控制器之外自动检查它的方法。我试图覆盖树枝加载器,但几种不同的方法都失败了,这方面的文档不是很详细。 目前我认为最好的解决方案,也许我应该为此创建一个服务。【参考方案3】:

您可以通过利用 Symfony2 http://symfony.com/doc/current/cookbook/bundles/inheritance.html 中的包继承属性轻松做到这一点

创建一个包含桌面模板的包 (AcmeDemoDesktopBundle) 创建一个包含您的移动模板 (AcmeDemoMobileBundle) 的包并将父级标记为 AcmeDemoDesktopBundle

然后,当您渲染模板时,只需调用 AcmeDemoMobileBundle:: - 如果模板存在,它将被渲染,否则您将巧妙地退回到桌面版本。不需要额外的代码、侦听器或任何不明显的东西。

当然,这样做的缺点是您将模板从单独的包中移出。

【讨论】:

好主意,但我看不出只有当设备是移动设备时才能使用它。 如果您想显示完整页面,也将无法访问非移动模板(如果存在移动版本)。 @MarkusKottländer - 这个问题表明你已经有了,这只是一种巧妙的回退方式,而不是尝试使用不同的模板名称。 @Qoop - 这假设已经有一个移动交换机并且您只想要移动模板,但很好。【参考方案4】:

您描述的后备行为并不那么容易实现(我们发现很难......)。好消息是我们想要您要求的相同设置,并最终为此使用了LiipThemeBundle。它允许您基于例如设备拥有不同的“主题”。它将为您完成后备部分。

例如:

渲染模板: @BundleName/Resources/template.html.twig

将按顺序渲染和回退:

app/Resources/themes/phone/BundleName/template.html.twig app/Resources/BundleName/views/template.html.twig src/BundleName/Resources/themes/phone/template.html.twig src/BundleName/Resources/views/template.html.twig

编辑:因此,使用这种方法,您可以拥有始终作为最终后备的默认模板,并在需要时为移动设备提供特殊模板。

【讨论】:

以上是关于Symfony2 twig 移动模板后备的主要内容,如果未能解决你的问题,请参考以下文章

在 Twig 模板中使用 Symfony2 控制器返回

Symfony2 - 在 TWIG 模板中获取当前 URL 或路由?

如何在 symfony2 中从数据库中渲染 Twig 模板

使用 Symfony2/Twig 创建引导轮播

Symfony2 中的 Twig CamelCase 过滤器

Twig 和 Symfony2 - 找不到实体