Introduction

My real job involves working with J2EE applications at this time. It pays the bills. I've worked with ASP in the past and have recently (2003) gotten more into working with PHP. Most of my sidework these days is data driven PHP/MySQL web sites. It's unfortunate that sidework doesn't pay the bills, because I have found PHP to be a very powerful programming platform. My attempt here is to apply some principles and concepts that I have learned in my work with J2EE into my work with PHP.

Value Objects

The purpose of a Value Object is to represent a business entity. In it's simplest form, a VO is a simple data mapping class that mirrors the entity as it exists in the database. A more complex VO may incorporate other functionality in its methods. For example, a VO may have a method that transforms it's data into an XML node or a VO may be able to read it's data from an HTML form. The one thing the VO doesn't have to worry about is the database. The main purpose of the VO is to store data as the data comes in and out of the database.

Data Access Objects

Data Access Objects act as the middle man between the database and the Value Objects. The base DAO class knows specifically how to talk to the database. The base DAO knows which database is being used, what kind of database it is and whether it's supposed to be working with mysql_*, mssql_* or pg_* methods. The base DAO is essentially the Data layer or Model part of the MVC The DAO classes that extend the base DAO class can use the base DAO's methods to select, insert, update and delete data. The extended DAO classes don't have to worry about whether they are talking to a MySQL database or a PostGreSQL database. If you switch from MySQL to PostGreSQL, you can change the base DAO class and the extended DAO's will not be affected. The extended DAO's are the business logic layer or the Controller part of the MVC.
DAO's and VO's Working Together
The extended DAO classes utilize the VO's to retrieve data and update data. If you want a single row of data from the database, based on the record's primary key value, the extended DAO will have a method that returns a single VO. The extended DAO will have a method that returns an array of VO's if you want more than one row of data. The extended DAO handles inserts, updates and deletes with methods that take a single VO as an argument.
The Application
In the example code, I'm presenting some of the functionality of a simple auto sales web site. We'll look at the vehicles that are stored in the database. The database is a MySQL database and the vehicles table is created with the following properties:
CREATE TABLE `vehicles` (
`vehicleid` INT NOT NULL AUTO_INCREMENT,
`year` VARCHAR( 4 ) NOT NULL ,
`make` VARCHAR( 25 ) NOT NULL ,
`model` VARCHAR( 25 ) NOT NULL ,
`color` VARCHAR( 25 ) NOT NULL ,
`price` DOUBLE NOT NULL ,
PRIMARY KEY ( `vehicleid` ) 
);
The Base Value Object
The Base VO has a number of methods that assist in reading forms. Another method that could be implemented here might be a date formatting method. Different databases return date values in many different formats. To keep consistency, you may want to do some string manipulation with the date values to keep them all looking the same.

The Extended Value Object

The VehicleVO class has five properties that reflect the five columns in the vehicles table. A default constructor exists to create an empty VO or if given arguments, a VO filled with data:

