14 Nov
PHP Patterns, Part II

PHP Patterns, Part II

In this installment, we will be looking at two patterns that have been 'borrrowed' from Java. If you've had any development experience with J2EE, you are probably well aware of how handy Data Access Objects and Value Objects can be. If you haven't, don't fret! This article was written especially for you!

If you've never heard these terms before, you may be wondering why I have chosen to group them together within one article. The simple explanation is ... well you'll see. For now just accept that they go hand-in-hand, much like salt and pepper or peanut butter and jelly or .

Excited? Let's dig deeper...



I. Data Access Objects (DAOs) and Value Objects (VOs), an Introduction
These two classes comprise what is often called the "model" layer of the MVC (Model View Control) model. If you aren't familiar with this concept do yourself a huge favor and read up on it - it will save you and your peers quite a bit of time further down the road on any project.

The DAO is basically the object you use to query your data source within your application. Rather than sprinkling SQL queries throughout your code, it allows you to encapsulate everything related to accessing your database within a single class. That way, if you ever change databases or need to tweak a query, you can do it in one place that's easy to find. Think of it like a filing cabinet that keeps all your papers from floating all over your desk.

The VO does what you might expect a "value object" to do - it holds values. It provides you with a nifty object to pass around your application and typically contains a single row of information from your database. Again the goal here is to simplify the process of handling data retrieved from the database.

II. The VO, A Closer Look
Creating a VO is probably one of the simplest classes you will ever create. It basically mirrors a table in your database, with setter and getter methods for each field in that table. First, let's pretend you have a simple database table called "users" that has the following columns:

  • id
  • user
  • pass

Using this information, we can create our very first Value Object:


class UserVO {
    protected 
$id;
    protected 
$username;
    protected 
$password;
    
    public function 
setId($id) {
        
$this->id $id;
    }
    
    public function 
getId() {
        return 
$this->id;
    }
    
    public function 
setUsername($username) {
        
$this->username $username;
    }
    
    public function 
getUsername() {
        return 
$this->username;
    }
    
    public function 
setPassword($password) {
        
$this->password $password;
    }
    
    public function 
getPassword() {
        return 
$this->password;
    }
}

Pretty simple and to the point, huh? You may be wondering why I have gone through and written explicit setters and getters for each value, rather than just making two generic functions (or simply making the class variables public). The primary reason for this has more to do with coding standards than anything else. It is usually good practice to prevent class variables from being accessed directly. This way you don't have to anticipate what might happen ouside the class. By declaring functions for each specific value, you have more control over the use of each value as well. For instance, where appropriate, you could use the addslashes() and stripslashes()  functions to automatically massage data before it is inserted into the database.

II. The DAO, A Closer Look
Now that we have created our first VO, let's take a look at its partner in crime, the DAO. Let's take a look at some code before we start picking it apart:

