11

Who defines the URLs to be redirected to the mobile version?

Lets take twitter as an example:

  • https://twitter.com -> redirect mobile
  • https://dev.twitter.com -> not redirect mobile

In an MVC application who would be responsible for the rule redirects?
Could be all in www has redirection to mobile device?

tereško
  • 58,060
  • 25
  • 98
  • 150
Papa Charlie
  • 625
  • 8
  • 31

4 Answers4

4

One way is to use .htaccess or equivalent tools to redirect from server (apache is not the only webserver) and force a redirect. It's already covered in other answers. But there is a different approach to. One that actually would utilize MVC: do not redirect at all.

Brief background on MVC design pattern

In correctly implemented MVC, view instances would contain all the UI logic. They would acquire data from model layer (the "how" constitutes most of the difference between Model2, MVP and MVVM), decide from which templates to assemble the response or even if the response need anything more then an HTTP location header.

The difference between mobile and desktop version would be contained to the view instances, assisted by controllers, which in correct MVC structure would only change the state of model layer and current view. What changes it makes should depend on user input.

Setting it all up

The following code would be part of bootstrap.php or init.php:

// the request instance acts like abstraction  for all the user input
$request = new Request;
$request->setUri();

// instantiate the routing mechanism
$router = new Router( new RouteBuilder );
$router->import('/path/to/config.file');

// apply rules from router so that request instance now 
// contains the parsed values from URI
$router->route( $request );

// handling of model layer
$serviceFactory = new ServiceFactory;

// since in MVC the controllers are closely tied to views 
// (1 controller for 1 view), usually it is convenient to use same class names
$resource = $request->getParameter('resource');
    
// instantiation of view and controller
$class = '\\View\\' . $resource;
$view = new {$class}( $serviceFactory );

$class = '\\Controller\\' . $resource;
$controller = new {$class}( $serviceFactory, $view);

// i find it convenient to have controller's action be made from 
// both REQUEST_METHOD and command name
$action = $request->getMethod() . $request->getParameter('command');

// run it all
$controller->{$action}( $request );
echo $view->render();

In this way, when execution hits the controller's action, it is provided with a fully prepared instance of Request. Said instance is what determines the details about user's equipment and provides a simple interface for reading these details.

The controller also has access to the model layer and current view, both of which are injected in it through the constructor.

Deciding what to show.

The most straight-forward way is the let the controller alter the state of current view.

namespace Conroller;

class SomeThing
{
    public function getUserDetails( $request )
    {
        if ( $request->isFromMobile() )
        {
            $this->view->adjustFor( $request->getDeviceType() );
        }
       
        $community = $this->serviceFactory->create('Community');
        $community->loadUser( $request->getParameter('id'));
    }
}

The adjustFor() method in this case informs the current view instance, that it will need use the templates, that are meant for some non-default device.

There is one very important downside for this approach: it violates OCP from SOLID principles (for lazy people: the short version), because you would have to rewrite each controller method, if you decided to add mobile version for an existing project.

.. there is a better way

While the following code is relatively easy to understand, it is a bit flawed:

$resource = $request->getParameter('resource');

// instantiation of view and controller
$class = '\\View\\' . $resource;
$view = new {$class}( $serviceFactory );

It starts to break down even when the you only have to provide both HTML and JSON/XML response. The view starts to accumulate same repeating IFs all over the code. That is a clear sign that you should have used polymorphism, and these lines would be where to do it.

Instead of the above shown code, you could use something like:

$resource = $request->getParameter('resource');

$class = '\\View\\' . $request->getDeviceType . $resource;
$view = new {$class}( $serviceFactory );

Now, when you are having mobile/desktop application you have two classes: \View\DekstopSomething and \View\MobileSomething. They each can have separate logic and request completely different data from model layer.

In the meanwhile, the rest of your code is completely decoupled from the output form.

What are the benefits ?

Few reasons why, instead of using server redirects, you should better choose this approach:

  1. Your application becomes independent from server software

    Not everywhere you will have Apache (high-load sites often use Nginx or Lighttpd instead) and even if you have Apache, your ability to use mod_rewrite would depend on server's configuration.

  2. Unified scheme for all the links in your site

    The link for viewing some news item is always the same, no matter on what device you use it. It makes fro much easier sharing and bookmarking of URLs.

  3. Single point of change

    This approach lets you initially make the site for desktop users and then add the mobile/tablet support without rewriting any of the existing code.

You might also be interested in reading two older post on the subject about implementation of model layer and access control in context of MVC.

Community
  • 1
  • 1
tereško
  • 58,060
  • 25
  • 98
  • 150
2

It depends how you want to detect the mobile user. It can be done by the web server using a rewrite rule. It can be detected via the user-agent script in your PHP code, in which case it would be done by the controller. It can be confusing mostly because there are so many options. Google has a LOT of specifics on this one.

Rewrite ex:

RewriteCond %{HTTP_USER_AGENT}  ^.*iPhone.*
RewriteRule ^(.*)$         m.site.com/$1
BlueRebel
  • 194
  • 10
  • if I use an htaccess redirect any subdomain to be done, including those outside the rule – Papa Charlie Dec 25 '12 at 05:29
  • htaccess was probably more useful in the old cell-phone days when you could detect by MIME (i.e. WAP). That said, Apache mod_rewrite would allow redirects using the user-agent. See example above. – BlueRebel Dec 25 '12 at 05:42
0

As @Supericy states, in most cases the front end (view) is responsible for detecting, because as its name states, it's what gets displayed to the user.

For instance, if you use the mobile detect class, you would simply write on the top of your index.php or similar file, if you intend to use the header() function.

<?php
include 'Mobile_Detect.php';
$detect = new Mobile_Detect();

if ($detect->isMobile()) {
    // Any mobile device. Do stuff
} else {

}

?>

There are more examples on the mobile detect class page https://github.com/serbanghita/Mobile-Detect. You can get really specific to the devices.

There are plenty other ways do achieve this, but this is the simplest in my opinion.

andrewk
  • 3,721
  • 4
  • 30
  • 37
0

Detecting users of mobile devices is best to give to your server, as it's supposed to rule all traffic. If your code leaves in different webroots, or you don't need to show same pages to user with desktop and mobile devices then here's piece of apache config that will solve your problem. Be sure to visit http://detectmobilebrowsers.com/ to grab new version of mobile detection regexp.

# if already mobile - skip this (usually there are will be another config for mobile)
RewriteCond %{HTTP_HOST} !^mobile\.
# put all sites you don't want to redirect here, or compose a regex that will match sites
RewriteCond %{HTTP_HOST} !(dev.twitter.com) 

# taken from http://detectmobilebrowsers.com/
RewriteCond %{HTTP_USER_AGENT} (android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge\ |maemo|midp|mmp|netfront|opera\ m(ob|in)i|palm(\ os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows\ (ce|phone)|xda|xiino [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a\ wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r\ |s\ )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1\ u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp(\ i|ip)|hs\-c|ht(c(\-|\ |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac(\ |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt(\ |\/)|klon|kpt\ |kwc\-|kyo(c|k)|le(no|xi)|lg(\ g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-|\ |o|v)|zz)|mt(50|p1|v\ )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v\ )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-|\ )|webc|whit|wi(g\ |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-) [NC]

# rewrite only sites.
RewriteCond %{HTTP_HOST} (\.|)([^\.]+\.[^\.0-9]+)$

#redirect to mobile
RewriteRule ^(.*)$ http://mobile.twitter.com$1 [L,NS]
Artem L
  • 10,123
  • 1
  • 20
  • 15