Symfony 2 Load Different Template Depending on User Agent Properties

Symfony 2 load different template depending on user agent properties

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... :)

Symfony2 twig mobile template fallback

You could create a service to handle it and then use it in the same way that you do the templating service like so..

Create a service with the templating and request service injected into it..

Service (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

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));
}

And then you could just call your templating service instead of the current one..

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

Symfony2: Change rendered view with a listener

Here is the solution:

First I have to listen to kernel.view, not kernel.controller.

Then I use the "@templating" service (Thanks Marko Jovanovic for the hint)

So here is my new config.yml:

services:
controller.pre_execute_listener:
class: MyProject\MyBundle\Listener\ControllerListener
arguments: ["@templating"]
tags:
- { name: kernel.event_listener, event: kernel.view, method: preExecute }

Finally here is my listener preExecute function

public function preExecute(\Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event){
//result returned by the controller
$data = $event->getControllerResult();

/* @var $request \Symfony\Component\HttpFoundation\Request */
$request = $event->getRequest();
$template = $request->get('_template');
$route = $request->get('_route');

if(substr($route,0,7) == 'mobile_'){
$newTemplate = str_replace('html.twig','mobile.html.twig',$template);

//Overwrite original template with the mobile one
$response = $this->templating->renderResponse($newTemplate, $data);
$event->setResponse($response);
}
}

Hope this helps!

J.

Symfony2 - How to check if we are being called from a mobile device

I'm sure Symfony has some wrapped command to do this as well but:

$_SERVER['HTTP_USER_AGENT']

will give you the user-agent string which tents to have a string "Android verNum".

Symfony set block content from controller

Since any parent Twig templates handle variables that are passed to their children templates, there's a simpler method to achieve what you want to do. In fact, this method is the basic equivalent of writing content into an entire block from the controller since we're essentially just inserting a passed render variable directly into the contents using {% block %}{{ variable }}{% endblock %}

Start a base layout template with the title block

{# Resources/views/base.html.twig #}
<html>
<head>
<title>{% block title %}{{ page_title is defined ? page_title }}{% endblock %}</title>
{# ... Rest of your HTML base template #}
</html>

The {% block %} part is not necessary but useful if you ever want to override or append to it (using parent())

You can also add a site-wide title so the page_title can be appended if it exists:

<title>{% block title %}{{ page_title is defined ? page_title ~ ' | ' }}Acme Industries Inc.{% endblock %}</title>

Extend this base layout with every child template

{# Resources/views/Child/template.html.twig #}
{% extends '::base.html.twig' %}

{# You can even re-use the page_title for things like heading tags #}
{% block content %}
<h1>{{ page_title }}</h1>
{% endblock %}

Pass the page_title into your render function which references your child template

return $this->render('AcmeBundle:Child:template.html.twig', array(
'page_title' => 'Title goes here!',
));


Related Topics



Leave a reply



Submit