<?php
// create a new VO
function VehicleVO($vehicleid0$year""$make"",
                   
$model""$color""$price0.00
{
    
$this->vehicleid $vehicleid;
    
$this->year $year;
    
$this->make $make;
    
$this->model $model;
    
$this->color $color;
    
$this->price $price;
}
?>

Other methods the VO includes are:
function equals($vo) -
Compares itself to another VehicleVO object. Returns true or false.
function copy($vo) -
Copies the contents of another VehicleVO object.
function readForm() -
Reads values from the $_POST array.
function readQuery() -
Reads values from the $_GET array.
function toXML() -
Returns a string that contains it's data represented as an XML node.
function toString()-
Returns a string that contains a delimited line of data.
function getPK() -
Return the value of the primary key field.
You can add any other methods you may need to the VO. For example, if you wanted to have a method that returned the vehicle year, make and model with the year in red but the rest of the text black:

<?php
function getDescription() 
{
    
$buf "<font color='#ff0000'>";
    
$buf .= $this->year "</font>";
    
$buf .= "<font color='#000000'>";
    
$buf .= $this->make " " $this "</font>";
    return 
$buf;
}
?>
The Base Data Access Object
The base DAO provides methods to the VehicleDAO class (and any other classes that extend it) that call database specific PHP methods to access the database. The base DAO could be used on it's own to provide data access on a PHP page, but in keeping with the MVC concepts, we use it as an abstract class whose methods are implemented by objects that know about specific entities in the database.
The Extended Data Access Object
The VehicleDAO class implements the functionality of the base DAO and transfers data between VehicleVO's and the database. The constructor calls the base DAO's constructor which establishes the connection to the database.

<?php
function VehicleDAO($dbserver=""$dbname=""$dbuser=""$dbpass=""
{
    
parent::BaseDAO($dbserver$dbname$dbuser$dbpass);
}
?>
The parent constructor knows the default database connection parameters, but you could specify the values in the constructor of the extended class too if you were working with multiple databases.
The VehicleDAO object keeps it's own simple SQL queries. This makes the data access methods easier to implement. The SELECT queries are made to allow you to append your own WHERE clause parameters, ORDER BY clauses and LIMIT clauses (for mySQL). The data manipulation SQL statements are formatted so that it takes little extra code inside the data access methods to set the values to be inserted or updated.

<?php
var $SQL_SELECT "SELECT * FROM `vehicles` ";
var 
$SQL_COUNT "SELECT count(*) AS cnt FROM `vehicles` ";
// insert with no value for an auto_increment field.
var $SQL_INSERT "INSERT INTO `vehicles` (year,make,model,color,price) VALUES ('%A','%B','%C','%D',%E)";
var 
$SQL_UPDATE "UPDATE `vehicles` SET ";
var 
$SQL_DELETE "DELETE FROM `vehicles` WHERE vehicleid=%A";
?>
The VehicleDAO object contains a number of data access methods:
function findByPK($vehicleid) -
Returns a single VehicleVO object based on the $vehicle id or null if no record is found.
function findBySQL($sql, $orderby = "") -
Returns an ordered array of VehicleVO's based on a complete SQL query.
For example:

<?php

$dao 
= new VehicleDAO();
$volist $dao->findBySQL("select * from vehicles where make='Ford' ""year, model");
foreach(
$volist as $vo
{
    echo(
"$vo->year $vo->model $vo->color<br />");
}
?>
The above would produce a list of Ford vehicles ordered by year and model.
function findWhere($where = "", $orderby = "") -
Returns an array of VehicleVO's based on WHERE clause fragments.
For example:

<?php

$dao 
= new VehicleDAO();
$volist $dao->findWhere("(year > '1999') AND (make='Ford')");
foreach(
$volist as $vo
{
    echo(
"$vo->year $vo->model $vo->color<br />");
}
?>
The above would produce a list of Ford vehicles made in the year 2000 or later.
You can easily add your own data access custom methods:

<?php
function findByPrice($price$gtlt
{
    
$this->sql $this->SQL_SELECT;
    
$voList = array();
    
$where "WHERE ( price $gtlt $price) ";  
    
$this->sql .= $where;
    
$this->exec($this->sql);
    while(
$row $this->getObject()) 
    {
        
$vo = new VehicleVO(
            
$row->vehicleid
            
$row->year
            
$row->make
            
$row->model
            
$row->color
            
$row->price
            
);
        
array_push($voList$vo);
    }
    return 
$voList;
}
?>
Then to use the custom method:

<?php
$dao 
= new VehicleDAO();
$volist $dao->findByPrice(5000"<");
foreach(
$volist as $vo
{
    echo(
$vo->getDescriptiion() . "<br />");
}
?>
The above would produce a list of Ford vehicles whose price was less than $5000.
The data manipulation methods are simple and use VO objects to add, update or remove data.
function insertVO($vo) -
Inserts the data from a VehicleVO into the database. Returns the number of rows affected.
function updateVO($vo) -
Updates a record based on the value of $vo->vehicleid. Returns the number of rows affected.
function deleteVO($vo) -
Deletes a record based on the value of $vo->vehicleid. Returns the number of rows affected.
Putting It All Together
In applist.php, we create a list of vehicles and display them to the user.

<?php  
$dao 
= new VehicleDAO();
$volist $dao->findWhere("""make, year LIMIT 0, 25");
foreach(
$volist as $vo
{
    echo(
"<tr><td><a href='appedit.php?vid=$vo->vehicleid'>$vo->vehicleid</a></td>");
    echo(
"<td>$vo->make</td><td>$vo->year</td><td>$vo->model</td>
          <td>$vo->color</td></tr>"
);
}
...
?>
Clicking on the vehicle id link takes the user to the vehicle_edit.php page.
In appedit.php we display a form loaded with the vehicle data. The form POSTs to the appupdate.php script.

<?php
$id 
$_GET["id"];
$dao = new VehicleDAO();
$vo $dao->findByPK($id);
...
    <
input type="text" name="year" value="<?=$vo->year?>" />
    <
input type="text" name="model" value="<?=$vo->model?>" />
    <
input type="text" name="color" value="<?=$vo->color?>" />
...
?>
In appupdate.php, we create an empty VehicleVO object and read in the data from the form. The VO is then passed to the update() method of a VehicleDAO object.

<?php

$vo 
= new VehicleVO();
$dao = new VehicleDAO();
$vo->readForm();
$records_updated $dao->update($vo);
...
?>
Summary
Now this is NOT the pure Model/View/Controller design that J2EE developers are all drooling over these days, but it's close enough for horseshoes. I am sure that with PHP 5 deeper object oriented capabilities, a better MVC design will arise. But I believe this is a pretty good object oriented design for now.
Also, I've included a PHP page that builds the VO and DAO classes for you. How much easier can you get it?
Geno Timlin