ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Three-Tier Development with PHP 5

by Luis Yordano Cruz
12/09/2004

This article will demonstrate the power of three-tier development in PHP 5, using PEAR::DB_DataObject for the business logic and Smarty for display logic. I assume that you have some familiarity with HTML, Smarty, PEAR::DB_DataObject, MySQL, and PHP 5. If you'd like to brush up on your knowledge, the following articles explain some of the theory:

You should have installed and configured Apache, MySQL, and PHP 5 (or IIS, MySQL 5, and PHP), as well as PEAR.

PEAR::DB_DataObject

DB_DataObject is an abstraction API for database access. It's a SQL builder and data modeling layer built on top of PEAR::DB. It maps database tables to PHP classes and provides easy access to common SQL functions like SELECT, INSERT, UPDATE, and DELETE. This allows developers with weak knowledge of SQL to write database-aware code and encourages a clean distinction between presentation and business logic.

(DB_OO has moved to into PEAR and is now DB_DataObject. If you have old code to update, see notes on converting old db_oo code to DB_DataObjects).

Related Reading

Upgrading to PHP 5
By Adam Trachtenberg

DataObject performs two tasks. First, it builds SQL statements based on the object's variables and the builder methods. Second, it acts as a datastore for a table row. There's a core class, which you extend for each of your tables so that you put the data logic inside the data classes. There's also an included Generator to make your configuration files and your base classes.

DataObject greatly simplifies code that accesses databases, and it makes developing large, data-driven sites easier.

At present, Alan Knowles, the lead developer of PEAR::DB_DataObject, is working on a new project called DBDO, a C implementation of the PEAR package DB_DataObjects, based on libgda. His goal is to create the next generation of PEAR::DB_DataObjects.

SMARTY

Smarty is template engine for PHP intended to separate the content from the presentation in a web page. It uses the GPL license.

Large projects commonly separate the role of the graphic designer from that of the programmer. However, programming in PHP has the tendency to combine those two roles in a person and inside the code. This can bring difficulties when it comes time to change some part of the page's design. If the page mixes content and presentation, the developer has to crawl through the program to find the presentation. Smarty helps to solve this problem.

Combining the Two

The first thing to do when starting this project is to create a workspace in which to store the project's code. Then it's time to configure PEAR::DB_DataObject to connect to the MySQL database MySQL (name: example), map the database tables to PHP classes, and then configure Smarty for the presentation tier for the user. Here are those steps in more detail:

Creating the workspace

Create a folder or directory called dataobjects. That was easy.

Installing PEAR::DB_DataObject

From the command line, type:

>pear install Date
>pear install DB_DataObject
>pear list

INSTALLED PACKAGES:
===================
PACKAGE         VERSION    STATE
Archive_Tar       1.2      stable
Console_Getopt    1.2      stable 
DB                1.6.5    stable
DB_DataObject     1.7.1    stable  *(Goal)
Date              1.4.3    stable
Mail              1.1.3    stable
Net_SMTP          1.2.6    stable
Net_Socket        1.0.2    stable
PEAR              1.3.1    stable
PHPUnit           1.0.1    stable
XML_Parser        1.2.0    stable
XML_RPC           1.1.0    stable

Installing and configuring Smarty

Start by downloading Smarty. (I used version 2.6.5.) Extract Smarty into its own directory. From its libs directory, move the Smarty.class.php, Smarty_Compiler.class.php, Config_File.class.php, and debug.tpl files to the dataobjects directory in your new workspace.

Also move the core and plugins directories with all of their contents. Create new directories named templates, templates_c, configs, and cache.

The resulting dataobjects directory should contain:


|---- cache
|---- configs
|---- core
|---- plugins
|---- templates
|---- templates_c

11/10/2004  11:17 a.m.    <DIR> .
11/10/2004  11:17 a.m.    <DIR> ..
11/10/2004  11:17 a.m.    <DIR> cache
11/10/2004  11:17 a.m.    <DIR> configs
11/10/2004  11:17 a.m.    <DIR> core
11/10/2004  11:17 a.m.    <DIR> plugins
11/10/2004  11:17 a.m.    <DIR> templates
11/10/2004  11:17 a.m.    <DIR> templates_c
07/09/2004  09:48 a.m.  13,105 Config_File.class.php
16/04/2004  03:03 a.m.  5,117 debug.tpl
10/09/2004  02:15 p.m.  65,350 Smarty.class.php
10/09/2004  07:14 p.m.  90,924 Smarty_Compiler.class.php
              4 archivos        174,496 bytes 
              8 dirs   6,699,454,464 bytes libres

Creating the database

Create a new MySQL database named example. It will contain a table named user. Don't worry about the schema yet; that will come later.

Configuring PEAR::DB_DataObject

To build the data objects, create the following files.

configDB.php
<?php
require_once 'DB/DataObject.php';
$config = parse_ini_file('example.ini',TRUE);

