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

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

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 Mobile

Computer 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 class="ui-li-count"><?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 class="rightColumn">
	<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="c">
        <?php render($products) ?>
    </ul>
</div>

<div class="leftColumn">
    <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 class="product">
	<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.

Join our newsletter and get our PSDs!19,654 people learn about HTML5, JS and more. Join them!

by Martin Angelov

Martin is a web developer with an eye for design from Bulgaria. He founded Tutorialzine in 2009 and it still is his favorite side project.

32 Comments

  1. John says:

    Fantastic, thanks for the share.

  2. Imran says:

    Your articles are always very good and up to date with technology...

  3. wiglaf says:

    Hi,

    Thank you for the tutorial. One question though. Why do you prefix some views with _ ?

    1. Martin Angelov says:

      This is actually a practice that is popular with the Ruby on Rails framework. When a view starts with an underscore, it would suggest that it is not meant to be used directly. It could be used by other views (like the header and footer), or used to visualize the models (like the _product and _category views). They are also called partials.

  4. Codeforest says:

    Great article as always. I really like the way you design things in your articles like database schema for the 1st part.

    Great.

    1. Martin Angelov says:

      Thank you!

  5. Kamal says:

    Great article for Free!!! that's amazing
    thanks for all

  6. Nice use of variable variable and partial view. Zine nanoFramework :)

  7. Cliff says:

    Nice tut, now i know it's not only wine that gets better with time.

  8. Thanks for great tutorial. You know my teachers in our faculty intend to make our portal be accessed/ viewed on mobile, because we always login to get updates but doing that on mobile is more convenient.

  9. Jermaine says:

    In the demo section when I click on a link my FF 6 freezes for 3 sec ?

    1. Martin Angelov says:

      I don't want to sound like tech support, but a restart should fix this. Transitions between pages are handled by jQuery Mobile entirely and it is optimized to run on even the slowest devices.

  10. Martin Angelov says:

    Thank you for the wonderful comments folks!

  11. Del says:

    Awesome post Martin! Good of you to take the time to put such an in depth tut together :)

  12. slicey says:

    Thanks a lot for sharing this! Keep it up! :)

  13. Martin West says:

    the header statements are in the wrong place - give warnings which you only see on a slow computer. Fix is to move the header statements from main.php to the very beginning of index.php ...

    <?php
    //header('Cache-Control: max-age=3600, public');
    //header('Pragma: cache');
    header('Cache-Control: no-cache, public');
    header('Pragma: no-cache');
    header("Last-Modified: ".gmdate("D, d M Y H:i:s",time())." GMT");
    header("Expires: ".gmdate("D, d M Y H:i:s",time()+3600)." GMT");

    Note, I switched of caching while debugging

  14. Steve says:

    Hi,

    How would i add sub categories into this? i have added a column called SubID which will have the ID of the master category then i dont know the best way to change your code to display sub categories.

    Steve

  15. Robert says:

    Very good article, Martin. One question. I'd like use your approach to build a site that looks good on not only on smartphones and tablets but desktop browsers as well. What technique(s) could one use to render desktop-browser specific views in addition to these jQuery Mobile views?

  16. Lloyd says:

    Great tutorial, can this be released in the android market or itunes with a apk etc files without affecting data and PHP codes.

  17. Rick says:

    Great Demo! I had it working till the hosting company upgraded to php 5.3... I changed the connect error message in the connect.php... I see PDOException' with message 'SQLSTATE[HY000] [2019] Can't initialize character set UTF-8 (path: /usr/share/mysql/charsets/)'

    my hosting company says " What you are seeing are warning messages due to functions used that were deprecated in PHP 5.3. "

    I'm not familiar with the PDO method... to use the MySql DB? Any ideas?

    thanks!

  18. Rick says:

    fixed it... removed the charset in the Try statement in connect.php

    try {
    	$db = new PDO(
    		"mysql:host=$db_host;dbname=$db_name",
    		$db_user,
    		$db_pass
    	);
    

    works now...

    1. Steve says:

      Thanks Rob- this had me beat as well. Perfect demo and tut Martin - supurb resource...

  19. Freud says:

    I was looking at the wrong table, the download is fine apologies for this.

    However I dfo encounter and error when creating the tables in MySQL

    #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''jqm_categories' ( 'id' int(6) unsigned NOT NULL auto_increment, 'name' varc' at line 1

  20. Pool says:

    Hi, I'm doing a project for my university and I feel good using mobile-store ...
    but when I run the code in-store mobile xampp a problem occurs ...

    "The problem is when I try to view the index.php, the browser will download the file without displaying the content ..."

    I hope you can help and congratulations for this valuable tutorial. Hits!

  21. subhendu says:

    hey,
    m using your code and i want to add, add to cart option in the listview of each product, how do i get the product id of the product against which add to cart button is clicked??
    so that i can match it with jqm_products table and retrieve the data.

  22. jeffsui says:

    its good!thank you!

  23. greg says:

    Hi, after seeing the html code generated by php, I notice that home.php displays correctly the html code of _header2.php while that of category.php display the header.php code (called only by home.php).

    I do not understand why category.php header2 displays well (liste, buttons...) but not its code?

    Normally I would just change the _header of a rendered product ​​(after selection from a list)

    I probably have some difficulties to formulate the problem, but guess it's a problem specific to MVC hierarchy ?

  24. Patschi says:

    It's aleady a old post, but I still want to thank yo for it :)
    I learnd with your tutorial how to build a mobile page and also built one for a 3 days event and got a Samsung Tab 2 for it :D
    Thanks!

  25. Krasimir Dimov says:

    Сърдечно благодаря, Мартин!

  26. Abdulqadir says:

    Can you please tell me how to set index.html file for using this code snippest in android phonegap framework for making a mobile websites

  27. Cesar Lopez says:

    Truly amazing bro.. this is a very nice tutorial..

  28. Jeremy Ragsdale says:

    Hey Man, Great Tutorial, I am an ASP.NET MVC guy but have the need to do a PHP project and I love the framework here.

    Do you have any samples of how to add login capabilities to this? I have tried but am struggling.

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links