php – Symfony 2 load different template depending on user agent properties-ThrowExceptions

Exception or error:

Is it possible (and how) to

  • determine if a user is using a mobile device
  • force symfony 2 to load different template in that case
  • (and fall back the default html template)

What id like to do is, to load different templates without modifying any controller.

UPDATE

It wasn’t the detection part the real issue here, it’s really nothing to do with symfony. It can be done (load different template) on a controller level:

public function indexAction()
{
    $format = $this->isMobile() ? 'mob' : 'html';
    return $this->render('AcmeBlogBundle:Blog:index.'.$format.'.twig');
}

But can it be done globally? Like a service, or something that execute before every request, and make changes in the templating rules.

How to solve:

Ok, so I don’t have a full solution but a little more than where to look for one ­čÖé

You can specify loaders (services) for templating item in app/config/config.yml

framework:
    esi:             { enabled: true }
    #translator:     { fallback: %locale% }
    secret:          %secret%
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: %kernel.debug%
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:       
        engines: 
           - twig 
        loaders:  [moby.loader]
    default_locale:  %locale%
    trust_proxy_headers: false
    session:         ~

Then define the mentioned loader service:

services:
    moby.loader:
        class: Acme\AppBundle\Twig\Loader\MobyFilesystemLoader
        arguments:    ["@templating.locator", "@service_container"]

After that define your loader service class:

namespace Acme\AppBundle\Twig\Loader;

use Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\Storage\FileStorage;


class MobyFilesystemLoader extends FilesystemLoader
{
     protected $container;

     public function __construct($templatePathPatterns, $container) 
     {
         parent::__construct($templatePathPatterns);
         $this->container = $container;
     }

     public function load(\Symfony\Component\Templating\TemplateReferenceInterface $template)
     {
         // Here you can filter what you actually want to change from html
         // to mob format
         // ->get('controller') returns the name of a controller
         // ->get('name')  returns the name of the template
         if($template->get('bundle') == 'AcmeAppBundle') 
         {
            $request = $this->container->get('request');
            $format = $this->isMobile($request) ? 'mob' : 'html';

            $template->set('format', $format);
         }

         try {
            $file = $this->locator->locate($template);
         } catch (\InvalidArgumentException $e) {
            return false;
         }

         return new FileStorage($file);
      }

      /**
       * Implement your check to see if request is made from mobile platform
       */
       private function isMobile($request)
       {
           return true;
       }
 }

As you can see this isn’t the full solution, but I hope that this, at least, points you to the right direction.

EDIT: Just found out that there is a bundle with mobile detection capabilities, with custom twig engine that renders template file depending on a device that sent request
ZenstruckMobileBundle, although I never used it so… ­čÖé

Answer´╝Ü

Well, you can use LiipThemeBundle.

Answer´╝Ü

You can utilize kernel.view event listener. This event comes to action when controller returns no response, only data. You can set reponse according to user agent property. For example

In your controller,

public function indexAction()
{
    $data = ... //data prepared for view
    $data['template_name'] = "AcmeBlogBundle:Blog:index";

    return $data;
}

And the in your kernel.view event listener,

<?php

namespace Your\Namespace;

use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Templating\EngineInterface;

Class ViewListener
{
    /**
     * @var EngineInterface
     */
    private $templating;

    public function __construct(EngineInterface $templating)
    {
        $this->templating = $templating;
    }

    public function onKernelView(GetResponseForControllerResultEvent $event)
    {
        $data = $event->getControllerResult(); //result returned by the controller
        $templateName = $data['template_name'];

        $format = $this->isMobile() ? 'mob' : 'html'; //isMobile() method may come from a injected service
        $response = $this->templating->renderResponse($templateName . "." . $format . "twig", $data);

        $event->setResponse($response);
    }
}

Service definition,

your_view_listener.listener:
    class: FQCN\Of\Listener\Class
    arguments:    [@templating]
    tags:
        - { name: kernel.event_listener, event: kernel.view, method: onKernelView }

Answer´╝Ü

This is what did the trick for me in Symfony 2.0:

Override twig.loader service so we can set our custom class:

twig.loader:
    class: Acme\AppBundle\TwigLoader\MobileFilesystemLoader
    arguments:
        locator:  "@templating.locator"
        parser:   "@templating.name_parser"

And create our custom class, that just sets “mob” format to the templates in case the client is a mobile device:

namespace Acme\AppBundle\TwigLoader;

use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader;

class MobileFilesystemLoader extends FilesystemLoader
{

    public function findTemplate($template)
    {
        if ($this->isMobile()) {
            $template->set('format', 'mob');
        }

        return parent::findTemplate($template);
     }


    private function isMobile()
    {
        //do whatever to detect it
    }
 }

Answer´╝Ü

I would suggest that this is not best handled by the controller but by CSS media queries, and serving a separate stylesheet to different classes of devices based on the results of that CSS media query.
A good intro here:
http://www.adobe.com/devnet/dreamweaver/articles/introducing-media-queries.html

and I would try reading http://www.abookapart.com/products/responsive-web-design in great detail. Some thinking has been done since the book was published, but it will get you headed the right direction.

Answer´╝Ü

From my experiences, you can but by specifying a format in the first place – check these docs, they may be able to assist you

Answer´╝Ü

I think is nothing to do with symfony. Templates are for the VIEW. You may achieve this by using different CSS for the same template to get different layout (template). I am using jQuery and CSS to handle different devices. You may want to look at some source code of the UI from http://themeforest.net/; specifically this template. This is one handles different device.

Answer´╝Ü

Alternative: https://github.com/suncat2000/MobileDetectBundle

I found it quite good compared to https://github.com/kbond/ZenstruckMobileBundle and https://github.com/liip/LiipThemeBundle

Leave a Reply

Your email address will not be published. Required fields are marked *