foreach($config as $class=>$values) {
    $options = &PEAR::getStaticProperty($class,'options');
    $options = $values;
}
?>

This script creates a connection to the database based on the values in the configuration file example.ini, shown next.

example.ini
[DB_DataObject]
database         = mysql://root:@localhost/example
schema_location  = /dataobjects/schema/
class_location   = /dataobjects/
require_prefix   = /dataobjects/
class_prefix     = DataObjects_
extends_location = DB/DataObject.php
extends          = DB_DataObject

Autobuilding a database schema

The plan consists of building an object-relational mapping of the database, and automatically creating a class from the user table in the example database. All of the field names found in the table will become member variables of the class.

To create the appropriate schema:

C:\PHP\PEAR\DB\DataObject>C:\PHP\php.exe createTables.php \
	C:\dataobjects\example.ini

This will generate User.php:

<?php
/**
 * Table Definition for user
 */
require_once 'DB/DataObject.php';

class DataObjects_User extends DB_DataObject 
{
    ###START_AUTOCODE

    /* the code below is auto generated do not remove the above tag */
    var $__table = 'user';       // table name
    var $user_Id;                // int(11)  not_null primary_key auto_increment
    var $first_Name;             // string(30)  not_null
    var $last_Name;              // string(40)  not_null
    var $email;                  // string(100)  not_null

    /* Static get */
    function staticGet($k,$v=NULL) {
		return DB_DataObject::staticGet('DataObjects_User',$k,$v);
	}

    /* the code above is auto generated do not remove the tag below */
    ###END_AUTOCODE
}
?>

It will also generate example.ini code for the user table schema:

[user]
user_Id    = 129
first_Name = 130
last_Name  = 130
email      = 130

[user__keys]
user_Id = N

Smarty files

It's time to create several files for Smarty:

include.php
1 <?
2   require('Smarty.class.php');
3   $smarty = new Smarty;
4   $smarty->template_dir = 'templates/';
5   $smarty->compile_dir  = 'templates_c/';
6   $smarty->config_dir   = 'configs/';
7   $smarty->cache_dir    =  'cache/';
?>

This script instantiates a new Smarty object for the application.

index.php
1 <? 
2  require("include.php");
3  $smarty->assign('TITLE','ACCESS MySQL DATABASE IN THREE TIERS WITH PHP');
4  $smarty->assign('HEADER','WHAT WISH DO ?');
5  $smarty->display('index.tpl');
?>

This script shows how to assign values to variables with Smarty. It calls include.php on line 2.

insert.php
1  <? 
2  require("include.php"); 
3  $smarty->assign('TITLE','INSERT DATA'); 
4  $smarty->assign('HEADER','Insert Data'); 
5  $smarty->assign('data1','First Name');
6  $smarty->assign('data2','Last Name');
7  $smarty->assign('data3','email');
8  $smarty->display('insert.tpl'); 
?>

Much like the previous script, this adds variables that will appear in the insert.tpl template.

save.php
1   <?
2   require_once('DB/DataObject.php');
3   require('configDB.php');
4   $user = DB_DataObject::factory('user');
5   $user->first_Name = $x;
6   $user->last_Name  = $y;
7   $user->email      = $z;
8   $user_Id = $user->insert();
9   $user->update();
10  echo "<script>location.href='index.php'</script>";
11 ?>
This script saves data by using a PEAR::DataObject for the user table. Line 2 loads the class DataObject, and line 3 calls configdb.php to connect to the database. Line 4 creates an instance of a user object (see User.php). Lines 5 through 7 pass the variables collected from the form in insert.tpl ($x, $y, and $z) in order to save the data in the database. The primary key of the table is an autoincrement column, so it doesn't need a value there. Line 8 inserts the object, and line 9 carries out an update.
view.php
1  <? 
2   require_once('DB/DataObject.php');
3   require('configDB.php');
4   require("include.php");
5   $user = DB_DataObject::factory('user');
6   $user->find();
7   while ($user->fetch()) {
8      $smarty->append('users', array(
          'ID'        => $user->user_Id,
          'FIRSTNAME' => $user->first_Name,     
          'LASTNAME'  => $user->last_Name,     
          'EMAIL'     => $user->email,     
       ));
    }
9   $smarty->assign('TITLE','List Users');
10  $smarty->assign('HEADER','List User');
11  $smarty->assign('data0','User_Id');
12  $smarty->assign('data1','First Name');
13  $smarty->assign('data2','Last Name');
14  $smarty->assign('data3','email');
15  $smarty->display('view.tpl');
16  ?>

This script displays all of the data stored in the user table. It loads PEAR::DataObject and include.php (to assign variables of Smarty) that to be shown in the template view.tpl. Line 5 creates a factory of user objects. Line 6 issues a find, which is equivalent to SELECT * FROM user. After selecting the data, it's time to store it for the template via fetch(). This returns one record at a time, which the code stores in a template variable named users, append()ing each row, named by the keys ID, FIRSTNAME, LASTNAME, and EMAIL.

