Building a Website with PHP, MySQL and jQuery Mobile, Part 2

Posted: January 31, 2012 in Mobile

 

This is the second part of a two-part tutorial, in which we use PHP, MySQL and jQuery mobile to build a simple computer web store. In the previous part we created the models and the controllers, and this time we will be writing our views.

jQuery mobile

First, lets say a few words about the library we will be using. jQuery mobile is a user interface library that sits on top of jQuery and provides support for a wide array of devices in the form of ready to use widgets and a touch-friendly development environment. It is still in beta, but upgrading to the official 1.0 release will be as simple as swapping a CDN URL.

The library is built around progressive enhancement. You, as the developer, only need to concern yourself with outputting the correct HTML, and the library will take care of the rest. jQuery mobile makes use of the HTML5 data- attributes and by adding them, you instruct the library how it should render your markup.

In this tutorial we will be using some of the interface components that this library gives us – lists, header and footer bars and buttons, all of which are defined using the data-role attributes, which you will see in use in the next section.

Rendering Views

The views are PHP files, or templates, that generate HTML code. They are printed by the controllers using the render() helper function. We have 7 views in use for this website – _category.php, _product.php, _header.php, _footer.php, category.php, home.php and error.php, which are discussed later on. First, here is render() function:

includes/helpers.php

/* These are helper functions */

function render($template,$vars = array()){

	// This function takes the name of a template and
	// a list of variables, and renders it.

	// This will create variables from the array:
	extract($vars);

	// It can also take an array of objects
	// instead of a template name.
	if(is_array($template)){

		// If an array was passed, it will loop
		// through it, and include a partial view
		foreach($template as $k){

			// This will create a local variable
			// with the name of the object's class

			$cl = strtolower(get_class($k));
			$$cl = $k;

			include "views/_$cl.php";
		}

	}
	else {
		include "views/$template.php";
	}
}

The first argument of this function is the name of the template file in the views/ folder (without the .php extension). The next is an array with arguments. These are extracted and form real variables which you can use in your template.

There is one more way this function can be called – instead of a template name, you can pass an array with objects. If you recall from last time, this is what is returned by using the find() method. So basically if you pass the result of Category::find() to render, the function will loop through the array, get the class names of the objects inside it, and automatically include the _category.php template for each one. Some frameworks (Rails for example) call these partials.

Computer Store with PHP, MySQL and jQuery MobileComputer Store with PHP, MySQL and jQuery Mobile

The Views

Lets start off with the first view – the header. You can see that this template is simply the top part of a regular HTML5 page with interleaved PHP code. This view is used in home.php and category.php to promote code reuse.

includes/views/_header.php

<!DOCTYPE html>
<html>
	<head>
	<title><?php echo formatTitle($title)?></title> 

	<meta name="viewport" content="width=device-width, initial-scale=1" /> 

	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.css" />
    <link rel="stylesheet" href="assets/css/styles.css" />
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b2/jquery.mobile-1.0b2.min.js"></script>
</head>
<body> 

<div data-role="page">

	<div data-role="header" data-theme="b">
	    <a href="./" data-icon="home" data-iconpos="notext" data-transition="fade">Home</a>
		<h1><?php echo $title?></h1>
	</div>

	<div data-role="content">

In the head section we include jQuery and jQuery mobile from jQuery’s CDN, and two stylesheets. The body section is where it gets interesting. We define a div with the data-role=”page” attribute. This, along with the data-role=”content” div, are the two elements required by the library to be present on every page.

The data-role=”header” div is transformed into a header bar. The data-theme attribute chooses one of the 5 standard themes. Inside it, we have a link that is assigned a home icon, and has its text hidden. jQuery Mobile comes with a set of icons you can choose from.

The closing tags (and the footer bar) reside in the _footer.php view:

includes/views/_footer.php

	</div>

	<div data-role="footer" id="pageFooter">
		<h4><?php echo $GLOBALS['defaultFooter']?></h4>
	</div>
</div>

</body>
</html>

Nothing too fancy here. We only have a div with the data-role=”footer” attribute, and inside it we print the globally accessible $defaultFooter variable, defined in includes/config.php.

Neither of the above views are printed directly by our controllers. They are instead used by category.php and home.php:

includes/views/home.php

<?php render('_header',array('title'=>$title))?>

<p>Welcome! This is a demo for a ...</p>
<p>Remember to try browsing this ...</p>

<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
    <li data-role="list-divider">Choose a product category</li>
    <?php render($content) ?>
</ul>

<?php render('_footer')?>

If you may recall, the home view was rendered in the home controller. There we passed an array with all the categories, which is available here as $content. So what this view does, is to print the header, and footer, define a jQuery mobile listview (using the data-role attribute), and generate the markup of the categories passed by the controller, using this template (used implicitly by render()):

index.php/views/_category.php

<li <?php echo ($active == $category->id ? 'data-theme="a"' : '') ?>>
<a href="?category=<?php echo $category->id?>" data-transition="fade">
	<?php echo $category->name ?>
    <span><?php echo $category->contains?></span></a>
</li>

Notice that we have a $category PHP variable that points to the actual object this view is being generated for. This is done in lines 24/25 of the render function. When the user clicks one of the links generated by the above fragment, he will be taken to the /?category=someid url, which will show the category.php view, given below.

<?php render('_header',array('title'=>$title))?>

<div>
	<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="c">
        <?php render($products) ?>
    </ul>
</div>

<div>
    <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
        <li data-role="list-divider">Categories</li>
        <?php render($categories,array('active'=>$_GET['category'])) ?>
    </ul>
</div>

<?php render('_footer')?>

This file also uses the header, footer and _category views, but it also presents a column with products (passed by the category controller). The products are rendered using the _product.php partial:

<li>
	<img src="assets/img/<?php echo $product->id ?>.jpg" alt="<?php echo $product->name ?>" />
	<?php echo $product->name ?> <i><?php echo $product->manufacturer?></i>
	<b>$<?php echo $product->price?></b>
</li>

As we have an image as the first child of the li elements, it is automatically displayed as an 80px thumbnail by jQuery mobile.

One of the advantages to using the interface components defined in the library is that they are automatically scaled to the width of the device. But what about the columns we defined above? We will need to style them ourselves with some CSS3 magic:

assets/css/styles.css

media all and (min-width: 650px){

	.rightColumn{
		width:56%;
		float:right;
		margin-left:4%;
	}

	.leftColumn{
		width:40%;
		float:left;
	}

}

.product i{
	display:block;
	font-size:0.8em;
	font-weight:normal;
	font-style:normal;
}

.product img{
	margin:10px;
}

.product b{
	position: absolute;
	right: 15px;
	top: 15px;
	font-size: 0.9em;
}

.product{
	height: 80px;
}

Using a media query, we tell the browser that if the view area is wider than 650px, it should display the columns side by side. If it is not (or if the browser does not support media queries) they will be displayed one on top of the other, the regular “block” behavior.

We’re done!

In the second and last part of this tutorial, we wrote our views to leverage the wonderful features of jQuery mobile. With minimal effort on our part, we were able to describe the roles of our markup and easily create a fully fledged mobile website.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s