class UserDAO {
    protected var 
$connect;
    protected var 
$db;
    
    
// Attempts to initialize the database connection using the supplied info.
    
public function UserDAO($host$username$password$database) {
        
$this->connect mysql_connect($host$username$password);
        
$this->db mysql_select_db($database);
    }
    
    
// Executes the specified query and returns an associative array of reseults.
    
protected function execute($sql) {
        
$res mysql_query($sql$this->connect) or die(mysql_error());
        
        if(
mysql_num_rows($res) > 0) {
            for(
$i 0$i mysql_num_rows($res); $i++) {
                
$row mysql_fetch_assoc($res);
                
$userVO[$i] = new UserVO();
                
                
$userVO[$i]->setId($row[id]);
                
$userVO[$i]->setUsername($row[username]);
                
$userVO[$i]->setPassword($row[password]);
            }
        }
        return 
$userVO;
    }
    
    
// Retrieves the corresponding row for the specified user ID.
    
public function getByUserId($userId) {
        
$sql "SELECT * FROM users WHERE id=".$userId;
        return 
$this->execute($sql);
    }
    
    
// Retrieves all users currently in the database.
    
public function getUsers() {
        
$sql "SELECT * FROM users";
        return 
$this->execute($sql);
    }
    
    
//Saves the supplied user to the database.
    
public function save($userVO) {
        
$affectedRows 0;
        
        if(
$userVO->getId() != "") {
            
$currUserVO $this->getByUserId($userVO->getId());
        }
        
        
// If the query returned a row then update,
        // otherwise insert a new user.
        
if(sizeof($currUserVO) > 0) {
            
$sql = "UPDATE users SET ".
               
"username='".$userVO->getUsername()."', ".
               
"password='".$userVO->getPassword()."' ".
               
"WHERE id=".$userVO->getId();
            
            
mysql_query($sql$this->connect) or die(mysql_error());
            
$affectedRows mysql_affected_rows();
        }
        else {
            
$sql "INSERT INTO users (username, password) VALUES('".
                
$userVO->getUsername()."', ".
                
$userVO->getPassword()."')".
            
            
mysql_query($sql$this->connect) or die(mysql_error());
            
$affectedRows mysql_affected_rows();
        }
        
        return 
$affectedRows;
    }
    
    
// Deletes the supplied user from the database.
    
public function delete($userVO) {
        
$affectedRows 0;
        
        
// Check for a user ID.
        
if($userVO->getId() != "") {
            
$currUserVO $this->getByUserId($userVO->getId());
        }
        
        
// Otherwise delete a user.
        
if(sizeof($currUserVO) > 0) {
            
$sql "DELETE FROM users WHERE id=".$userVO->getId();
            
            
mysql_query($sql$this->connect) or die(mysql_error());
            
$affectedRows mysql_affected_rows();
        
        return 
$affectedRows;
    }
}

The Data Access Object consists a handful of basic methods. First is the constructor, which basically initiates the connection to the database. You pass it the database connection info and credentials, and it takes care of the rest.

Next comes the getter functions, which basically represent a collection of the queries used to access the database. For example, you might have a getByUserId() method that returns the UserVO we created above filled with the corresponding data for a particular user ID.

Following the getters is the save() function, which essentially does what you would imagine it does. You pass a VO to it and it will either update an existing record if it finds one that matches the VO or will insert a new row. I'll bet you're thinking what I thought the first time I learned of this function: "holy cow! that just reduced 8+ lines of code down to one!"

Finally we have the big scary delete() function that, you guessed it, deletes a record from the database. To make it slightly less scary, it requires that you pass it a VO that matches a database row exactly. This will force you to have first retrieved the row you want to delete before calling this method. This helps prevent the accidental deletion of a record.

And there you have it! Together these two classes can save you a tremendous amount of effort and time, makes it much easier to maintain say 6-12 months from now when you have completely forgotten how you wrote that file manager script.

A side note: If you live in fear of having to write these classes for databases with 100 tables that have 200 columns each, don't despair! With the help of Google you can find countless PHP DAO and VO generators that will build the code for you (take a look here for example). Some will do it based on existing database tables, and others will do it based on what you tell them. They might still require some tweaking afterwards, but at least that way the hard part would be done and over with.

Coding Techniques, From the Workbench, MySQL, PHP, Software Engineering
by Nick Williams | 11/14/2007 3:48pm

Good
I made it for my big project, but when you have for example group of user, and many group in a structure, when you want update all the user of the structure, I have a lot of query...
You're method is good for some data but not for many...
So you have to modify some pattern to save many mysql ressource... Could you talk about that ?
For example, i had :
Object::get_by_id($tab_id){} ->return a tab of object
Object::get_one_by_id($tab_id){} -> return always one object
And after all my function work always with a tab of object...
So when you want update many object in the same time, a get all my object in a tab, and I test each one, I store the object to update in another tab and finally a make a big UPDATE query (perhaps many query of 50 updates for example...)
I think it's save many call the mysql...

I it's a bad idea, tell me... I'm not sure I ve made the good choice...

Left by arthemisia | Nov 15, 2007

It seems my post made it to the front page of DZone.com:

http://www.dzone.com/links/php_patterns_part_ii_the_dao_and_vo_patterns.html

Left by Nick Williams | Nov 15, 2007

Could you provide some examples of how this would be used? For example, you'd need to create a userDAO and use that to retrieve the user's info and then assign those values to the userVO? Sorry if I'm being shortsighted, I'm just not seeing how this would be applied.

Left by | Nov 15, 2007

The usefulness comes when you start expanding upon the VO. Yes, you can have something resembling the DAO that simply returns an associative array, that will serve the same purpose in reducing code time, without any need at all for the VO. The fact is, however, that rarely is an object just a value object. If you look at most objects in the Javadocs, the get and set methods are only the tip of the iceberg. This model becomes useful as more than just a struct or array when you start building in data manipulation.

First off, I tend to combine the two. Rather than a function that can be passed a User object to save it, will often build the save, load and delete functions straight into the VO. Thus, instead of instantiating both objects and then calling $dao->save($uo), the call becomes simply $uo->save()

Further functionality is also possible. A very basic example can be applied to Nick's UserVO. The next logical step is to build a login function. You've got the object containing access information, so then you add something like this (keeping in mind that with a composite object the VO would already be handling the database connection):


function login() {
$sql = "SELECT username FROM User
WHERE username='$this->user'
AND password=PASSWORD('$this->pass')"
$res = mysql_query($sql);
if(mysql_num_rows($res) > 0) {
$_SESSION['logged_in'] = true;
return true;
} else {
return false;
}
}

So then when you need to log a user in via a form, you can use:

$user = new User();
$user->setUsername($_POST['un']);
$user->setPassword($_POST['pw']);

if($user->login()) {
// forward to account landing page
} else {
// reload form with an error message
}


So basically, what Nick has show us is not an end itself, but a starting point that leads to more useful applications.

Left by Joe Izenman | Dec 6, 2007

I found this a very useful tutorial. However there are MANY errors in this code. From function names not matching like "setUser" in the VO and setUsername in the DAO, same with password...

The update query has an extra comma "password='".$userVO->getPassword()."', ".

BUT I did get it working and was finding it very difficult to understand until I found this article. So Thank you!

Left by Justin | Nov 12, 2008

Thanks for catching those Justin, I went ahead and corrected the post. In an effort to simplify the original code into something a bit more blog-appropriate, it looks like I missed a few things.

I'm glad you found it helpful though! If you found this example helpful, you may also want to look into some of the features introduced in PHP 5 that further streamline implementing this pattern. The magic methods __get() and __set() are particularly handy, as well as the ArrayAccess and Iterator interfaces.

Link: http://us2.php.net/manual/en/language.oop5.overloading.php
Link: http://www.phpro.org/tutorials/Introduction-to-SPL-ArrayAccess.html
Link: http://www.devarticles.com/c/a/PHP/Building-an-Iterator-with-PHP/

Left by Nick Williams | Nov 13, 2008

we have the big scary delete() function that, you guessed it, deletes a record from the database. To make it slightly less scary, it requires that you pass it a VO that matches a database row exactly. This will force you to have first retrieved the row you want to delete before calling this method. This helps prevent the accidental deletion of a record.

Left by Tungsten Rings with Inlay | Jun 1, 2009

In case anyone else finds this oldish post, if you do this, beware:

$user = new User();
$user->setUsername($_POST['un']);
[...]

$sql = "SELECT username FROM User
WHERE username='$this->user'
AND password=PASSWORD('$this->pass')"
$res = mysql_query($sql);

Hello SQL injection! Make sure you treat http parameters as untrusted and validate them before applying them to your data objects.

Left by Simon | Mar 12, 2010

Hello Simon,

That should be the task of the businessobject Or the object which is responsible for retrieving $_POST variables. So for instance if you have a controller object which reacts on $_POST or $_GET alsways before you construct a VO validate the content you want to pass into it by either is_empty or is_numeric or whatever you need.

Left by jjtb somhorst | Jun 18, 2010

I want mvc with dao and vo

Left by Mohan | Mar 17, 2011

Could someone also extend the above examples and add a Business-Object (BO) to it?
That would really complete the article

Left by Hannes Werner | Jan 26, 2012

Fantastic article! I have been using MVC for some time but bumped into some trouble when my classes did not make sense to have methods such as getAll().

Using a VO separates the model from the functionality of retrieving/setting data.

Amarjit
http://www.bharath.co.uk

Left by Amarjit | Feb 7, 2012

This is because YAML labiry is not installed or not in path.Add library to your code before that ensure it exists.require

Left by Joaquin | Aug 26, 2012

Leave a Comment




* required    Comment Guidelines