Lines 9 through 14 assign values to other Smarty variables.

All of these .php files should go in the dataobjects directory.

Now for the templates, there are index.tpl, list.tpl, and save.tpl. Here is their code:

index.tpl
1  <html>
2   <head>
3    <title>{$TITLE}</title>
4     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5   </head>
6   <table align="center">    
7    <tr> 
8     <td>
9       <b>{$HEADER}</b>
10    </td>
11   </tr>
12  </table>    
13  <table width="250" border="1" align="center" >
14   <tr> 
16     <td align="center">
17       <input type="button" name="insert" value="Insert"    
           onclick="javascript:location.href='insert.php';">
18    </td>
19   </tr>
20   <tr> 
21     <td align="center">
22      <input type="button" name="view" value="View" 
          onclick="javascript:location.href='view.php';">
23   </td>
24   </tr>
25  </table>
26  </body>
27 </html>

This template is the main page of the system. It shows two variables, $TITLE and $HEADER, in lines 3 and 9, respectively. These values of the variables come from index.php.

Later comes a table with two buttons, Insert and View, which have those respective actions. If the user clicks on Insert, the server invokes insert.php (see line 17). If the user clicks on View, this calls view.php (see lines 3 and 4 of that script).

insert.tpl
1 <html>
2  <head>
3    <title>{$TITLE}</title>
4     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5  </head>
6  <body>
7     <form name="form1" action="save.php" method="post">
8       <table width="300" border="1" align="center" >
9        <tr>
10          <td align="center">
11            <b>{$HEADER}</b>
12         </td>
13       </tr>
14       <tr>
15         <td>
16           {$data1}
17            <input type="text" name="x">
18         </td>
19       </tr>
20       <tr>
21         <td>
22           {$data2}
23            <input type="text" name="y">
24         </td>
25       </tr>
26       <tr>
27         <td>
28           {$data3}
29            <input type="text" name="z">
30         </td>
31       </tr>
32       <tr>
33          <td align="center">
34            <input type="submit" name="Submit" value="Add">
35            <input type="button" name="Reset" value="Return/Cancel"
                 onclick="javascript:location.href='index.php';">
36          </td>
37        </tr>
38      </table>
39    </form>
40  </body>
41 </html>

This template has a form and two buttons, Add and Return/Cancel.

The user enters data for the first name, last name, and email fields (lines 16, 22, and 28). insert.php expects to receive this information in variables named x, y, and z (lines 17, 23, and 29). Clicking on the Add button calls save.php (see line 7) to perform the save. In the contrary case, if the user clicks on Return/Cancel, it executes index.php (line 35).

view.tpl
1 <html>
2  <head>
3    <title>{$TITLE}</title>
4  </head>
5  <body>
6   <table align="center">
7      <tr>
8         <td align="center">
9         <b>{$HEADER}</b>
10       </td>
11     </tr>
12     </table>
13   <table width="500" border="1" align="center">
14   <tr>
16        <td align="center">
17         <b>{$data0}</b>
18       </td>
19        <td align="center">
20         <b>{$data1}</b>
21       </td>
22        <td align="center">
23         <b>{$data2}</b>
24       </td>
25     <td align="center">
26         <b>{$data3}</b>
27       </td>
28     </tr>
29  {section name=display loop=$users}
30     <tr>
31       <td>
32         {$users[display].ID}
33       </td>
34       <td>
35         {$users[display].FIRSTNAME}
36       </td>
37       <td>
38         {$users[display].LASTNAME}
39       </td>
40       <td>
41         {$users[display].EMAIL}
42       </td>
43     </tr>
44     {/section}
45     <br>
46   </table>
47   <br>
48    <table align="center">
49     <tr>
50        <td align="center">
51          <input name="vol" type="button" value="Return"  
               onclick="javascript:location.href='index.php';">
52       </td>
53     </tr>
54   </table>
55  </body>
56 </html>

This template is able to display all of the data stored in the database example.

The first table shows the HEADER of the table, and the second shows data with its respective header (see lines 17, 20, 23, and 26, with the values coming from view.php). Line 29 shows the extraction of data by looping over the users array from view.php to show the values of the user, ID, FIRSTNAME, LASTNAME, and EMAIL fields (lines 32, 35, 38, and 41).

Finally, the Return button sends users back to the home page.

All of the template files (*.tpl) must go in the templates directory.

Conclusion

Working in three tiers with PHP saves repetitive work and makes for maintainable code, partly due to object oriented programming. It's easy to use the powerful Smarty and PEAR::DB_DataObject together.

Luis Yordano Cruz is a systems engineer in Trujillo PERU with more than five years of experience developing web-based applications with PHP, Java/J2EE, and JavaScript.


Return to the PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.