Monday, May 14, 2007

Polymorphism PHP Mysql

Polymorphism

The database wrappers developed in this chapter are pretty generic. In fact, if you look at the other database extensions built in to PHP, you see the same basic functionality over and over againconnecting to a database, preparing queries, executing queries, and fetching back the results. If you wanted to, you could write a similar DB_Pgsql or DB_Oracle class that wraps the PostgreSQL or Oracle libraries, and you would have basically the same methods in it.

In fact, although having basically the same methods does not buy you anything, having identically named methods to perform the same sorts of tasks is important. It allows for polymorphism, which is the ability to transparently replace one object with another if their access APIs are the same.

In practical terms, polymorphism means that you can write functions like this:

function show_entry($entry_id, $dbh)
{
$query = "SELECT * FROM Entries WHERE entry_id = :1";
$stmt = $dbh->prepare($query)->execute($entry_id);
$entry = $stmt->fetch_row();
// display entry
}

This function not only works if $dbh is a DB_Mysql object, but it works fine as long as $dbh implements a prepare() method and that method returns an object that implements the execute() and fetch_assoc() methods.

To avoid passing a database object into every function called, you can use the concept of delegation. Delegation is an OO pattern whereby an object has as an attribute another object that it uses to perform certain tasks.

The database wrapper libraries are a perfect example of a class that is often delegated to. In a common application, many classes need to perform database operations. The classes have two options:

  • You can implement all their database calls natively. This is silly. It makes all the work you've done in putting together a database wrapper pointless.

  • You can use the database wrapper API but instantiate objects on-the-fly. Here is an example that uses this option:

    class Weblog {
    public function show_entry($entry_id)
    {
    $query = "SELECT * FROM Entries WHERE entry_id = :1";
    $dbh = new Mysql_Weblog();
    $stmt = $dbh->prepare($query)->execute($entry_id);
    $entry = $stmt->fetch_row();
    // display entry
    }
    }

    On the surface, instantiating database connection objects on-the-fly seems like a fine idea; you are using the wrapper library, so all is good. The problem is that if you need to switch the database this class uses, you need to go through and change every function in which a connection is made.

  • You implement delegation by having Weblog contain a database wrapper object as an attribute of the class. When an instance of the class is instantiated, it creates a database wrapper object that it will use for all input/output (I/O). Here is a re-implementation of Weblog that uses this technique:

    class Weblog {
    protected $dbh;
    public function setDB($dbh)
    {
    $this->dbh = $dbh;
    }
    public function show_entry($entry_id)
    {
    $query = "SELECT * FROM Entries WHERE entry_id = :1";
    $stmt = $this->dbh->prepare($query)->execute($entry_id);
    $entry = $stmt->fetch_row();
    // display entry
    }
    }

Now you can set the database for your object, as follows:

$blog = new Weblog;
$dbh = new Mysql_Weblog;
$blog->setDB($dbh);

Of course, you can also opt to use a Template pattern instead to set your database delegate:

class Weblog_Std extends Weblog {
protected $dbh;
public function _ _construct()
{
$this->dbh = new Mysql_Weblog;
}
}
$blog = new Weblog_Std;

Delegation is useful any time you need to perform a complex service or a service that is likely to vary inside a class. Another place that delegation is commonly used is in classes that need to generate output. If the output might be rendered in a number of possible ways (for example, HTML, RSS [which stands for Rich Site Summary or Really Simple Syndication, depending on who you ask], or plain text), it might make sense to register a delegate capable of generating the output that you want.

No comments: