Sandlight CMS II : Mobile First!

mobileFirstI’m not a graphic designer, and so I depend on others for the graphic elements and arrangement of my Web pages. However, I strive to make a site that is clear, easy to understand and useful. My focus is on good user experience (UX) and information design—clear communication with the user. In order to have good UX, you need to know something about Responsive Web Design (RWD), and if you don’t, check out the RWD link. Further, if you are unfamiliar with the approaches to RWD, I’m sold on the Mobile First approach, but possibly for different reasons than designers. Let me explain.

In designing my own site, my focus is on content categories, ease of maintenance, which includes updates and changes, and device flexibility. So I have to keep all of those in mind. I want PHP to handle regular updates by using content from a MySql database (the Content Management of CMS), and I need it to work on different devices. By tackling mobile first, I have to create a diamond-tipped focus on exactly what I want the user to view because even with the new “Phablets,” I’m not dealing with a lot of screen real estate. Currently, my old working mobile phone has a CSS resolution of 320 x 480, and my Phablet is 414 x 736. That’s less that 100 units different. (By CSS resolution, I’m referring to what CSS reads as far as screen width is concerned. See this table.)

Choosing the Devices

In an another sniffer program using a Chain of Responsibility (CoR) design pattern and a PHP user agent ($_SERVER['HTTP_USER_AGENT']) posted on this blog, the sniffer detected the user agent and then found the handler responsible for that agent. Now that user agents have been replaced by CSS screen width (as determined by a JavaScript function) for determining the device, we can use the same CoR pattern making the necessary changes. However, instead of getting real pages, we can use stand-ins that only have the roughest page content. All of the content will be encapsulated into PHP classes using heredoc strings. Near-future posts cover the mechanics of working out the MySql to provide dynamic content for the pages, along with other details necessary for the CMS. For now, though, the dummy pages will only contain enough material to demonstrate that each is appropriate for the selected device. Use the buttons below to see the current state of the CMS and download the files for this portion:
PlayDownload

Note that all devices can now access the Flag Counter. Where is your country on the Flag Counter? (See the note about the Flag Counter at the end of this post.)

Back to the Chain of Responsibility Pattern (CoR)

The CoR pattern is handy because it’s easy to update and change. For example, suppose that having three device categories (e.g., phone, tablets and desktops) proves to be inadequate and you want to add two more; one for laptops and another for phablets. It’s a simple matter to add to the chain and add device classes to handle the new devices. Figure 1 shows the first design pattern to be used in the CMS:

Figure 1: Chain of Responsibility Implementation

Figure 1: Chain of Responsibility Implementation

In Part I of this series, you can see how the device width and height is determined using a JavaScript closure (object) to pass the information to HTML and on to PHP. Since we only need to find the width, the JavaScript code has been slightly altered and placed in a separate file (deviceCatcher.js) in case it needs to be reused.

?View Code JAVASCRIPT
//deviceCatcher.js
function getWide()
{
	var wide = screen.width;
	return function()
	{
		return wide;
	}
}
var w = getWide();
//Send data to PHP class, CoRClient.php	
var lambdaPass= function() {window.location.href = "CoRClient.php?hor=" + w();};

The HTML file simply calls the closure function which passes the values to PHP:

<html>
	<head>
		<title>Device Catcher</title>
		<script src="deviceCatcher.js" type="text/javascript"></script>
	</head>
	<body onload=lambdaPass()>
	</body>
</html>

The HTML file is a trigger to get the ball rolling with the client class (CoRClient).

Starting the Chain

The client pulls the viewing device’s width from the superglobal, and passes it to a PHP variable. Given the variability in the width of device screens, I made the decision to work with three sizes to get started: 1) phone, 2) tablet, and 3) desktop. So, depending on the width, the request would be handled by one of those three device groups. I used the following cutoff sizes:

  1. Phone: >= 480
  2. Tablet: >=481 and < 900
  3. Desktop: >= 900

I used this table as a guide, but cutoff points can be anything you want.

Getting the width from the superglobal is easy enough using a static variable:

self::$wide=$_GET['hor'];

The, using the cutoffs, the program needs to generate three strings, phone, tablet, and desktop to send to the Request class that stores the appropriate string. The most obvious way is to use conditional statements (if or switch) to generate the correct string for Request. For example an imperative algorithm such as the following would do the trick:

if(self::$wide <= 480)
{
	return "phone";
}
elseif (self::$wide >= 900)
{
	return "desktop";
}
else
{
	return "tablet";
}

However, a functional program would be more compact, and like the JavaScript closure used in Part I, it would be an “object.” Transformed into a functional closure, the operation would look like the following:

$beta = self::$wide >= 900 ? 'desktop' : 'tablet';
$lambda = function($x) use ($beta) {
	$alpha =  $x <= 480 ? 'phone' : $beta;
	return $alpha;};

Using ternary operations ?: , $alpha and $beta both have function-like qualities. for example, $beta could have been written as a function beta() as shown in Figure 2:

Figure 2: "Functional" variables

Figure 2: “Functional” variables

As you can see in Figure 2, $beta provides the same functionality as beta(), and $beta can be used as a reference in the $lambda function along with $alpha in a PHP closure. (For some reason, when $beta is assigned an anonymous function, I was unable to get it to be added as a closure in the $lambda anonymous function.)

The CoRClient class passes the width to a variable, decides which device is using the page and passes on that information as a parameter in making a call to the Request class. First, though, the client instantiated handler instances of all of the device categories and then sets the sequence of the chain of command:

<?php
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", 1);
// Autoload given function name.
function includeAll($className)
{
    include_once($className . '.php');
}
spl_autoload_register('includeAll');
 
class CoRClient
{
	private static $wide;
 
	public static function getSize()
	{
		//Instantiate Concrete Handlers
		$Phone = new Phone();
                $Tablet = new Tablet();
		$Desktop=new Desktop();
                $Phone->setSuccessor($Tablet);
		$Tablet->setSuccessor($Desktop);
 
		//Create function with closure to determine
		//which device is viewing the page
		self::$wide=$_GET['hor'];
		$beta = self::$wide >= 900 ? 'desktop' : 'tablet';
		$lambda = function($x) use ($beta) {
			$alpha =  $x <= 480 ? 'phone' : $beta;
			return $alpha;};
 
        // Generate and process requests
        $startChain = new Request($lambda(self::$wide));
        $Phone->handleRequest ($startChain);
	}
}
CoRClient::getSize();
?>

The $startChain variable is an instance of Request with the value of the device as parameter in the $lambda closure.

The Request class is a helper class that can be referenced by the concrete handlers to find whether to handle the device request or pass it on down the line to the next handler in the chain.

<?php
class Request
{
    private $value;
 
    public function __construct($device)
    {
        $this->value=$device;
    }
 
    public function getDevice()
    {
        return $this->value;
    }
}
?>

You can think of the CoRClient and Request classes as a single class to set up the chain and to get the ball rolling down the chain to find the correct handlers.

The Device Handlers

With only three device categories, the pattern only requires three device handlers in the chain. All three implement an interface made up of a DeviceHandler abstract class:

<?php
abstract class DeviceHandler
{
	protected $site;
	abstract public function handleRequest(Request $request); 
	abstract public function setSuccessor(DeviceHandler $nextDevice);		
}
?>

The DeviceHandler interface has two methods to be instantiated by the child classes; the specific device handlers. The handleRequest() method expects a device type as an argument, and the setSuccessor() expects a concrete device handler as an argument. The type hinters, Request and DeviceHandler provide a good idea of what the two methods will do. Furthermore, the DeviceHandler type hint is inside the DeviceHandler abstract class; making it a self-referent. That’s okay, though, because the concrete instances of the device handler use the method to point to the next object in the chain of responsibility. So, while it looks to be a type of recursion, it is in fact a sequencing method necessary for the chain. The following three classes are concrete implementations of the DeviceHandler:

 
<?php
//PHONE
class Phone extends DeviceHandler
{
    private $successor;
 
    public function setSuccessor(DeviceHandler $nextDevice)
    {
        $this->successor=$$nextDevice;
    }
 
    public function handleRequest (Request $request)
    {
        if ($request->getDevice()=='phone')
        {
           $this->site=new PhoneDevice();
        }
        else if ($this->successor != NULL)
        {
                $this->successor->handleRequest ($request);
        }
    }
}
?>
 
<?php
//TABLET
class Tablet extends DeviceHandler
{
    private $successor;
 
    public function setSuccessor(DeviceHandler $nextDevice)
    {
        $this->successor=$nextDevice;
    }
 
    public function handleRequest (Request $request)
    {
        if ($request->getDevice()=='tablet')
        {
           $this->site=new TabletDevice();
        }
        else if ($this->successor != NULL)
        {
                $this->successor->handleRequest ($request);
        }
    }
}
?>
 
 
<?php
//DESKTOP
class Desktop extends DeviceHandler
{
    private $successor;
 
    public function setSuccessor(DeviceHandler $nextDevice)
    {
        $this->successor=$nextDevice;
    }
 
    public function handleRequest (Request $request)
    {
        $this->site=new DesktopDevice();
    }
}
?>

As you can see the Desktop class has fewer lines of code because it represents the end of the chain, and so it has no successors. The other two check to see if this stop in the chain can handle the device, and if so, they call the handler for the device. Otherwise, they pass it along.

The Devices

The device handlers “handle” the request by calling in the correct PHP class with the associated formatting for different device-specific HTML pages bound in heredoc strings. For now, I’ve just added bare bones pages to finish this section of the CMS project. The next segment adds dynamic content to the pages in the form of a MySQL database for retrieving information. However, to keep things moving, I placed the page for the site at the Sandlight Productions site. The PhoneDevice class uses jQuery Mobile materials, but the other two use HTML5 with just a little CSS.

<?php
//PHONE
class PhoneDevice
{
	function __construct()
	{
		$setPage = <<<PHONE
		<!DOCTYPE html> 
		<html>
		<head>
		<title>Sandlight Productions, LLC</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css" />
		<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
		<script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>
		<style>
		.center {   display: block;   margin-left: auto;   margin-right: auto; }
		</style>
		</head>
		<body>
		<div data-role="page">
		  <div data-role="header">
			<h1>Sandlight<br/> Productions, LLC</h1>
		  </div>
		  <div data-role="content"> <img src="phonelogo.png">
			<p>Currently, this is Stage 2 of the new CMS for Sandlight. You should be able to view this on differnt devices, including this one for a smart phone viewing. To follow the project take a look at the PHP Design Pattern link below.</p>
		  </div>
		  <ul data-role="listview" data-inset="true" data-theme="e">
			<li><a href="http://www.php5dp.com" target="_blank">PHP Design Patterns</a></li>
			<li><a href="http://www.sandlight.com/sandlight/" target="_blank"">Functional Programming</a></li>
			<li><a href="#">Mobile Apps</a></li>
			<li><a href="#">About Us</a></li>
			<li><a href="#">Contact</a></li>
		  </ul>
		  <div data-role="content"> 
		  <img class="center" src="covers/cat.gif">
		  </div>
 
		 <a href="http://s04.flagcounter.com/more/prcT"><img src="http://s04.flagcounter.com/count/prcT/bg=eeeeee/txt=000000/border=BFD9CA/columns=3/maxflags=18/viewers=0/labels=1/pageviews=1/" alt="free counters" border="0"></a>
		</div>
		</body>
		</html> 
PHONE;
echo $setPage;
	}
}
?>
 
<?php
//TABLET
class TabletDevice
{
	function __construct()
	{
		$setPage = <<<TABLET
		<!DOCTYPE html> 
		<html>
		<head>
		<title>Sandlight Productions, LLC</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="sandlight.css" />
		</head>
		<body>
		<image src="tabletlogo.png">
		<div>
		<head><h2>Sandlight Productions, LLC </h2></head>
		</div>
		<h3>This is the Tablet View</h3>
 
		<p>Currently, this is Stage 2 of the new CMS for Sandlight. You should be able to view this on differnt devices, including this one for Desktop viewing. To follow the project take a look at the PHP Design Pattern link below.</p>
		<div><h4>Links</h4></div>
		<div>
		&nbsp;&nbsp;&nbsp;<a href="http://www.php5dp.com" target="_blank">PHP Design Pattern Blog</a><br /><br />
		</div>
		<div>
		&nbsp;&nbsp;&nbsp;<a href="http://www.sandlight.com/sandlight/" target="_blank">Sandlight Functional Programming</a><br /><br />
		</div>
		<p>
		 <a href="http://s04.flagcounter.com/more/prcT"><img src="http://s04.flagcounter.com/count/prcT/bg=eeeeee/txt=000000/border=BFD9CA/columns=3/maxflags=18/viewers=0/labels=1/pageviews=1/" alt="free counters" border="0"></a>
		</p>
		</body>
		</html> 
TABLET;
		echo $setPage;
			}
		}
?>
 
<?php
//DESKTOP
class DesktopDevice
{
	function __construct()
	{
		$setPage = <<<DESKTOP
		<!DOCTYPE html> 
		<html>
		<head>
		<title>Sandlight Productions, LLC</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="sandlight.css" />
		</head>
		<body>
		<image src="logoTight.png">
		<div>
		<head><h2>Sandlight Productions, LLC </h2></head>
		</div>
		<h3>This is the Desktop View</h3>
 
		<p>Currently, this is Stage 2 of the new CMS for Sandlight. You should be able to view this on differnt devices, including this one for Desktop viewing. To follow the project take a look at the PHP Design Pattern link below.</p>
		<div><h4>Links</h4></div>
		<div>
		&nbsp;&nbsp;&nbsp;<a href="http://www.php5dp.com" target="_blank">PHP Design Pattern Blog</a><br /><br />
		</div>
		<div>
		&nbsp;&nbsp;&nbsp;<a href="http://www.sandlight.com/sandlight/" target="_blank">Sandlight Functional Programming</a><br /><br />
		</div>
		<p>
		 <a href="http://s04.flagcounter.com/more/prcT"><img src="http://s04.flagcounter.com/count/prcT/bg=eeeeee/txt=000000/border=BFD9CA/columns=3/maxflags=18/viewers=0/labels=1/pageviews=1/" alt="free counters" border="0"></a>
		</p>
		</body>
		</html> 
DESKTOP;
		echo $setPage;
			}
		}
?>

Certain static materials are on every Web site, and these bare bone pages represent the static materials and links to this blog and a new blog for working through functional programming on plain vanilla PHP and seeing where it can take us. The dynamic portion, along with the design pattern to build it, will be the topic of the next post on this blog.

Why the Flags?

In some corner of my mind I believe that programmers make up an international brotherhood and sisterhood that represents an ideal state of mutual respect, interest and exploration. We’d make up a good team to explore space. Some years ago, I was working on a project for Adobe Systems, and the team was made up of a programmers in Peru, India with sites in those two countries plus San Francisco and Connecticut in the United States. In other projects I’ve worked with programmers in the U.S., Canada, Germany, and the U.K. Books I’ve written have been translated in Spanish, Portuguese, Korean, Chinese, Japanese, French, German, Russian, Polish, Dutch, Arabic as well as others——many of which I’ve never seen. So for me, whenever I see a new flag or the number of flag hits from different countries climb, I’m reminded of this international group of programmers who have a wide ranges of interests and skills. So, drop by the flag counter page and see where different programmers are coming from and add your own flag.

Share

Copyright © 2015 William Sanders. All Rights Reserved.

0 Responses to “Sandlight CMS II : Mobile First!”


  • No Comments

Leave a Reply