[介绍]PHP设计模式:DAO(数据访问对象模式)

系统 2302 0

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

      
        
          <?php
        
      
      
        
          /** * A simple class for querying MySQL */
        
      
      
        
          class
        
      
       DataAccess 
      
        {
      
      
        
          /** * Private * $db stores a database resource */
        
      
      
        
          var
        
      
      
        $db
      
      ;

    
      
        
          //! A constructor.
        
      
      
        
          /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
        
      
      
        
          function
        
      
       DataAccess 
      
        (
      
      
        $host
      
      ,
      
        $user
      
      ,
      
        $pass
      
      ,
      
        $db
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        db
      
      =
      
        
          mysql_pconnect
        
      
      
        (
      
      
        $host
      
      ,
      
        $user
      
      ,
      
        $pass
      
      
        )
      
      ;
        
      
        
          mysql_select_db
        
      
      
        (
      
      
        $db
      
      ,
      
        $this
      
      ->
      
        db
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
        
      
      
        
          function
        
      
       & fetch
      
        (
      
      
        $sql
      
      
        )
      
      
        {
      
      
        return
      
      
        
          new
        
      
       DataAccessResult
      
        (
      
      
        $this
      
      ,
      
        
          mysql_query
        
      
      
        (
      
      
        $sql
      
      ,
      
        $this
      
      ->
      
        db
      
      
        )
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns any MySQL errors * @return string a MySQL error */
        
      
      
        
          function
        
      
       isError 
      
        (
      
      
        )
      
      
        {
      
      
        return
      
      
        
          mysql_error
        
      
      
        (
      
      
        $this
      
      ->
      
        db
      
      
        )
      
      ;
    
      
        }
      
      
        }
      
      
        
          /** * Fetches MySQL database rows as objects */
        
      
      
        
          class
        
      
       DataAccessResult 
      
        {
      
      
        
          /** * Private * $da stores data access object */
        
      
      
        
          var
        
      
      
        $da
      
      ;
    
      
        
          /** * Private * $query stores a query resource */
        
      
      
        
          var
        
      
      
        $query
      
      ;

    
      
        
          function
        
      
       DataAccessResult
      
        (
      
      & 
      
        $da
      
      ,
      
        $query
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        da
      
      =& 
      
        $da
      
      ;
        
      
        $this
      
      ->
      
        query
      
      =
      
        $query
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns an array from query row or false if no more rows * @return mixed */
        
      
      
        
          function
        
      
       getRow 
      
        (
      
      
        )
      
      
        {
      
      
        if
      
      
        (
      
      
        $row
      
      =
      
        
          mysql_fetch_array
        
      
      
        (
      
      
        $this
      
      ->
      
        query
      
      ,MYSQL_ASSOC
      
        )
      
      
        )
      
      
        return
      
      
        $row
      
      ;
        
      
        else
      
      
        return
      
      
        
          false
        
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns the number of rows affected * @return int */
        
      
      
        
          function
        
      
       rowCount 
      
        (
      
      
        )
      
      
        {
      
      
        return
      
      
        
          mysql_num_rows
        
      
      
        (
      
      
        $this
      
      ->
      
        query
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns false if no errors or returns a MySQL error message * @return mixed */
        
      
      
        
          function
        
      
       isError 
      
        (
      
      
        )
      
      
        {
      
      
        $error
      
      =
      
        $this
      
      ->
      
        da
      
      ->
      
        isError
      
      
        (
      
      
        )
      
      ;
        
      
        if
      
      
        (
      
      !
      
        
          empty
        
      
      
        (
      
      
        $error
      
      
        )
      
      
        )
      
      
        return
      
      
        $error
      
      ;
        
      
        else
      
      
        return
      
      
        
          false
        
      
      ;
    
      
        }
      
      
        }
      
      
        
          ?>
        
      
    

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

      
        
          <?php
        
      
      
        
          /** * Base class for data access objects */
        
      
      
        
          class
        
      
       Dao 
      
        {
      
      
        
          /** * Private * $da stores data access object */
        
      
      
        
          var
        
      
      
        $da
      
      ;

    
      
        
          //! A constructor
        
      
      
        
          /** * Constructs the Dao * @param $da instance of the DataAccess class */
        
      
      
        
          function
        
      
       Dao 
      
        (
      
       & 
      
        $da
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        da
      
      =
      
        $da
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
        
      
      
        
          function
        
      
       & retrieve 
      
        (
      
      
        $sql
      
      
        )
      
      
        {
      
      
        $result
      
      =& 
      
        $this
      
      ->
      
        da
      
      ->
      
        fetch
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
        
      
        if
      
      
        (
      
      
        $error
      
      =
      
        $result
      
      ->
      
        isError
      
      
        (
      
      
        )
      
      
        )
      
      
        {
      
      
        
          trigger_error
        
      
      
        (
      
      
        $error
      
      
        )
      
      ;
            
      
        return
      
      
        
          false
        
      
      ;
        
      
        }
      
      
        else
      
      
        {
      
      
        return
      
      
        $result
      
      ;
        
      
        }
      
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
        
      
      
        
          function
        
      
       update 
      
        (
      
      
        $sql
      
      
        )
      
      
        {
      
      
        $result
      
      =
      
        $this
      
      ->
      
        da
      
      ->
      
        fetch
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
        
      
        if
      
      
        (
      
      
        $error
      
      =
      
        $result
      
      ->
      
        isError
      
      
        (
      
      
        )
      
      
        )
      
      
        {
      
      
        
          trigger_error
        
      
      
        (
      
      
        $error
      
      
        )
      
      ;
            
      
        return
      
      
        
          false
        
      
      ;
        
      
        }
      
      
        else
      
      
        {
      
      
        return
      
      
        
          true
        
      
      ;
        
      
        }
      
      
        }
      
      
        }
      
      
        
          ?>
        
      
    

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

      CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

    

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

      
        
          <?php
        
      
      
        
          /** * Data Access Object for Log Table */
        
      
      
        
          class
        
      
       LogDao extends Dao 
      
        {
      
      
        
          //! A constructor
        
      
      
        
          /** * Constructs the LogDao * @param $da instance of the DataAccess class */
        
      
      
        
          function
        
      
       LogDao 
      
        (
      
       & 
      
        $da
      
      
        )
      
      
        {
      
      
        Dao::
      
        Dao
      
      
        (
      
      
        $da
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Gets a log files * @return object a result object */
        
      
      
        
          function
        
      
       & searchAll 
      
        (
      
      
        $start
      
      =
      
        
          false
        
      
      ,
      
        $rows
      
      =
      
        
          false
        
      
      
        )
      
      
        {
      
      
        $sql
      
      =
      
        "SELECT * FROM log ORDER BY date DESC"
      
      ;
        
      
        if
      
      
        (
      
      
        $start
      
      
        )
      
      
        {
      
      
        $sql
      
      .=
      
        " LIMIT "
      
      .
      
        $start
      
      ;
            
      
        if
      
      
        (
      
      
        $rows
      
      
        )
      
      
        $sql
      
      .=
      
        ", "
      
      .
      
        $rows
      
      ;
        
      
        }
      
      
        return
      
      
        $this
      
      ->
      
        retrieve
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Searches logs by IP address * @return object a result object */
        
      
      
        
          function
        
      
       & searchByAddress 
      
        (
      
      
        $address
      
      
        )
      
      
        {
      
      
        $sql
      
      =
      
        "SELECT * FROM log WHERE address='"
      
      .
      
        $address
      
      .
      
        "'"
      
      .
            
      
        " ORDER BY date DESC"
      
      ;
        
      
        return
      
      
        $this
      
      ->
      
        retrieve
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Searches logs by country * @return object a result object */
        
      
      
        
          function
        
      
       & searchByCountry 
      
        (
      
      
        $country
      
      
        )
      
      
        {
      
      
        $sql
      
      =
      
        "SELECT * FROM log WHERE country='"
      
      .
      
        $country
      
      .
      
        "'"
      
      .
            
      
        " ORDER BY date DESC"
      
      ;
        
      
        return
      
      
        $this
      
      ->
      
        retrieve
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Searches logs by id * @return object a result object */
        
      
      
        
          function
        
      
       & searchByID 
      
        (
      
      
        $id
      
      
        )
      
      
        {
      
      
        $sql
      
      =
      
        "SELECT * FROM log WHERE id='"
      
      .
      
        $id
      
      .
      
        "'"
      
      ;
        
      
        return
      
      
        $this
      
      ->
      
        retrieve
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
    
      
        }
      
      
        
          function
        
      
       & totalRows 
      
        (
      
      
        )
      
      
        {
      
      
        $sql
      
      =
      
        "SELECT count(*) as count FROM log"
      
      ;
        
      
        return
      
      
        $this
      
      ->
      
        retrieve
      
      
        (
      
      
        $sql
      
      
        )
      
      ;
    
      
        }
      
      
        }
      
      
        
          ?>
        
      
    

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

      
        
          <?php
        
      
      
        
          /** * Modelling log data */
        
      
      
        
          class
        
      
       LogModel 
      
        {
      
      
        
          /** * Private * $dao stores data access object */
        
      
      
        
          var
        
      
      
        $dao
      
      ; 

    
      
        
          /** * Private * $result stores result object */
        
      
      
        
          var
        
      
      
        $result
      
      ;

    
      
        
          /** * Private * $rowCount stores number of rows returned */
        
      
      
        
          var
        
      
      
        $numPages
      
      ;

    
      
        
          //! A constructor
        
      
      
        
          /** * Constructs the LogModel * @param $da instance of the DataAccess class */
        
      
      
        
          function
        
      
       LogModel 
      
        (
      
       & 
      
        $dao
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        dao
      
      =& 
      
        $dao
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Gets a paged result set * @param $page the page to view from result set * @return void */
        
      
      
        
          function
        
      
       listLogs 
      
        (
      
      
        $page
      
      =
      
        1
      
      
        )
      
      
        {
      
      
        $rows
      
      =
      
        20
      
      ;       
        
      
        $start
      
      =
      
        $rows
      
      *
      
        $page
      
      ;
        
      
        $this
      
      ->
      
        result
      
      =& 
      
        $this
      
      ->
      
        dao
      
      ->
      
        searchAll
      
      
        (
      
      
        $start
      
      ,
      
        $rows
      
      
        )
      
      ;
        
      
        $numRowsRes
      
      =
      
        $this
      
      ->
      
        dao
      
      ->
      
        totalRows
      
      
        (
      
      
        )
      
      ;
        
      
        $numRow
      
      =
      
        $numRowsRes
      
      ->
      
        getRow
      
      
        (
      
      
        )
      
      ;
        
      
        $numRows
      
      =
      
        $numRow
      
      
        [
      
      
        'count'
      
      
        ]
      
      ;
        
      
        $this
      
      ->
      
        numPages
      
      =
      
        
          floor
        
      
      
        (
      
      
        $numRows
      
      /
      
        $rows
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns the number of pages in result set * @return int */
        
      
      
        
          function
        
      
       getNumPages 
      
        (
      
      
        )
      
      
        {
      
      
        return
      
      
        $this
      
      ->
      
        numPages
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
        
      
      
        
          function
        
      
       searchLogs
      
        (
      
      
        $searchBy
      
      =
      
        'address'
      
      ,
      
        $searchString
      
      
        )
      
      
        {
      
      
        switch
      
      
        (
      
      
        $searchBy
      
      
        )
      
      
        {
      
      
        case
      
      
        "country"
      
      :
                
      
        $this
      
      ->
      
        result
      
      =& 
      
        $this
      
      ->
      
        dao
      
      ->
      
        searchByCountry
      
      
        (
      
      
        $searchString
      
      
        )
      
      ;
                
      
        break
      
      ;
            
      
        
          default
        
      
      :
                
      
        $this
      
      ->
      
        result
      
      =& 
      
        $this
      
      ->
      
        dao
      
      ->
      
        searchByAddress
      
      
        (
      
      
        $searchString
      
      
        )
      
      ;
                
      
        break
      
      ;
        
      
        }
      
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Gets a single log row by it's id * @param $id of the log row * @return void */
        
      
      
        
          function
        
      
       listLog 
      
        (
      
      
        $id
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        result
      
      =& 
      
        $this
      
      ->
      
        dao
      
      ->
      
        searchByID
      
      
        (
      
      
        $id
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Gets the data from a single row * @return array a single log row */
        
      
      
        
          function
        
      
       getLog
      
        (
      
      
        )
      
      
        {
      
      
        return
      
      
        $this
      
      ->
      
        result
      
      ->
      
        getRow
      
      
        (
      
      
        )
      
      ;
    
      
        }
      
      
        }
      
      
        
          ?>
        
      
    

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

      
        
          <?php
        
      
      
        
          /** * Controls the application */
        
      
      
        
          class
        
      
       LogController 
      
        {
      
      
        
          /** * Private * $model an instance of LogModel */
        
      
      
        
          var
        
      
      
        $model
      
      ;

    
      
        
          /** * Private * $view an instance of LogView */
        
      
      
        
          var
        
      
      
        $view
      
      ;

    
      
        
          //! A constructor.
        
      
      
        
          /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
        
      
      
        
          function
        
      
       LogController 
      
        (
      
      & 
      
        $da
      
      
        )
      
      
        {
      
      
        $logDao
      
      =& 
      
        
          new
        
      
       LogDao
      
        (
      
      
        $da
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        model
      
      =& 
      
        
          new
        
      
       LogModel
      
        (
      
      
        $logDao
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns the view bound with data * @return string */
        
      
      
        
          function
        
      
       & getView 
      
        (
      
      
        )
      
      
        {
      
      
        return
      
      
        $this
      
      ->
      
        view
      
      ;
    
      
        }
      
      
        }
      
      
        
          // ...
        
      
      
        
          class
        
      
       LogTableController extends LogController 
      
        {
      
      
        
          //! A constructor.
        
      
      
        
          /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
        
      
      
        
          function
        
      
       LogTableController 
      
        (
      
      & 
      
        $da
      
      ,
      
        $getvars
      
      =
      
        
          null
        
      
      
        )
      
      
        {
      
      
        LogController::
      
        LogController
      
      
        (
      
      
        $da
      
      
        )
      
      ;
        
      
        if
      
      
        (
      
       !
      
        
          isset
        
      
      
        (
      
      
        $getvars
      
      
        [
      
      
        'page'
      
      
        ]
      
      
        )
      
      
        )
      
      
        $getvars
      
      
        [
      
      
        'page'
      
      
        ]
      
      =
      
        1
      
      ;
        
      
        $this
      
      ->
      
        view
      
      =& 
      
        
          new
        
      
       LogTableView
      
        (
      
      
        $this
      
      ->
      
        model
      
      ,
      
        $getvars
      
      
        [
      
      
        'page'
      
      
        ]
      
      
        )
      
      ;
    
      
        }
      
      
        }
      
      
        
          // ...
        
      
      
        
          ?>
        
      
    

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

      
        
          <?php
        
      
      
        
          /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
        
      
      
        
          // A function to create static instances of the DOM API
        
      
      
        
          function
        
      
       staticDom
      
        (
      
      
        $type
      
      =
      
        
          null
        
      
      ,
      
        $source
      
      =
      
        
          null
        
      
      
        )
      
      
        {
      
      
        
          // Declare a static variable to hold the dom object
        
      
      
        
          static
        
      
      
        $dom
      
      ;

    
      
        
          // If the instance is not there, create one
        
      
      
        if
      
      
        (
      
      !
      
        
          isset
        
      
      
        (
      
      
        $dom
      
      
        )
      
      
        )
      
      
        {
      
      
        
          // Deal with the possible ways DOM can be constructed
        
      
      
        switch
      
      
        (
      
      
        $type
      
      
        )
      
      
        {
      
      
        case
      
      
        "file"
      
      :
                
      
        $dom
      
      =domxml_open_file
      
        (
      
      
        $source
      
      
        )
      
      ; 
      
        
          // $source: path to file
        
      
      
        break
      
      ;
            
      
        case
      
      
        "mem"
      
      :
                
      
        $dom
      
      =domxml_open_mem
      
        (
      
      
        $source
      
      
        )
      
      ; 
      
        
          // $sounce: XML as string
        
      
      
        break
      
      ;
            
      
        
          default
        
      
      :
                
      
        $dom
      
      =domxml_new_doc
      
        (
      
      
        '1.0'
      
      
        )
      
      ; 
      
        
          // create a new one
        
      
      
        break
      
      ;
        
      
        }
      
      
        }
      
      
        return
      
      
        (
      
      
        $dom
      
      
        )
      
      ;

      
        }
      
      
        
          /** * Base Widget class */
        
      
      
        
          class
        
      
       Widget 
      
        {
      
      
        
          /** * Private * $dom an instance of the DOM API */
        
      
      
        
          var
        
      
      
        $dom
      
      ;

    
      
        
          /** * Private * $out whether to return a DOM object or an XML string */
        
      
      
        
          var
        
      
      
        $out
      
      ;

    
      
        
          /** * Private * $widget stores a widget object */
        
      
      
        
          var
        
      
      
        $widget
      
      ;

    
      
        
          //! A Constructor
        
      
      
        
          /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
        
      
      
        
          function
        
      
       Widget 
      
        (
      
      
        $out
      
      =
      
        'string'
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        dom
      
      =& staticDom
      
        (
      
      
        )
      
      ; 
      
        
          // Construct DOM from static instance
        
      
      
        $this
      
      ->
      
        out
      
      =
      
        $out
      
      ;
    
      
        }
      
      
        
          //! A manipulator
        
      
      
        
          /** * Abstract widget creation method * @return void */
        
      
      
        
          function
        
      
       createWidget 
      
        (
      
      
        )
      
      
        {
      
      
        
          // Empty
        
      
      
        }
      
      
        
          //! A manipulator
        
      
      
        
          /** * Abstract widget finalization method * @return void */
        
      
      
        
          function
        
      
       finalizeWidget 
      
        (
      
      
        )
      
      
        {
      
      
        
          // Empty
        
      
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Change the current value of $this->out * @return void */
        
      
      
        
          function
        
      
       setOut 
      
        (
      
      
        $out
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        out
      
      =
      
        $out
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a generic widget to the current widget * @return void */
        
      
      
        
          function
        
      
       addWidget
      
        (
      
      
        $newWidget
      
      
        )
      
      
        {
      
      
        $newWidget
      
      ->
      
        setOut
      
      
        (
      
      
        'object'
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        widget
      
      ->
      
        append_child
      
      
        (
      
      
        $newWidget
      
      ->
      
        fetch
      
      
        (
      
      
        )
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a generic attibute to the current widget * @return void */
        
      
      
        
          function
        
      
       addAttribute
      
        (
      
      
        $name
      
      ,
      
        $value
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        widget
      
      ->
      
        set_attribute
      
      
        (
      
      
        $name
      
      ,
      
        $value
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Places text in the widget * @return void */
        
      
      
        
          function
        
      
       addText
      
        (
      
      
        $text
      
      
        )
      
      
        {
      
      
        $text
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_text_node
      
      
        (
      
      
        $text
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        widget
      
      ->
      
        append_child
      
      
        (
      
      
        $text
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a class="" attribute to the current widget * @return void */
        
      
      
        
          function
        
      
       addClass
      
        (
      
      
        $class
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        widget
      
      ->
      
        set_attribute
      
      
        (
      
      
        'class'
      
      ,
      
        $class
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a style="" attribute to the current widget * @return void */
        
      
      
        
          function
        
      
       addStyle
      
        (
      
      
        $style
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        widget
      
      ->
      
        set_attribute
      
      
        (
      
      
        'style'
      
      ,
      
        $style
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Returns either XML as a string or a DOM object * @return mixed */
        
      
      
        
          function
        
      
       &fetch 
      
        (
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        finalizeWidget
      
      
        (
      
      
        )
      
      ;
        
      
        if
      
      
        (
      
      
        $this
      
      ->
      
        out
      
      ==
      
        'string'
      
      
        )
      
      
        {
      
      
        return
      
      
        $this
      
      ->
      
        dom
      
      ->
      
        dump_node
      
      
        (
      
      
        $this
      
      ->
      
        widget
      
      
        )
      
      ;
        
      
        }
      
      
        else
      
      
        {
      
      
        return
      
      
        $this
      
      ->
      
        widget
      
      ;
        
      
        }
      
      
        }
      
      
        }
      
      
        
          class
        
      
       PageWidget extends Widget 
      
        {
      
      
        
          /** * Private * $head XML object for <head /> */
        
      
      
        
          var
        
      
      
        $head
      
      ;

    
      
        
          /** * Private * $body XML object for <body /> */
        
      
      
        
          var
        
      
      
        $body
      
      ;

    
      
        
          //! A constructor
        
      
      
        
          /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
        
      
      
        
          function
        
      
       PageWidget
      
        (
      
      
        $out
      
      =
      
        'string'
      
      
        )
      
      
        {
      
      
        Widget::
      
        Widget
      
      
        (
      
      
        $out
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        createWidget
      
      
        (
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! A manipulator
        
      
      
        
          /** * Page widget creation method * @return void */
        
      
      
        
          function
        
      
       createWidget 
      
        (
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        widget
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_element
      
      
        (
      
      
        'html'
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        head
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_element
      
      
        (
      
      
        'head'
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        body
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_element
      
      
        (
      
      
        'body'
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! A manipulator
        
      
      
        
          /** * Page widget finalization method * @return void */
        
      
      
        
          function
        
      
       finalizeWidget 
      
        (
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        widget
      
      ->
      
        append_child
      
      
        (
      
      
        $this
      
      ->
      
        head
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        widget
      
      ->
      
        append_child
      
      
        (
      
      
        $this
      
      ->
      
        body
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a title element * @return void */
        
      
      
        
          function
        
      
       addTitle 
      
        (
      
      
        $text
      
      
        )
      
      
        {
      
      
        $title
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_element
      
      
        (
      
      
        'title'
      
      
        )
      
      ;
        
      
        $text
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_text_node
      
      
        (
      
      
        $text
      
      
        )
      
      ;
        
      
        $title
      
      ->
      
        append_child
      
      
        (
      
      
        $text
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        head
      
      ->
      
        append_child
      
      
        (
      
      
        $title
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Adds a link tag for CSS files * @return void */
        
      
      
        
          function
        
      
       cssLink 
      
        (
      
      
        $url
      
      
        )
      
      
        {
      
      
        $cssLink
      
      =
      
        $this
      
      ->
      
        dom
      
      ->
      
        create_element
      
      
        (
      
      
        'link'
      
      
        )
      
      ;
        
      
        $cssLink
      
      ->
      
        set_attribute
      
      
        (
      
      
        'href'
      
      ,
      
        $url
      
      
        )
      
      ;
        
      
        $cssLink
      
      ->
      
        set_attribute
      
      
        (
      
      
        'type'
      
      ,
      
        'text/css'
      
      
        )
      
      ;
        
      
        $cssLink
      
      ->
      
        set_attribute
      
      
        (
      
      
        'rel'
      
      ,
      
        'stylesheet'
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        head
      
      ->
      
        append_child
      
      
        (
      
      
        $cssLink
      
      
        )
      
      ;
    
      
        }
      
      
        
          //! An accessor
        
      
      
        
          /** * Appends a widget to $this->body * @return void */
        
      
      
        
          function
        
      
       addBody
      
        (
      
      
        $widget
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        body
      
      ->
      
        append_child
      
      
        (
      
      
        $widget
      
      ->
      
        fetch
      
      
        (
      
      
        )
      
      
        )
      
      ;
    
      
        }
      
      
        }
      
      
        
          // ... etc. etc.
        
      
    

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

      
        
          <?php
        
      
      
        
          /** * Binds log data to HTML content */
        
      
      
        
          class
        
      
       LogView 
      
        {
      
      
        
          /** * Private * $model an instance of the LogModel class */
        
      
      
        
          var
        
      
      
        $model
      
      ;

    
      
        
          /** * Private * $output contains instance of DOM page object */
        
      
      
        
          var
        
      
      
        $output
      
      ;

    
      
        
          //! A constructor.
        
      
      
        
          /** * Constucts a new LogView object * @param $model an instance of the LogModel class */
        
      
      
        
          function
        
      
       LogView 
      
        (
      
      &
      
        $model
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        model
      
      =& 
      
        $model
      
      ;
    
      
        }
      
      
        
          //! A manipulator
        
      
      
        
          /** * Builds the top of an HTML page * @return void */
        
      
      
        
          function
        
      
       create 
      
        (
      
      
        )
      
      
        {
      
      
        $this
      
      ->
      
        output
      
      =
      
        
          new
        
      
       PageWidget
      
        (
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        output
      
      ->
      
        addTitle
      
      
        (
      
      
        'IP Log Files'
      
      
        )
      
      ;
        
      
        $this
      
      ->
      
        output
      
      ->
      
        cssLink
      
      
        (
      
      
        'css/style.css'
      
      
        )
      
      ;
        
      
        $link
      
      =
      
        
          new
        
      
       LinkWidget
      
        (
      
      
        'object'
      
      
        )
      
      ;
        
      
        $link
      
      ->
      
        addLink
      
      
        (
      
      
        

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

            
              
                <?php
              
            
            
              
                /** * A simple class for querying MySQL */
              
            
            
              
                class
              
            
             DataAccess 
            
              {
            
            
              
                /** * Private * $db stores a database resource */
              
            
            
              
                var
              
            
            
              $db
            
            ;

    
            
              
                //! A constructor.
              
            
            
              
                /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
              
            
            
              
                function
              
            
             DataAccess 
            
              (
            
            
              $host
            
            ,
            
              $user
            
            ,
            
              $pass
            
            ,
            
              $db
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              db
            
            =
            
              
                mysql_pconnect
              
            
            
              (
            
            
              $host
            
            ,
            
              $user
            
            ,
            
              $pass
            
            
              )
            
            ;
        
            
              
                mysql_select_db
              
            
            
              (
            
            
              $db
            
            ,
            
              $this
            
            ->
            
              db
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
              
            
            
              
                function
              
            
             & fetch
            
              (
            
            
              $sql
            
            
              )
            
            
              {
            
            
              return
            
            
              
                new
              
            
             DataAccessResult
            
              (
            
            
              $this
            
            ,
            
              
                mysql_query
              
            
            
              (
            
            
              $sql
            
            ,
            
              $this
            
            ->
            
              db
            
            
              )
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns any MySQL errors * @return string a MySQL error */
              
            
            
              
                function
              
            
             isError 
            
              (
            
            
              )
            
            
              {
            
            
              return
            
            
              
                mysql_error
              
            
            
              (
            
            
              $this
            
            ->
            
              db
            
            
              )
            
            ;
    
            
              }
            
            
              }
            
            
              
                /** * Fetches MySQL database rows as objects */
              
            
            
              
                class
              
            
             DataAccessResult 
            
              {
            
            
              
                /** * Private * $da stores data access object */
              
            
            
              
                var
              
            
            
              $da
            
            ;
    
            
              
                /** * Private * $query stores a query resource */
              
            
            
              
                var
              
            
            
              $query
            
            ;

    
            
              
                function
              
            
             DataAccessResult
            
              (
            
            & 
            
              $da
            
            ,
            
              $query
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              da
            
            =& 
            
              $da
            
            ;
        
            
              $this
            
            ->
            
              query
            
            =
            
              $query
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns an array from query row or false if no more rows * @return mixed */
              
            
            
              
                function
              
            
             getRow 
            
              (
            
            
              )
            
            
              {
            
            
              if
            
            
              (
            
            
              $row
            
            =
            
              
                mysql_fetch_array
              
            
            
              (
            
            
              $this
            
            ->
            
              query
            
            ,MYSQL_ASSOC
            
              )
            
            
              )
            
            
              return
            
            
              $row
            
            ;
        
            
              else
            
            
              return
            
            
              
                false
              
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns the number of rows affected * @return int */
              
            
            
              
                function
              
            
             rowCount 
            
              (
            
            
              )
            
            
              {
            
            
              return
            
            
              
                mysql_num_rows
              
            
            
              (
            
            
              $this
            
            ->
            
              query
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns false if no errors or returns a MySQL error message * @return mixed */
              
            
            
              
                function
              
            
             isError 
            
              (
            
            
              )
            
            
              {
            
            
              $error
            
            =
            
              $this
            
            ->
            
              da
            
            ->
            
              isError
            
            
              (
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
            !
            
              
                empty
              
            
            
              (
            
            
              $error
            
            
              )
            
            
              )
            
            
              return
            
            
              $error
            
            ;
        
            
              else
            
            
              return
            
            
              
                false
              
            
            ;
    
            
              }
            
            
              }
            
            
              
                ?>
              
            
          

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

            
              
                <?php
              
            
            
              
                /** * Base class for data access objects */
              
            
            
              
                class
              
            
             Dao 
            
              {
            
            
              
                /** * Private * $da stores data access object */
              
            
            
              
                var
              
            
            
              $da
            
            ;

    
            
              
                //! A constructor
              
            
            
              
                /** * Constructs the Dao * @param $da instance of the DataAccess class */
              
            
            
              
                function
              
            
             Dao 
            
              (
            
             & 
            
              $da
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              da
            
            =
            
              $da
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
              
            
            
              
                function
              
            
             & retrieve 
            
              (
            
            
              $sql
            
            
              )
            
            
              {
            
            
              $result
            
            =& 
            
              $this
            
            ->
            
              da
            
            ->
            
              fetch
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
            
              $error
            
            =
            
              $result
            
            ->
            
              isError
            
            
              (
            
            
              )
            
            
              )
            
            
              {
            
            
              
                trigger_error
              
            
            
              (
            
            
              $error
            
            
              )
            
            ;
            
            
              return
            
            
              
                false
              
            
            ;
        
            
              }
            
            
              else
            
            
              {
            
            
              return
            
            
              $result
            
            ;
        
            
              }
            
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
              
            
            
              
                function
              
            
             update 
            
              (
            
            
              $sql
            
            
              )
            
            
              {
            
            
              $result
            
            =
            
              $this
            
            ->
            
              da
            
            ->
            
              fetch
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
            
              $error
            
            =
            
              $result
            
            ->
            
              isError
            
            
              (
            
            
              )
            
            
              )
            
            
              {
            
            
              
                trigger_error
              
            
            
              (
            
            
              $error
            
            
              )
            
            ;
            
            
              return
            
            
              
                false
              
            
            ;
        
            
              }
            
            
              else
            
            
              {
            
            
              return
            
            
              
                true
              
            
            ;
        
            
              }
            
            
              }
            
            
              }
            
            
              
                ?>
              
            
          

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

            CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

          

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

            
              
                <?php
              
            
            
              
                /** * Data Access Object for Log Table */
              
            
            
              
                class
              
            
             LogDao extends Dao 
            
              {
            
            
              
                //! A constructor
              
            
            
              
                /** * Constructs the LogDao * @param $da instance of the DataAccess class */
              
            
            
              
                function
              
            
             LogDao 
            
              (
            
             & 
            
              $da
            
            
              )
            
            
              {
            
            
        Dao::
            
              Dao
            
            
              (
            
            
              $da
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Gets a log files * @return object a result object */
              
            
            
              
                function
              
            
             & searchAll 
            
              (
            
            
              $start
            
            =
            
              
                false
              
            
            ,
            
              $rows
            
            =
            
              
                false
              
            
            
              )
            
            
              {
            
            
              $sql
            
            =
            
              "SELECT * FROM log ORDER BY date DESC"
            
            ;
        
            
              if
            
            
              (
            
            
              $start
            
            
              )
            
            
              {
            
            
              $sql
            
            .=
            
              " LIMIT "
            
            .
            
              $start
            
            ;
            
            
              if
            
            
              (
            
            
              $rows
            
            
              )
            
            
              $sql
            
            .=
            
              ", "
            
            .
            
              $rows
            
            ;
        
            
              }
            
            
              return
            
            
              $this
            
            ->
            
              retrieve
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Searches logs by IP address * @return object a result object */
              
            
            
              
                function
              
            
             & searchByAddress 
            
              (
            
            
              $address
            
            
              )
            
            
              {
            
            
              $sql
            
            =
            
              "SELECT * FROM log WHERE address='"
            
            .
            
              $address
            
            .
            
              "'"
            
            .
            
            
              " ORDER BY date DESC"
            
            ;
        
            
              return
            
            
              $this
            
            ->
            
              retrieve
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Searches logs by country * @return object a result object */
              
            
            
              
                function
              
            
             & searchByCountry 
            
              (
            
            
              $country
            
            
              )
            
            
              {
            
            
              $sql
            
            =
            
              "SELECT * FROM log WHERE country='"
            
            .
            
              $country
            
            .
            
              "'"
            
            .
            
            
              " ORDER BY date DESC"
            
            ;
        
            
              return
            
            
              $this
            
            ->
            
              retrieve
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Searches logs by id * @return object a result object */
              
            
            
              
                function
              
            
             & searchByID 
            
              (
            
            
              $id
            
            
              )
            
            
              {
            
            
              $sql
            
            =
            
              "SELECT * FROM log WHERE id='"
            
            .
            
              $id
            
            .
            
              "'"
            
            ;
        
            
              return
            
            
              $this
            
            ->
            
              retrieve
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
    
            
              }
            
            
              
                function
              
            
             & totalRows 
            
              (
            
            
              )
            
            
              {
            
            
              $sql
            
            =
            
              "SELECT count(*) as count FROM log"
            
            ;
        
            
              return
            
            
              $this
            
            ->
            
              retrieve
            
            
              (
            
            
              $sql
            
            
              )
            
            ;
    
            
              }
            
            
              }
            
            
              
                ?>
              
            
          

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

            
              
                <?php
              
            
            
              
                /** * Modelling log data */
              
            
            
              
                class
              
            
             LogModel 
            
              {
            
            
              
                /** * Private * $dao stores data access object */
              
            
            
              
                var
              
            
            
              $dao
            
            ; 

    
            
              
                /** * Private * $result stores result object */
              
            
            
              
                var
              
            
            
              $result
            
            ;

    
            
              
                /** * Private * $rowCount stores number of rows returned */
              
            
            
              
                var
              
            
            
              $numPages
            
            ;

    
            
              
                //! A constructor
              
            
            
              
                /** * Constructs the LogModel * @param $da instance of the DataAccess class */
              
            
            
              
                function
              
            
             LogModel 
            
              (
            
             & 
            
              $dao
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              dao
            
            =& 
            
              $dao
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Gets a paged result set * @param $page the page to view from result set * @return void */
              
            
            
              
                function
              
            
             listLogs 
            
              (
            
            
              $page
            
            =
            
              1
            
            
              )
            
            
              {
            
            
              $rows
            
            =
            
              20
            
            ;       
        
            
              $start
            
            =
            
              $rows
            
            *
            
              $page
            
            ;
        
            
              $this
            
            ->
            
              result
            
            =& 
            
              $this
            
            ->
            
              dao
            
            ->
            
              searchAll
            
            
              (
            
            
              $start
            
            ,
            
              $rows
            
            
              )
            
            ;
        
            
              $numRowsRes
            
            =
            
              $this
            
            ->
            
              dao
            
            ->
            
              totalRows
            
            
              (
            
            
              )
            
            ;
        
            
              $numRow
            
            =
            
              $numRowsRes
            
            ->
            
              getRow
            
            
              (
            
            
              )
            
            ;
        
            
              $numRows
            
            =
            
              $numRow
            
            
              [
            
            
              'count'
            
            
              ]
            
            ;
        
            
              $this
            
            ->
            
              numPages
            
            =
            
              
                floor
              
            
            
              (
            
            
              $numRows
            
            /
            
              $rows
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns the number of pages in result set * @return int */
              
            
            
              
                function
              
            
             getNumPages 
            
              (
            
            
              )
            
            
              {
            
            
              return
            
            
              $this
            
            ->
            
              numPages
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
              
            
            
              
                function
              
            
             searchLogs
            
              (
            
            
              $searchBy
            
            =
            
              'address'
            
            ,
            
              $searchString
            
            
              )
            
            
              {
            
            
              switch
            
            
              (
            
            
              $searchBy
            
            
              )
            
            
              {
            
            
              case
            
            
              "country"
            
            :
                
            
              $this
            
            ->
            
              result
            
            =& 
            
              $this
            
            ->
            
              dao
            
            ->
            
              searchByCountry
            
            
              (
            
            
              $searchString
            
            
              )
            
            ;
                
            
              break
            
            ;
            
            
              
                default
              
            
            :
                
            
              $this
            
            ->
            
              result
            
            =& 
            
              $this
            
            ->
            
              dao
            
            ->
            
              searchByAddress
            
            
              (
            
            
              $searchString
            
            
              )
            
            ;
                
            
              break
            
            ;
        
            
              }
            
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Gets a single log row by it's id * @param $id of the log row * @return void */
              
            
            
              
                function
              
            
             listLog 
            
              (
            
            
              $id
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              result
            
            =& 
            
              $this
            
            ->
            
              dao
            
            ->
            
              searchByID
            
            
              (
            
            
              $id
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Gets the data from a single row * @return array a single log row */
              
            
            
              
                function
              
            
             getLog
            
              (
            
            
              )
            
            
              {
            
            
              return
            
            
              $this
            
            ->
            
              result
            
            ->
            
              getRow
            
            
              (
            
            
              )
            
            ;
    
            
              }
            
            
              }
            
            
              
                ?>
              
            
          

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

            
              
                <?php
              
            
            
              
                /** * Controls the application */
              
            
            
              
                class
              
            
             LogController 
            
              {
            
            
              
                /** * Private * $model an instance of LogModel */
              
            
            
              
                var
              
            
            
              $model
            
            ;

    
            
              
                /** * Private * $view an instance of LogView */
              
            
            
              
                var
              
            
            
              $view
            
            ;

    
            
              
                //! A constructor.
              
            
            
              
                /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
              
            
            
              
                function
              
            
             LogController 
            
              (
            
            & 
            
              $da
            
            
              )
            
            
              {
            
            
              $logDao
            
            =& 
            
              
                new
              
            
             LogDao
            
              (
            
            
              $da
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              model
            
            =& 
            
              
                new
              
            
             LogModel
            
              (
            
            
              $logDao
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns the view bound with data * @return string */
              
            
            
              
                function
              
            
             & getView 
            
              (
            
            
              )
            
            
              {
            
            
              return
            
            
              $this
            
            ->
            
              view
            
            ;
    
            
              }
            
            
              }
            
            
              
                // ...
              
            
            
              
                class
              
            
             LogTableController extends LogController 
            
              {
            
            
              
                //! A constructor.
              
            
            
              
                /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
              
            
            
              
                function
              
            
             LogTableController 
            
              (
            
            & 
            
              $da
            
            ,
            
              $getvars
            
            =
            
              
                null
              
            
            
              )
            
            
              {
            
            
        LogController::
            
              LogController
            
            
              (
            
            
              $da
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
             !
            
              
                isset
              
            
            
              (
            
            
              $getvars
            
            
              [
            
            
              'page'
            
            
              ]
            
            
              )
            
            
              )
            
            
              $getvars
            
            
              [
            
            
              'page'
            
            
              ]
            
            =
            
              1
            
            ;
        
            
              $this
            
            ->
            
              view
            
            =& 
            
              
                new
              
            
             LogTableView
            
              (
            
            
              $this
            
            ->
            
              model
            
            ,
            
              $getvars
            
            
              [
            
            
              'page'
            
            
              ]
            
            
              )
            
            ;
    
            
              }
            
            
              }
            
            
              
                // ...
              
            
            
              
                ?>
              
            
          

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

            
              
                <?php
              
            
            
              
                /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
              
            
            
              
                // A function to create static instances of the DOM API
              
            
            
              
                function
              
            
             staticDom
            
              (
            
            
              $type
            
            =
            
              
                null
              
            
            ,
            
              $source
            
            =
            
              
                null
              
            
            
              )
            
            
              {
            
            
              
                // Declare a static variable to hold the dom object
              
            
            
              
                static
              
            
            
              $dom
            
            ;

    
            
              
                // If the instance is not there, create one
              
            
            
              if
            
            
              (
            
            !
            
              
                isset
              
            
            
              (
            
            
              $dom
            
            
              )
            
            
              )
            
            
              {
            
            
              
                // Deal with the possible ways DOM can be constructed
              
            
            
              switch
            
            
              (
            
            
              $type
            
            
              )
            
            
              {
            
            
              case
            
            
              "file"
            
            :
                
            
              $dom
            
            =domxml_open_file
            
              (
            
            
              $source
            
            
              )
            
            ; 
            
              
                // $source: path to file
              
            
            
              break
            
            ;
            
            
              case
            
            
              "mem"
            
            :
                
            
              $dom
            
            =domxml_open_mem
            
              (
            
            
              $source
            
            
              )
            
            ; 
            
              
                // $sounce: XML as string
              
            
            
              break
            
            ;
            
            
              
                default
              
            
            :
                
            
              $dom
            
            =domxml_new_doc
            
              (
            
            
              '1.0'
            
            
              )
            
            ; 
            
              
                // create a new one
              
            
            
              break
            
            ;
        
            
              }
            
            
              }
            
            
              return
            
            
              (
            
            
              $dom
            
            
              )
            
            ;

            
              }
            
            
              
                /** * Base Widget class */
              
            
            
              
                class
              
            
             Widget 
            
              {
            
            
              
                /** * Private * $dom an instance of the DOM API */
              
            
            
              
                var
              
            
            
              $dom
            
            ;

    
            
              
                /** * Private * $out whether to return a DOM object or an XML string */
              
            
            
              
                var
              
            
            
              $out
            
            ;

    
            
              
                /** * Private * $widget stores a widget object */
              
            
            
              
                var
              
            
            
              $widget
            
            ;

    
            
              
                //! A Constructor
              
            
            
              
                /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
              
            
            
              
                function
              
            
             Widget 
            
              (
            
            
              $out
            
            =
            
              'string'
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              dom
            
            =& staticDom
            
              (
            
            
              )
            
            ; 
            
              
                // Construct DOM from static instance
              
            
            
              $this
            
            ->
            
              out
            
            =
            
              $out
            
            ;
    
            
              }
            
            
              
                //! A manipulator
              
            
            
              
                /** * Abstract widget creation method * @return void */
              
            
            
              
                function
              
            
             createWidget 
            
              (
            
            
              )
            
            
              {
            
            
              
                // Empty
              
            
            
              }
            
            
              
                //! A manipulator
              
            
            
              
                /** * Abstract widget finalization method * @return void */
              
            
            
              
                function
              
            
             finalizeWidget 
            
              (
            
            
              )
            
            
              {
            
            
              
                // Empty
              
            
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Change the current value of $this->out * @return void */
              
            
            
              
                function
              
            
             setOut 
            
              (
            
            
              $out
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              out
            
            =
            
              $out
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a generic widget to the current widget * @return void */
              
            
            
              
                function
              
            
             addWidget
            
              (
            
            
              $newWidget
            
            
              )
            
            
              {
            
            
              $newWidget
            
            ->
            
              setOut
            
            
              (
            
            
              'object'
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              widget
            
            ->
            
              append_child
            
            
              (
            
            
              $newWidget
            
            ->
            
              fetch
            
            
              (
            
            
              )
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a generic attibute to the current widget * @return void */
              
            
            
              
                function
              
            
             addAttribute
            
              (
            
            
              $name
            
            ,
            
              $value
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              widget
            
            ->
            
              set_attribute
            
            
              (
            
            
              $name
            
            ,
            
              $value
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Places text in the widget * @return void */
              
            
            
              
                function
              
            
             addText
            
              (
            
            
              $text
            
            
              )
            
            
              {
            
            
              $text
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_text_node
            
            
              (
            
            
              $text
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              widget
            
            ->
            
              append_child
            
            
              (
            
            
              $text
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a class="" attribute to the current widget * @return void */
              
            
            
              
                function
              
            
             addClass
            
              (
            
            
              $class
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              widget
            
            ->
            
              set_attribute
            
            
              (
            
            
              'class'
            
            ,
            
              $class
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a style="" attribute to the current widget * @return void */
              
            
            
              
                function
              
            
             addStyle
            
              (
            
            
              $style
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              widget
            
            ->
            
              set_attribute
            
            
              (
            
            
              'style'
            
            ,
            
              $style
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Returns either XML as a string or a DOM object * @return mixed */
              
            
            
              
                function
              
            
             &fetch 
            
              (
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              finalizeWidget
            
            
              (
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
            
              $this
            
            ->
            
              out
            
            ==
            
              'string'
            
            
              )
            
            
              {
            
            
              return
            
            
              $this
            
            ->
            
              dom
            
            ->
            
              dump_node
            
            
              (
            
            
              $this
            
            ->
            
              widget
            
            
              )
            
            ;
        
            
              }
            
            
              else
            
            
              {
            
            
              return
            
            
              $this
            
            ->
            
              widget
            
            ;
        
            
              }
            
            
              }
            
            
              }
            
            
              
                class
              
            
             PageWidget extends Widget 
            
              {
            
            
              
                /** * Private * $head XML object for <head /> */
              
            
            
              
                var
              
            
            
              $head
            
            ;

    
            
              
                /** * Private * $body XML object for <body /> */
              
            
            
              
                var
              
            
            
              $body
            
            ;

    
            
              
                //! A constructor
              
            
            
              
                /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
              
            
            
              
                function
              
            
             PageWidget
            
              (
            
            
              $out
            
            =
            
              'string'
            
            
              )
            
            
              {
            
            
        Widget::
            
              Widget
            
            
              (
            
            
              $out
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              createWidget
            
            
              (
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! A manipulator
              
            
            
              
                /** * Page widget creation method * @return void */
              
            
            
              
                function
              
            
             createWidget 
            
              (
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              widget
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_element
            
            
              (
            
            
              'html'
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              head
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_element
            
            
              (
            
            
              'head'
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              body
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_element
            
            
              (
            
            
              'body'
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! A manipulator
              
            
            
              
                /** * Page widget finalization method * @return void */
              
            
            
              
                function
              
            
             finalizeWidget 
            
              (
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              widget
            
            ->
            
              append_child
            
            
              (
            
            
              $this
            
            ->
            
              head
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              widget
            
            ->
            
              append_child
            
            
              (
            
            
              $this
            
            ->
            
              body
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a title element * @return void */
              
            
            
              
                function
              
            
             addTitle 
            
              (
            
            
              $text
            
            
              )
            
            
              {
            
            
              $title
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_element
            
            
              (
            
            
              'title'
            
            
              )
            
            ;
        
            
              $text
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_text_node
            
            
              (
            
            
              $text
            
            
              )
            
            ;
        
            
              $title
            
            ->
            
              append_child
            
            
              (
            
            
              $text
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              head
            
            ->
            
              append_child
            
            
              (
            
            
              $title
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Adds a link tag for CSS files * @return void */
              
            
            
              
                function
              
            
             cssLink 
            
              (
            
            
              $url
            
            
              )
            
            
              {
            
            
              $cssLink
            
            =
            
              $this
            
            ->
            
              dom
            
            ->
            
              create_element
            
            
              (
            
            
              'link'
            
            
              )
            
            ;
        
            
              $cssLink
            
            ->
            
              set_attribute
            
            
              (
            
            
              'href'
            
            ,
            
              $url
            
            
              )
            
            ;
        
            
              $cssLink
            
            ->
            
              set_attribute
            
            
              (
            
            
              'type'
            
            ,
            
              'text/css'
            
            
              )
            
            ;
        
            
              $cssLink
            
            ->
            
              set_attribute
            
            
              (
            
            
              'rel'
            
            ,
            
              'stylesheet'
            
            
              )
            
            ;
        
            
              $this
            
            ->
            
              head
            
            ->
            
              append_child
            
            
              (
            
            
              $cssLink
            
            
              )
            
            ;
    
            
              }
            
            
              
                //! An accessor
              
            
            
              
                /** * Appends a widget to $this->body * @return void */
              
            
            
              
                function
              
            
             addBody
            
              (
            
            
              $widget
            
            
              )
            
            
              {
            
            
              $this
            
            ->
            
              body
            
            ->
            
              append_child
            
            
              (
            
            
              $widget
            
            ->
            
              fetch
            
            
              (
            
            
              )
            
            
              )
            
            ;
    
            
              }
            
            
              }
            
            
              
                // ... etc. etc.
              
            
          

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

            ___FCKpd___7
          

Of particular interest is this line;

                    $table->addRow(LogTableWidget::getPager($this->page,$numPages));

          

Here the code for rendering the “Google-like” result pager is farmed off to another class;

            
              
                <?php
              
            
            
              
                // ...
              
            
            
              
                class
              
            
             LogTableWidget 
            
              {
            
            
              
                function
              
            
             getPager
            
              (
            
            
              $page
            
            ,
            
              $numPages
            
            
              )
            
            
              {
            
            
              $row
            
            =
            
              
                new
              
            
             RowWidget
            
              (
            
            
              'object'
            
            
              )
            
            ;
        
            
              $cell
            
            =
            
              
                new
              
            
             CellWidget
            
              (
            
            
              'object'
            
            
              )
            
            ;
        
            
              $cell
            
            ->
            
              addAttribute
            
            
              (
            
            
              'align'
            
            ,
            
              'center'
            
            
              )
            
            ;
        
            
              $cell
            
            ->
            
              addAttribute
            
            
              (
            
            
              'colspan'
            
            ,
            
              '5'
            
            
              )
            
            ;
        
            
              if
            
            
              (
            
            
              $numPages
            
            !=
            
              1
            
            &&
            
              $page
            
            !=
            
              1
            
            
              )
            
            
              {
            
            
              $link
            
            =
            
              
                new
              
            
             LinkWidget
            
              (
            
            
              'object'
            
            
              )
            
            ;
            
            
              $link
            
            ->
            
              addLink
            
            
              (
            
            
              

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * A simple class for querying MySQL */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccess 
                  
                    {
                  
                  
                    
                      /** * Private * $db stores a database resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $db
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                    
                  
                  
                    
                      function
                    
                  
                   DataAccess 
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  ,
                  
                    $db
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  =
                  
                    
                      mysql_pconnect
                    
                  
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  
                    )
                  
                  ;
        
                  
                    
                      mysql_select_db
                    
                  
                  
                    (
                  
                  
                    $db
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & fetch
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      new
                    
                  
                   DataAccessResult
                  
                    (
                  
                  
                    $this
                  
                  ,
                  
                    
                      mysql_query
                    
                  
                  
                    (
                  
                  
                    $sql
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns any MySQL errors * @return string a MySQL error */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_error
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      /** * Fetches MySQL database rows as objects */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccessResult 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;
    
                  
                    
                      /** * Private * $query stores a query resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $query
                  
                  ;

    
                  
                    
                      function
                    
                  
                   DataAccessResult
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $query
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =& 
                  
                    $da
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    query
                  
                  =
                  
                    $query
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns an array from query row or false if no more rows * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   getRow 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    if
                  
                  
                    (
                  
                  
                    $row
                  
                  =
                  
                    
                      mysql_fetch_array
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  ,MYSQL_ASSOC
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $row
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of rows affected * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   rowCount 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_num_rows
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $error
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      empty
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $error
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Base class for data access objects */
                    
                  
                  
                    
                      class
                    
                  
                   Dao 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the Dao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   Dao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =
                  
                    $da
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & retrieve 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $result
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                    
                  
                  
                    
                      function
                    
                  
                   update 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      true
                    
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                  CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Data Access Object for Log Table */
                    
                  
                  
                    
                      class
                    
                  
                   LogDao extends Dao 
                  
                    {
                  
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogDao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
        Dao::
                  
                    Dao
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a log files * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchAll 
                  
                    (
                  
                  
                    $start
                  
                  =
                  
                    
                      false
                    
                  
                  ,
                  
                    $rows
                  
                  =
                  
                    
                      false
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log ORDER BY date DESC"
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $start
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  .=
                  
                    " LIMIT "
                  
                  .
                  
                    $start
                  
                  ;
            
                  
                    if
                  
                  
                    (
                  
                  
                    $rows
                  
                  
                    )
                  
                  
                    $sql
                  
                  .=
                  
                    ", "
                  
                  .
                  
                    $rows
                  
                  ;
        
                  
                    }
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by IP address * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByAddress 
                  
                    (
                  
                  
                    $address
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE address='"
                  
                  .
                  
                    $address
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by country * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByCountry 
                  
                    (
                  
                  
                    $country
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE country='"
                  
                  .
                  
                    $country
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by id * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByID 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE id='"
                  
                  .
                  
                    $id
                  
                  .
                  
                    "'"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      function
                    
                  
                   & totalRows 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT count(*) as count FROM log"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Modelling log data */
                    
                  
                  
                    
                      class
                    
                  
                   LogModel 
                  
                    {
                  
                  
                    
                      /** * Private * $dao stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dao
                  
                  ; 

    
                  
                    
                      /** * Private * $result stores result object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $result
                  
                  ;

    
                  
                    
                      /** * Private * $rowCount stores number of rows returned */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $numPages
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogModel 
                  
                    (
                  
                   & 
                  
                    $dao
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  =& 
                  
                    $dao
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLogs 
                  
                    (
                  
                  
                    $page
                  
                  =
                  
                    1
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $rows
                  
                  =
                  
                    20
                  
                  ;       
        
                  
                    $start
                  
                  =
                  
                    $rows
                  
                  *
                  
                    $page
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchAll
                  
                  
                    (
                  
                  
                    $start
                  
                  ,
                  
                    $rows
                  
                  
                    )
                  
                  ;
        
                  
                    $numRowsRes
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    totalRows
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRow
                  
                  =
                  
                    $numRowsRes
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRows
                  
                  =
                  
                    $numRow
                  
                  
                    [
                  
                  
                    'count'
                  
                  
                    ]
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  =
                  
                    
                      floor
                    
                  
                  
                    (
                  
                  
                    $numRows
                  
                  /
                  
                    $rows
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of pages in result set * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   getNumPages 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   searchLogs
                  
                    (
                  
                  
                    $searchBy
                  
                  =
                  
                    'address'
                  
                  ,
                  
                    $searchString
                  
                  
                    )
                  
                  
                    {
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $searchBy
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "country"
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByCountry
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByAddress
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLog 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByID
                  
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets the data from a single row * @return array a single log row */
                    
                  
                  
                    
                      function
                    
                  
                   getLog
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Controls the application */
                    
                  
                  
                    
                      class
                    
                  
                   LogController 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of LogModel */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $view an instance of LogView */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $view
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $logDao
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogDao
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogModel
                  
                    (
                  
                  
                    $logDao
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the view bound with data * @return string */
                    
                  
                  
                    
                      function
                    
                  
                   & getView 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    view
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      class
                    
                  
                   LogTableController extends LogController 
                  
                    {
                  
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogTableController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $getvars
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
        LogController::
                  
                    LogController
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                   !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  
                    )
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  =
                  
                    1
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    view
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogTableView
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  ,
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      ?>
                    
                  
                

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                  
                    
                      <?php
                    
                  
                  
                    
                      /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                    
                  
                  
                    
                      // A function to create static instances of the DOM API
                    
                  
                  
                    
                      function
                    
                  
                   staticDom
                  
                    (
                  
                  
                    $type
                  
                  =
                  
                    
                      null
                    
                  
                  ,
                  
                    $source
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Declare a static variable to hold the dom object
                    
                  
                  
                    
                      static
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      // If the instance is not there, create one
                    
                  
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Deal with the possible ways DOM can be constructed
                    
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $type
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "file"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_file
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $source: path to file
                    
                  
                  
                    break
                  
                  ;
            
                  
                    case
                  
                  
                    "mem"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_mem
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $sounce: XML as string
                    
                  
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $dom
                  
                  =domxml_new_doc
                  
                    (
                  
                  
                    '1.0'
                  
                  
                    )
                  
                  ; 
                  
                    
                      // create a new one
                    
                  
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    return
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  ;

                  
                    }
                  
                  
                    
                      /** * Base Widget class */
                    
                  
                  
                    
                      class
                    
                  
                   Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $dom an instance of the DOM API */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      /** * Private * $out whether to return a DOM object or an XML string */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $out
                  
                  ;

    
                  
                    
                      /** * Private * $widget stores a widget object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $widget
                  
                  ;

    
                  
                    
                      //! A Constructor
                    
                  
                  
                    
                      /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   Widget 
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  =& staticDom
                  
                    (
                  
                  
                    )
                  
                  ; 
                  
                    
                      // Construct DOM from static instance
                    
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Change the current value of $this->out * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   setOut 
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic widget to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addWidget
                  
                    (
                  
                  
                    $newWidget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $newWidget
                  
                  ->
                  
                    setOut
                  
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $newWidget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic attibute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addAttribute
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Places text in the widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addText
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a class="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addClass
                  
                    (
                  
                  
                    $class
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'class'
                  
                  ,
                  
                    $class
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a style="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addStyle
                  
                    (
                  
                  
                    $style
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'style'
                  
                  ,
                  
                    $style
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns either XML as a string or a DOM object * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   &fetch 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    finalizeWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  ==
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    dump_node
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  
                    )
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      class
                    
                  
                   PageWidget extends Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $head XML object for <head /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $head
                  
                  ;

    
                  
                    
                      /** * Private * $body XML object for <body /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $body
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
        Widget::
                  
                    Widget
                  
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    createWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'html'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'head'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    body
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'body'
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    head
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a title element * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addTitle 
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $title
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'title'
                  
                  
                    )
                  
                  ;
        
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $title
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $title
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a link tag for CSS files * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   cssLink 
                  
                    (
                  
                  
                    $url
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $cssLink
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'link'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'href'
                  
                  ,
                  
                    $url
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'type'
                  
                  ,
                  
                    'text/css'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'rel'
                  
                  ,
                  
                    'stylesheet'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $cssLink
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Appends a widget to $this->body * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addBody
                  
                    (
                  
                  
                    $widget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $widget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ... etc. etc.
                    
                  
                

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Binds log data to HTML content */
                    
                  
                  
                    
                      class
                    
                  
                   LogView 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of the LogModel class */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $output contains instance of DOM page object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $output
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogView object * @param $model an instance of the LogModel class */
                    
                  
                  
                    
                      function
                    
                  
                   LogView 
                  
                    (
                  
                  &
                  
                    $model
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    $model
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Builds the top of an HTML page * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   create 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    output
                  
                  =
                  
                    
                      new
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    addTitle
                  
                  
                    (
                  
                  
                    'IP Log Files'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    cssLink
                  
                  
                    (
                  
                  
                    'css/style.css'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  =
                  
                    
                      new
                    
                  
                   LinkWidget
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  ->
                  
                    addLink
                  
                  
                    (
                  
                  
                    

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * A simple class for querying MySQL */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccess 
                        
                          {
                        
                        
                          
                            /** * Private * $db stores a database resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $db
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                          
                        
                        
                          
                            function
                          
                        
                         DataAccess 
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        ,
                        
                          $db
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        =
                        
                          
                            mysql_pconnect
                          
                        
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        
                          )
                        
                        ;
        
                        
                          
                            mysql_select_db
                          
                        
                        
                          (
                        
                        
                          $db
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & fetch
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            new
                          
                        
                         DataAccessResult
                        
                          (
                        
                        
                          $this
                        
                        ,
                        
                          
                            mysql_query
                          
                        
                        
                          (
                        
                        
                          $sql
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns any MySQL errors * @return string a MySQL error */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_error
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            /** * Fetches MySQL database rows as objects */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccessResult 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;
    
                        
                          
                            /** * Private * $query stores a query resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $query
                        
                        ;

    
                        
                          
                            function
                          
                        
                         DataAccessResult
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $query
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =& 
                        
                          $da
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        =
                        
                          $query
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns an array from query row or false if no more rows * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         getRow 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          if
                        
                        
                          (
                        
                        
                          $row
                        
                        =
                        
                          
                            mysql_fetch_array
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        ,MYSQL_ASSOC
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $row
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of rows affected * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         rowCount 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_num_rows
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $error
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            empty
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $error
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Base class for data access objects */
                          
                        
                        
                          
                            class
                          
                        
                         Dao 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the Dao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         Dao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =
                        
                          $da
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & retrieve 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $result
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                          
                        
                        
                          
                            function
                          
                        
                         update 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            true
                          
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                        CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                      

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Data Access Object for Log Table */
                          
                        
                        
                          
                            class
                          
                        
                         LogDao extends Dao 
                        
                          {
                        
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogDao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
        Dao::
                        
                          Dao
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a log files * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchAll 
                        
                          (
                        
                        
                          $start
                        
                        =
                        
                          
                            false
                          
                        
                        ,
                        
                          $rows
                        
                        =
                        
                          
                            false
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log ORDER BY date DESC"
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $start
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        .=
                        
                          " LIMIT "
                        
                        .
                        
                          $start
                        
                        ;
            
                        
                          if
                        
                        
                          (
                        
                        
                          $rows
                        
                        
                          )
                        
                        
                          $sql
                        
                        .=
                        
                          ", "
                        
                        .
                        
                          $rows
                        
                        ;
        
                        
                          }
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by IP address * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByAddress 
                        
                          (
                        
                        
                          $address
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE address='"
                        
                        .
                        
                          $address
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by country * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByCountry 
                        
                          (
                        
                        
                          $country
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE country='"
                        
                        .
                        
                          $country
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by id * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByID 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE id='"
                        
                        .
                        
                          $id
                        
                        .
                        
                          "'"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            function
                          
                        
                         & totalRows 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT count(*) as count FROM log"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Modelling log data */
                          
                        
                        
                          
                            class
                          
                        
                         LogModel 
                        
                          {
                        
                        
                          
                            /** * Private * $dao stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dao
                        
                        ; 

    
                        
                          
                            /** * Private * $result stores result object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $result
                        
                        ;

    
                        
                          
                            /** * Private * $rowCount stores number of rows returned */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $numPages
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogModel 
                        
                          (
                        
                         & 
                        
                          $dao
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        =& 
                        
                          $dao
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLogs 
                        
                          (
                        
                        
                          $page
                        
                        =
                        
                          1
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $rows
                        
                        =
                        
                          20
                        
                        ;       
        
                        
                          $start
                        
                        =
                        
                          $rows
                        
                        *
                        
                          $page
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchAll
                        
                        
                          (
                        
                        
                          $start
                        
                        ,
                        
                          $rows
                        
                        
                          )
                        
                        ;
        
                        
                          $numRowsRes
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          totalRows
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRow
                        
                        =
                        
                          $numRowsRes
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRows
                        
                        =
                        
                          $numRow
                        
                        
                          [
                        
                        
                          'count'
                        
                        
                          ]
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        =
                        
                          
                            floor
                          
                        
                        
                          (
                        
                        
                          $numRows
                        
                        /
                        
                          $rows
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of pages in result set * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         getNumPages 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         searchLogs
                        
                          (
                        
                        
                          $searchBy
                        
                        =
                        
                          'address'
                        
                        ,
                        
                          $searchString
                        
                        
                          )
                        
                        
                          {
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $searchBy
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "country"
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByCountry
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByAddress
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLog 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByID
                        
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets the data from a single row * @return array a single log row */
                          
                        
                        
                          
                            function
                          
                        
                         getLog
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Controls the application */
                          
                        
                        
                          
                            class
                          
                        
                         LogController 
                        
                          {
                        
                        
                          
                            /** * Private * $model an instance of LogModel */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $model
                        
                        ;

    
                        
                          
                            /** * Private * $view an instance of LogView */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $view
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $logDao
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogDao
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogModel
                        
                          (
                        
                        
                          $logDao
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the view bound with data * @return string */
                          
                        
                        
                          
                            function
                          
                        
                         & getView 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            class
                          
                        
                         LogTableController extends LogController 
                        
                          {
                        
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogTableController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $getvars
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
        LogController::
                        
                          LogController
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                         !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        
                          )
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        =
                        
                          1
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogTableView
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        ,
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            ?>
                          
                        
                      

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                        
                          
                            <?php
                          
                        
                        
                          
                            /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                          
                        
                        
                          
                            // A function to create static instances of the DOM API
                          
                        
                        
                          
                            function
                          
                        
                         staticDom
                        
                          (
                        
                        
                          $type
                        
                        =
                        
                          
                            null
                          
                        
                        ,
                        
                          $source
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Declare a static variable to hold the dom object
                          
                        
                        
                          
                            static
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            // If the instance is not there, create one
                          
                        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Deal with the possible ways DOM can be constructed
                          
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $type
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "file"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_file
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $source: path to file
                          
                        
                        
                          break
                        
                        ;
            
                        
                          case
                        
                        
                          "mem"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_mem
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $sounce: XML as string
                          
                        
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $dom
                        
                        =domxml_new_doc
                        
                          (
                        
                        
                          '1.0'
                        
                        
                          )
                        
                        ; 
                        
                          
                            // create a new one
                          
                        
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          return
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        ;

                        
                          }
                        
                        
                          
                            /** * Base Widget class */
                          
                        
                        
                          
                            class
                          
                        
                         Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $dom an instance of the DOM API */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            /** * Private * $out whether to return a DOM object or an XML string */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $out
                        
                        ;

    
                        
                          
                            /** * Private * $widget stores a widget object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $widget
                        
                        ;

    
                        
                          
                            //! A Constructor
                          
                        
                        
                          
                            /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         Widget 
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        =& staticDom
                        
                          (
                        
                        
                          )
                        
                        ; 
                        
                          
                            // Construct DOM from static instance
                          
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Change the current value of $this->out * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         setOut 
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic widget to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addWidget
                        
                          (
                        
                        
                          $newWidget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $newWidget
                        
                        ->
                        
                          setOut
                        
                        
                          (
                        
                        
                          'object'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $newWidget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic attibute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addAttribute
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Places text in the widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addText
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a class="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addClass
                        
                          (
                        
                        
                          $class
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'class'
                        
                        ,
                        
                          $class
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a style="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addStyle
                        
                          (
                        
                        
                          $style
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'style'
                        
                        ,
                        
                          $style
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns either XML as a string or a DOM object * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         &fetch 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          finalizeWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        ==
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          dump_node
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        
                          )
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            class
                          
                        
                         PageWidget extends Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $head XML object for <head /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $head
                        
                        ;

    
                        
                          
                            /** * Private * $body XML object for <body /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $body
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         PageWidget
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
        Widget::
                        
                          Widget
                        
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          createWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'html'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'head'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'body'
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a title element * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addTitle 
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $title
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'title'
                        
                        
                          )
                        
                        ;
        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $title
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $title
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a link tag for CSS files * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         cssLink 
                        
                          (
                        
                        
                          $url
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $cssLink
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'link'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'href'
                        
                        ,
                        
                          $url
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'type'
                        
                        ,
                        
                          'text/css'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'rel'
                        
                        ,
                        
                          'stylesheet'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $cssLink
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Appends a widget to $this->body * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addBody
                        
                          (
                        
                        
                          $widget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $widget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ... etc. etc.
                          
                        
                      

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                        ___FCKpd___7
                      

Of particular interest is this line;

                                $table->addRow(LogTableWidget::getPager($this->page,$numPages));

                      

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                        ___FCKpd___9
                      

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] ) ; $link -> addText ( 'Start Over' ) ; $this -> output -> addBody ( $link ) ; } //! An manipulator /** * Abstract method with completes the page * @return void */ function finalize ( ) { // Empty } //! An accessor /** * Returns the page * @return void */ function display ( ) { $this -> finalize ( ) ; return $this -> output -> fetch ( ) ; } } // ... class LogTableView extends LogView { /** * Private * $page the page we're viewing */ var $page ; //! A constructor. /** * Constucts a new LogView object * @param $model an instance of the LogModel class */ function LogTableView ( & $model , $page = 1 ) { LogView:: LogView ( $model ) ; $this -> page = $page ; $this -> create ( ) ; } //! A manipulator /** * Renders a log table * @return void */ function logTable ( ) { $this -> model -> listLogs ( $this -> page ) ; $heading = new HeadingWidget ( 'object' , 2 ) ; $heading -> addText ( 'Paged Log Result Set' ) ; $heading -> addAttribute ( 'align' , 'center' ) ; $this -> output -> addBody ( $heading ) ; // Build result table $table = new TableWidget ( 'object' ) ; $table -> addAttribute ( 'align' , 'center' ) ; $table -> addAttribute ( 'width' , '750' ) ; // Build result pager $numPages = $this -> model -> getNumPages ( ) ; $table -> addRow ( LogTableWidget:: getPager ( $this -> page , $numPages ) ) ; // Build table rows $table -> addRow ( LogTableWidget:: getHeader ( ) ) ; while ( $log = $this -> model -> getLog ( ) ) { if ( $alt == '#f6f7f8' ) $alt = '#ffffff' ; else $alt = '#f6f7f8' ; $table -> addRow ( LogTableWidget:: getRow ( $log , $alt ) ) ; } $this -> output -> addBody ( $table ) ; } //! An manipulator /** * Runs the logItem method * @return void */ function finalize ( ) { $this -> logTable ( ) ; } } // ... ?>

Of particular interest is this line;

                  ___FCKpd___8
                

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                  ___FCKpd___9
                

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] . '?page=1' ) ; $link -> addText ( '<<' ) ; $cell -> addWidget ( $link ) ; $cell -> addText ( ' ' ) ; } if ( ( $page - 5 ) > 1 ) { $cell -> addText ( '...' ) ; } for ( $i = ( $page - 5 ) ; $i <= ( $page - 1 ) ; $i ++ ) { if ( $i > 0 ) { $link = new LinkWidget ( 'object' ) ; $link -> addLink (

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * A simple class for querying MySQL */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccess 
                  
                    {
                  
                  
                    
                      /** * Private * $db stores a database resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $db
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                    
                  
                  
                    
                      function
                    
                  
                   DataAccess 
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  ,
                  
                    $db
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  =
                  
                    
                      mysql_pconnect
                    
                  
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  
                    )
                  
                  ;
        
                  
                    
                      mysql_select_db
                    
                  
                  
                    (
                  
                  
                    $db
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & fetch
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      new
                    
                  
                   DataAccessResult
                  
                    (
                  
                  
                    $this
                  
                  ,
                  
                    
                      mysql_query
                    
                  
                  
                    (
                  
                  
                    $sql
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns any MySQL errors * @return string a MySQL error */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_error
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      /** * Fetches MySQL database rows as objects */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccessResult 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;
    
                  
                    
                      /** * Private * $query stores a query resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $query
                  
                  ;

    
                  
                    
                      function
                    
                  
                   DataAccessResult
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $query
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =& 
                  
                    $da
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    query
                  
                  =
                  
                    $query
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns an array from query row or false if no more rows * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   getRow 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    if
                  
                  
                    (
                  
                  
                    $row
                  
                  =
                  
                    
                      mysql_fetch_array
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  ,MYSQL_ASSOC
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $row
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of rows affected * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   rowCount 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_num_rows
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $error
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      empty
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $error
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Base class for data access objects */
                    
                  
                  
                    
                      class
                    
                  
                   Dao 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the Dao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   Dao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =
                  
                    $da
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & retrieve 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $result
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                    
                  
                  
                    
                      function
                    
                  
                   update 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      true
                    
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                  CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Data Access Object for Log Table */
                    
                  
                  
                    
                      class
                    
                  
                   LogDao extends Dao 
                  
                    {
                  
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogDao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
        Dao::
                  
                    Dao
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a log files * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchAll 
                  
                    (
                  
                  
                    $start
                  
                  =
                  
                    
                      false
                    
                  
                  ,
                  
                    $rows
                  
                  =
                  
                    
                      false
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log ORDER BY date DESC"
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $start
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  .=
                  
                    " LIMIT "
                  
                  .
                  
                    $start
                  
                  ;
            
                  
                    if
                  
                  
                    (
                  
                  
                    $rows
                  
                  
                    )
                  
                  
                    $sql
                  
                  .=
                  
                    ", "
                  
                  .
                  
                    $rows
                  
                  ;
        
                  
                    }
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by IP address * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByAddress 
                  
                    (
                  
                  
                    $address
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE address='"
                  
                  .
                  
                    $address
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by country * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByCountry 
                  
                    (
                  
                  
                    $country
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE country='"
                  
                  .
                  
                    $country
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by id * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByID 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE id='"
                  
                  .
                  
                    $id
                  
                  .
                  
                    "'"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      function
                    
                  
                   & totalRows 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT count(*) as count FROM log"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Modelling log data */
                    
                  
                  
                    
                      class
                    
                  
                   LogModel 
                  
                    {
                  
                  
                    
                      /** * Private * $dao stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dao
                  
                  ; 

    
                  
                    
                      /** * Private * $result stores result object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $result
                  
                  ;

    
                  
                    
                      /** * Private * $rowCount stores number of rows returned */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $numPages
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogModel 
                  
                    (
                  
                   & 
                  
                    $dao
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  =& 
                  
                    $dao
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLogs 
                  
                    (
                  
                  
                    $page
                  
                  =
                  
                    1
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $rows
                  
                  =
                  
                    20
                  
                  ;       
        
                  
                    $start
                  
                  =
                  
                    $rows
                  
                  *
                  
                    $page
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchAll
                  
                  
                    (
                  
                  
                    $start
                  
                  ,
                  
                    $rows
                  
                  
                    )
                  
                  ;
        
                  
                    $numRowsRes
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    totalRows
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRow
                  
                  =
                  
                    $numRowsRes
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRows
                  
                  =
                  
                    $numRow
                  
                  
                    [
                  
                  
                    'count'
                  
                  
                    ]
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  =
                  
                    
                      floor
                    
                  
                  
                    (
                  
                  
                    $numRows
                  
                  /
                  
                    $rows
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of pages in result set * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   getNumPages 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   searchLogs
                  
                    (
                  
                  
                    $searchBy
                  
                  =
                  
                    'address'
                  
                  ,
                  
                    $searchString
                  
                  
                    )
                  
                  
                    {
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $searchBy
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "country"
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByCountry
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByAddress
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLog 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByID
                  
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets the data from a single row * @return array a single log row */
                    
                  
                  
                    
                      function
                    
                  
                   getLog
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Controls the application */
                    
                  
                  
                    
                      class
                    
                  
                   LogController 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of LogModel */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $view an instance of LogView */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $view
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $logDao
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogDao
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogModel
                  
                    (
                  
                  
                    $logDao
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the view bound with data * @return string */
                    
                  
                  
                    
                      function
                    
                  
                   & getView 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    view
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      class
                    
                  
                   LogTableController extends LogController 
                  
                    {
                  
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogTableController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $getvars
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
        LogController::
                  
                    LogController
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                   !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  
                    )
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  =
                  
                    1
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    view
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogTableView
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  ,
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      ?>
                    
                  
                

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                  
                    
                      <?php
                    
                  
                  
                    
                      /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                    
                  
                  
                    
                      // A function to create static instances of the DOM API
                    
                  
                  
                    
                      function
                    
                  
                   staticDom
                  
                    (
                  
                  
                    $type
                  
                  =
                  
                    
                      null
                    
                  
                  ,
                  
                    $source
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Declare a static variable to hold the dom object
                    
                  
                  
                    
                      static
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      // If the instance is not there, create one
                    
                  
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Deal with the possible ways DOM can be constructed
                    
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $type
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "file"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_file
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $source: path to file
                    
                  
                  
                    break
                  
                  ;
            
                  
                    case
                  
                  
                    "mem"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_mem
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $sounce: XML as string
                    
                  
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $dom
                  
                  =domxml_new_doc
                  
                    (
                  
                  
                    '1.0'
                  
                  
                    )
                  
                  ; 
                  
                    
                      // create a new one
                    
                  
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    return
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  ;

                  
                    }
                  
                  
                    
                      /** * Base Widget class */
                    
                  
                  
                    
                      class
                    
                  
                   Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $dom an instance of the DOM API */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      /** * Private * $out whether to return a DOM object or an XML string */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $out
                  
                  ;

    
                  
                    
                      /** * Private * $widget stores a widget object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $widget
                  
                  ;

    
                  
                    
                      //! A Constructor
                    
                  
                  
                    
                      /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   Widget 
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  =& staticDom
                  
                    (
                  
                  
                    )
                  
                  ; 
                  
                    
                      // Construct DOM from static instance
                    
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Change the current value of $this->out * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   setOut 
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic widget to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addWidget
                  
                    (
                  
                  
                    $newWidget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $newWidget
                  
                  ->
                  
                    setOut
                  
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $newWidget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic attibute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addAttribute
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Places text in the widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addText
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a class="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addClass
                  
                    (
                  
                  
                    $class
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'class'
                  
                  ,
                  
                    $class
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a style="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addStyle
                  
                    (
                  
                  
                    $style
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'style'
                  
                  ,
                  
                    $style
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns either XML as a string or a DOM object * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   &fetch 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    finalizeWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  ==
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    dump_node
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  
                    )
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      class
                    
                  
                   PageWidget extends Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $head XML object for <head /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $head
                  
                  ;

    
                  
                    
                      /** * Private * $body XML object for <body /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $body
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
        Widget::
                  
                    Widget
                  
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    createWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'html'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'head'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    body
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'body'
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    head
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a title element * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addTitle 
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $title
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'title'
                  
                  
                    )
                  
                  ;
        
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $title
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $title
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a link tag for CSS files * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   cssLink 
                  
                    (
                  
                  
                    $url
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $cssLink
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'link'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'href'
                  
                  ,
                  
                    $url
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'type'
                  
                  ,
                  
                    'text/css'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'rel'
                  
                  ,
                  
                    'stylesheet'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $cssLink
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Appends a widget to $this->body * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addBody
                  
                    (
                  
                  
                    $widget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $widget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ... etc. etc.
                    
                  
                

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Binds log data to HTML content */
                    
                  
                  
                    
                      class
                    
                  
                   LogView 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of the LogModel class */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $output contains instance of DOM page object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $output
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogView object * @param $model an instance of the LogModel class */
                    
                  
                  
                    
                      function
                    
                  
                   LogView 
                  
                    (
                  
                  &
                  
                    $model
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    $model
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Builds the top of an HTML page * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   create 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    output
                  
                  =
                  
                    
                      new
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    addTitle
                  
                  
                    (
                  
                  
                    'IP Log Files'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    cssLink
                  
                  
                    (
                  
                  
                    'css/style.css'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  =
                  
                    
                      new
                    
                  
                   LinkWidget
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  ->
                  
                    addLink
                  
                  
                    (
                  
                  
                    

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * A simple class for querying MySQL */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccess 
                        
                          {
                        
                        
                          
                            /** * Private * $db stores a database resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $db
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                          
                        
                        
                          
                            function
                          
                        
                         DataAccess 
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        ,
                        
                          $db
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        =
                        
                          
                            mysql_pconnect
                          
                        
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        
                          )
                        
                        ;
        
                        
                          
                            mysql_select_db
                          
                        
                        
                          (
                        
                        
                          $db
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & fetch
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            new
                          
                        
                         DataAccessResult
                        
                          (
                        
                        
                          $this
                        
                        ,
                        
                          
                            mysql_query
                          
                        
                        
                          (
                        
                        
                          $sql
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns any MySQL errors * @return string a MySQL error */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_error
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            /** * Fetches MySQL database rows as objects */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccessResult 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;
    
                        
                          
                            /** * Private * $query stores a query resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $query
                        
                        ;

    
                        
                          
                            function
                          
                        
                         DataAccessResult
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $query
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =& 
                        
                          $da
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        =
                        
                          $query
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns an array from query row or false if no more rows * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         getRow 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          if
                        
                        
                          (
                        
                        
                          $row
                        
                        =
                        
                          
                            mysql_fetch_array
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        ,MYSQL_ASSOC
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $row
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of rows affected * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         rowCount 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_num_rows
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $error
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            empty
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $error
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Base class for data access objects */
                          
                        
                        
                          
                            class
                          
                        
                         Dao 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the Dao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         Dao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =
                        
                          $da
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & retrieve 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $result
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                          
                        
                        
                          
                            function
                          
                        
                         update 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            true
                          
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                        CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                      

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Data Access Object for Log Table */
                          
                        
                        
                          
                            class
                          
                        
                         LogDao extends Dao 
                        
                          {
                        
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogDao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
        Dao::
                        
                          Dao
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a log files * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchAll 
                        
                          (
                        
                        
                          $start
                        
                        =
                        
                          
                            false
                          
                        
                        ,
                        
                          $rows
                        
                        =
                        
                          
                            false
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log ORDER BY date DESC"
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $start
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        .=
                        
                          " LIMIT "
                        
                        .
                        
                          $start
                        
                        ;
            
                        
                          if
                        
                        
                          (
                        
                        
                          $rows
                        
                        
                          )
                        
                        
                          $sql
                        
                        .=
                        
                          ", "
                        
                        .
                        
                          $rows
                        
                        ;
        
                        
                          }
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by IP address * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByAddress 
                        
                          (
                        
                        
                          $address
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE address='"
                        
                        .
                        
                          $address
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by country * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByCountry 
                        
                          (
                        
                        
                          $country
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE country='"
                        
                        .
                        
                          $country
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by id * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByID 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE id='"
                        
                        .
                        
                          $id
                        
                        .
                        
                          "'"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            function
                          
                        
                         & totalRows 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT count(*) as count FROM log"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Modelling log data */
                          
                        
                        
                          
                            class
                          
                        
                         LogModel 
                        
                          {
                        
                        
                          
                            /** * Private * $dao stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dao
                        
                        ; 

    
                        
                          
                            /** * Private * $result stores result object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $result
                        
                        ;

    
                        
                          
                            /** * Private * $rowCount stores number of rows returned */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $numPages
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogModel 
                        
                          (
                        
                         & 
                        
                          $dao
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        =& 
                        
                          $dao
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLogs 
                        
                          (
                        
                        
                          $page
                        
                        =
                        
                          1
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $rows
                        
                        =
                        
                          20
                        
                        ;       
        
                        
                          $start
                        
                        =
                        
                          $rows
                        
                        *
                        
                          $page
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchAll
                        
                        
                          (
                        
                        
                          $start
                        
                        ,
                        
                          $rows
                        
                        
                          )
                        
                        ;
        
                        
                          $numRowsRes
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          totalRows
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRow
                        
                        =
                        
                          $numRowsRes
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRows
                        
                        =
                        
                          $numRow
                        
                        
                          [
                        
                        
                          'count'
                        
                        
                          ]
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        =
                        
                          
                            floor
                          
                        
                        
                          (
                        
                        
                          $numRows
                        
                        /
                        
                          $rows
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of pages in result set * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         getNumPages 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         searchLogs
                        
                          (
                        
                        
                          $searchBy
                        
                        =
                        
                          'address'
                        
                        ,
                        
                          $searchString
                        
                        
                          )
                        
                        
                          {
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $searchBy
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "country"
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByCountry
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByAddress
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLog 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByID
                        
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets the data from a single row * @return array a single log row */
                          
                        
                        
                          
                            function
                          
                        
                         getLog
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Controls the application */
                          
                        
                        
                          
                            class
                          
                        
                         LogController 
                        
                          {
                        
                        
                          
                            /** * Private * $model an instance of LogModel */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $model
                        
                        ;

    
                        
                          
                            /** * Private * $view an instance of LogView */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $view
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $logDao
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogDao
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogModel
                        
                          (
                        
                        
                          $logDao
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the view bound with data * @return string */
                          
                        
                        
                          
                            function
                          
                        
                         & getView 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            class
                          
                        
                         LogTableController extends LogController 
                        
                          {
                        
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogTableController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $getvars
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
        LogController::
                        
                          LogController
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                         !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        
                          )
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        =
                        
                          1
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogTableView
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        ,
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            ?>
                          
                        
                      

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                        
                          
                            <?php
                          
                        
                        
                          
                            /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                          
                        
                        
                          
                            // A function to create static instances of the DOM API
                          
                        
                        
                          
                            function
                          
                        
                         staticDom
                        
                          (
                        
                        
                          $type
                        
                        =
                        
                          
                            null
                          
                        
                        ,
                        
                          $source
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Declare a static variable to hold the dom object
                          
                        
                        
                          
                            static
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            // If the instance is not there, create one
                          
                        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Deal with the possible ways DOM can be constructed
                          
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $type
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "file"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_file
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $source: path to file
                          
                        
                        
                          break
                        
                        ;
            
                        
                          case
                        
                        
                          "mem"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_mem
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $sounce: XML as string
                          
                        
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $dom
                        
                        =domxml_new_doc
                        
                          (
                        
                        
                          '1.0'
                        
                        
                          )
                        
                        ; 
                        
                          
                            // create a new one
                          
                        
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          return
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        ;

                        
                          }
                        
                        
                          
                            /** * Base Widget class */
                          
                        
                        
                          
                            class
                          
                        
                         Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $dom an instance of the DOM API */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            /** * Private * $out whether to return a DOM object or an XML string */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $out
                        
                        ;

    
                        
                          
                            /** * Private * $widget stores a widget object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $widget
                        
                        ;

    
                        
                          
                            //! A Constructor
                          
                        
                        
                          
                            /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         Widget 
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        =& staticDom
                        
                          (
                        
                        
                          )
                        
                        ; 
                        
                          
                            // Construct DOM from static instance
                          
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Change the current value of $this->out * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         setOut 
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic widget to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addWidget
                        
                          (
                        
                        
                          $newWidget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $newWidget
                        
                        ->
                        
                          setOut
                        
                        
                          (
                        
                        
                          'object'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $newWidget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic attibute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addAttribute
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Places text in the widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addText
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a class="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addClass
                        
                          (
                        
                        
                          $class
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'class'
                        
                        ,
                        
                          $class
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a style="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addStyle
                        
                          (
                        
                        
                          $style
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'style'
                        
                        ,
                        
                          $style
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns either XML as a string or a DOM object * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         &fetch 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          finalizeWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        ==
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          dump_node
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        
                          )
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            class
                          
                        
                         PageWidget extends Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $head XML object for <head /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $head
                        
                        ;

    
                        
                          
                            /** * Private * $body XML object for <body /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $body
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         PageWidget
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
        Widget::
                        
                          Widget
                        
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          createWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'html'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'head'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'body'
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a title element * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addTitle 
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $title
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'title'
                        
                        
                          )
                        
                        ;
        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $title
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $title
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a link tag for CSS files * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         cssLink 
                        
                          (
                        
                        
                          $url
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $cssLink
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'link'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'href'
                        
                        ,
                        
                          $url
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'type'
                        
                        ,
                        
                          'text/css'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'rel'
                        
                        ,
                        
                          'stylesheet'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $cssLink
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Appends a widget to $this->body * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addBody
                        
                          (
                        
                        
                          $widget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $widget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ... etc. etc.
                          
                        
                      

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                        ___FCKpd___7
                      

Of particular interest is this line;

                                $table->addRow(LogTableWidget::getPager($this->page,$numPages));

                      

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                        ___FCKpd___9
                      

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] ) ; $link -> addText ( 'Start Over' ) ; $this -> output -> addBody ( $link ) ; } //! An manipulator /** * Abstract method with completes the page * @return void */ function finalize ( ) { // Empty } //! An accessor /** * Returns the page * @return void */ function display ( ) { $this -> finalize ( ) ; return $this -> output -> fetch ( ) ; } } // ... class LogTableView extends LogView { /** * Private * $page the page we're viewing */ var $page ; //! A constructor. /** * Constucts a new LogView object * @param $model an instance of the LogModel class */ function LogTableView ( & $model , $page = 1 ) { LogView:: LogView ( $model ) ; $this -> page = $page ; $this -> create ( ) ; } //! A manipulator /** * Renders a log table * @return void */ function logTable ( ) { $this -> model -> listLogs ( $this -> page ) ; $heading = new HeadingWidget ( 'object' , 2 ) ; $heading -> addText ( 'Paged Log Result Set' ) ; $heading -> addAttribute ( 'align' , 'center' ) ; $this -> output -> addBody ( $heading ) ; // Build result table $table = new TableWidget ( 'object' ) ; $table -> addAttribute ( 'align' , 'center' ) ; $table -> addAttribute ( 'width' , '750' ) ; // Build result pager $numPages = $this -> model -> getNumPages ( ) ; $table -> addRow ( LogTableWidget:: getPager ( $this -> page , $numPages ) ) ; // Build table rows $table -> addRow ( LogTableWidget:: getHeader ( ) ) ; while ( $log = $this -> model -> getLog ( ) ) { if ( $alt == '#f6f7f8' ) $alt = '#ffffff' ; else $alt = '#f6f7f8' ; $table -> addRow ( LogTableWidget:: getRow ( $log , $alt ) ) ; } $this -> output -> addBody ( $table ) ; } //! An manipulator /** * Runs the logItem method * @return void */ function finalize ( ) { $this -> logTable ( ) ; } } // ... ?>

Of particular interest is this line;

                  ___FCKpd___8
                

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                  ___FCKpd___9
                

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] . '?page=' . $i ) ; $link -> addText ( $i . ' ' ) ; $cell -> addWidget ( $link ) ; } } $link = new LinkWidget ( 'object' ) ; $link -> addText ( '[' . $page . '] ' ) ; $cell -> addWidget ( $link ) ; for ( $i = ( $page + 1 ) ; $i <= ( $page + 5 ) ; $i ++ ) { if ( $i <= $numPages ) { $link = new LinkWidget ( 'object' ) ; $link -> addLink (

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * A simple class for querying MySQL */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccess 
                  
                    {
                  
                  
                    
                      /** * Private * $db stores a database resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $db
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                    
                  
                  
                    
                      function
                    
                  
                   DataAccess 
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  ,
                  
                    $db
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  =
                  
                    
                      mysql_pconnect
                    
                  
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  
                    )
                  
                  ;
        
                  
                    
                      mysql_select_db
                    
                  
                  
                    (
                  
                  
                    $db
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & fetch
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      new
                    
                  
                   DataAccessResult
                  
                    (
                  
                  
                    $this
                  
                  ,
                  
                    
                      mysql_query
                    
                  
                  
                    (
                  
                  
                    $sql
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns any MySQL errors * @return string a MySQL error */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_error
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      /** * Fetches MySQL database rows as objects */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccessResult 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;
    
                  
                    
                      /** * Private * $query stores a query resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $query
                  
                  ;

    
                  
                    
                      function
                    
                  
                   DataAccessResult
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $query
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =& 
                  
                    $da
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    query
                  
                  =
                  
                    $query
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns an array from query row or false if no more rows * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   getRow 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    if
                  
                  
                    (
                  
                  
                    $row
                  
                  =
                  
                    
                      mysql_fetch_array
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  ,MYSQL_ASSOC
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $row
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of rows affected * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   rowCount 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_num_rows
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $error
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      empty
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $error
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Base class for data access objects */
                    
                  
                  
                    
                      class
                    
                  
                   Dao 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the Dao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   Dao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =
                  
                    $da
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & retrieve 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $result
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                    
                  
                  
                    
                      function
                    
                  
                   update 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      true
                    
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                  CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Data Access Object for Log Table */
                    
                  
                  
                    
                      class
                    
                  
                   LogDao extends Dao 
                  
                    {
                  
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogDao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
        Dao::
                  
                    Dao
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a log files * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchAll 
                  
                    (
                  
                  
                    $start
                  
                  =
                  
                    
                      false
                    
                  
                  ,
                  
                    $rows
                  
                  =
                  
                    
                      false
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log ORDER BY date DESC"
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $start
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  .=
                  
                    " LIMIT "
                  
                  .
                  
                    $start
                  
                  ;
            
                  
                    if
                  
                  
                    (
                  
                  
                    $rows
                  
                  
                    )
                  
                  
                    $sql
                  
                  .=
                  
                    ", "
                  
                  .
                  
                    $rows
                  
                  ;
        
                  
                    }
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by IP address * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByAddress 
                  
                    (
                  
                  
                    $address
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE address='"
                  
                  .
                  
                    $address
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by country * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByCountry 
                  
                    (
                  
                  
                    $country
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE country='"
                  
                  .
                  
                    $country
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by id * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByID 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE id='"
                  
                  .
                  
                    $id
                  
                  .
                  
                    "'"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      function
                    
                  
                   & totalRows 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT count(*) as count FROM log"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Modelling log data */
                    
                  
                  
                    
                      class
                    
                  
                   LogModel 
                  
                    {
                  
                  
                    
                      /** * Private * $dao stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dao
                  
                  ; 

    
                  
                    
                      /** * Private * $result stores result object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $result
                  
                  ;

    
                  
                    
                      /** * Private * $rowCount stores number of rows returned */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $numPages
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogModel 
                  
                    (
                  
                   & 
                  
                    $dao
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  =& 
                  
                    $dao
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLogs 
                  
                    (
                  
                  
                    $page
                  
                  =
                  
                    1
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $rows
                  
                  =
                  
                    20
                  
                  ;       
        
                  
                    $start
                  
                  =
                  
                    $rows
                  
                  *
                  
                    $page
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchAll
                  
                  
                    (
                  
                  
                    $start
                  
                  ,
                  
                    $rows
                  
                  
                    )
                  
                  ;
        
                  
                    $numRowsRes
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    totalRows
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRow
                  
                  =
                  
                    $numRowsRes
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRows
                  
                  =
                  
                    $numRow
                  
                  
                    [
                  
                  
                    'count'
                  
                  
                    ]
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  =
                  
                    
                      floor
                    
                  
                  
                    (
                  
                  
                    $numRows
                  
                  /
                  
                    $rows
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of pages in result set * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   getNumPages 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   searchLogs
                  
                    (
                  
                  
                    $searchBy
                  
                  =
                  
                    'address'
                  
                  ,
                  
                    $searchString
                  
                  
                    )
                  
                  
                    {
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $searchBy
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "country"
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByCountry
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByAddress
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLog 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByID
                  
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets the data from a single row * @return array a single log row */
                    
                  
                  
                    
                      function
                    
                  
                   getLog
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Controls the application */
                    
                  
                  
                    
                      class
                    
                  
                   LogController 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of LogModel */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $view an instance of LogView */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $view
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $logDao
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogDao
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogModel
                  
                    (
                  
                  
                    $logDao
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the view bound with data * @return string */
                    
                  
                  
                    
                      function
                    
                  
                   & getView 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    view
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      class
                    
                  
                   LogTableController extends LogController 
                  
                    {
                  
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogTableController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $getvars
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
        LogController::
                  
                    LogController
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                   !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  
                    )
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  =
                  
                    1
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    view
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogTableView
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  ,
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      ?>
                    
                  
                

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                  
                    
                      <?php
                    
                  
                  
                    
                      /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                    
                  
                  
                    
                      // A function to create static instances of the DOM API
                    
                  
                  
                    
                      function
                    
                  
                   staticDom
                  
                    (
                  
                  
                    $type
                  
                  =
                  
                    
                      null
                    
                  
                  ,
                  
                    $source
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Declare a static variable to hold the dom object
                    
                  
                  
                    
                      static
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      // If the instance is not there, create one
                    
                  
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Deal with the possible ways DOM can be constructed
                    
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $type
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "file"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_file
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $source: path to file
                    
                  
                  
                    break
                  
                  ;
            
                  
                    case
                  
                  
                    "mem"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_mem
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $sounce: XML as string
                    
                  
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $dom
                  
                  =domxml_new_doc
                  
                    (
                  
                  
                    '1.0'
                  
                  
                    )
                  
                  ; 
                  
                    
                      // create a new one
                    
                  
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    return
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  ;

                  
                    }
                  
                  
                    
                      /** * Base Widget class */
                    
                  
                  
                    
                      class
                    
                  
                   Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $dom an instance of the DOM API */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      /** * Private * $out whether to return a DOM object or an XML string */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $out
                  
                  ;

    
                  
                    
                      /** * Private * $widget stores a widget object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $widget
                  
                  ;

    
                  
                    
                      //! A Constructor
                    
                  
                  
                    
                      /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   Widget 
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  =& staticDom
                  
                    (
                  
                  
                    )
                  
                  ; 
                  
                    
                      // Construct DOM from static instance
                    
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Change the current value of $this->out * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   setOut 
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic widget to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addWidget
                  
                    (
                  
                  
                    $newWidget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $newWidget
                  
                  ->
                  
                    setOut
                  
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $newWidget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic attibute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addAttribute
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Places text in the widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addText
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a class="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addClass
                  
                    (
                  
                  
                    $class
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'class'
                  
                  ,
                  
                    $class
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a style="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addStyle
                  
                    (
                  
                  
                    $style
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'style'
                  
                  ,
                  
                    $style
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns either XML as a string or a DOM object * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   &fetch 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    finalizeWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  ==
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    dump_node
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  
                    )
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      class
                    
                  
                   PageWidget extends Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $head XML object for <head /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $head
                  
                  ;

    
                  
                    
                      /** * Private * $body XML object for <body /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $body
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
        Widget::
                  
                    Widget
                  
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    createWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'html'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'head'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    body
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'body'
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    head
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a title element * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addTitle 
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $title
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'title'
                  
                  
                    )
                  
                  ;
        
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $title
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $title
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a link tag for CSS files * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   cssLink 
                  
                    (
                  
                  
                    $url
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $cssLink
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'link'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'href'
                  
                  ,
                  
                    $url
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'type'
                  
                  ,
                  
                    'text/css'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'rel'
                  
                  ,
                  
                    'stylesheet'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $cssLink
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Appends a widget to $this->body * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addBody
                  
                    (
                  
                  
                    $widget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $widget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ... etc. etc.
                    
                  
                

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Binds log data to HTML content */
                    
                  
                  
                    
                      class
                    
                  
                   LogView 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of the LogModel class */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $output contains instance of DOM page object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $output
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogView object * @param $model an instance of the LogModel class */
                    
                  
                  
                    
                      function
                    
                  
                   LogView 
                  
                    (
                  
                  &
                  
                    $model
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    $model
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Builds the top of an HTML page * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   create 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    output
                  
                  =
                  
                    
                      new
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    addTitle
                  
                  
                    (
                  
                  
                    'IP Log Files'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    cssLink
                  
                  
                    (
                  
                  
                    'css/style.css'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  =
                  
                    
                      new
                    
                  
                   LinkWidget
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  ->
                  
                    addLink
                  
                  
                    (
                  
                  
                    

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * A simple class for querying MySQL */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccess 
                        
                          {
                        
                        
                          
                            /** * Private * $db stores a database resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $db
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                          
                        
                        
                          
                            function
                          
                        
                         DataAccess 
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        ,
                        
                          $db
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        =
                        
                          
                            mysql_pconnect
                          
                        
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        
                          )
                        
                        ;
        
                        
                          
                            mysql_select_db
                          
                        
                        
                          (
                        
                        
                          $db
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & fetch
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            new
                          
                        
                         DataAccessResult
                        
                          (
                        
                        
                          $this
                        
                        ,
                        
                          
                            mysql_query
                          
                        
                        
                          (
                        
                        
                          $sql
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns any MySQL errors * @return string a MySQL error */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_error
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            /** * Fetches MySQL database rows as objects */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccessResult 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;
    
                        
                          
                            /** * Private * $query stores a query resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $query
                        
                        ;

    
                        
                          
                            function
                          
                        
                         DataAccessResult
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $query
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =& 
                        
                          $da
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        =
                        
                          $query
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns an array from query row or false if no more rows * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         getRow 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          if
                        
                        
                          (
                        
                        
                          $row
                        
                        =
                        
                          
                            mysql_fetch_array
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        ,MYSQL_ASSOC
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $row
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of rows affected * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         rowCount 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_num_rows
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $error
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            empty
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $error
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Base class for data access objects */
                          
                        
                        
                          
                            class
                          
                        
                         Dao 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the Dao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         Dao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =
                        
                          $da
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & retrieve 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $result
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                          
                        
                        
                          
                            function
                          
                        
                         update 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            true
                          
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                        CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                      

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Data Access Object for Log Table */
                          
                        
                        
                          
                            class
                          
                        
                         LogDao extends Dao 
                        
                          {
                        
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogDao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
        Dao::
                        
                          Dao
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a log files * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchAll 
                        
                          (
                        
                        
                          $start
                        
                        =
                        
                          
                            false
                          
                        
                        ,
                        
                          $rows
                        
                        =
                        
                          
                            false
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log ORDER BY date DESC"
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $start
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        .=
                        
                          " LIMIT "
                        
                        .
                        
                          $start
                        
                        ;
            
                        
                          if
                        
                        
                          (
                        
                        
                          $rows
                        
                        
                          )
                        
                        
                          $sql
                        
                        .=
                        
                          ", "
                        
                        .
                        
                          $rows
                        
                        ;
        
                        
                          }
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by IP address * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByAddress 
                        
                          (
                        
                        
                          $address
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE address='"
                        
                        .
                        
                          $address
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by country * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByCountry 
                        
                          (
                        
                        
                          $country
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE country='"
                        
                        .
                        
                          $country
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by id * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByID 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE id='"
                        
                        .
                        
                          $id
                        
                        .
                        
                          "'"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            function
                          
                        
                         & totalRows 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT count(*) as count FROM log"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Modelling log data */
                          
                        
                        
                          
                            class
                          
                        
                         LogModel 
                        
                          {
                        
                        
                          
                            /** * Private * $dao stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dao
                        
                        ; 

    
                        
                          
                            /** * Private * $result stores result object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $result
                        
                        ;

    
                        
                          
                            /** * Private * $rowCount stores number of rows returned */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $numPages
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogModel 
                        
                          (
                        
                         & 
                        
                          $dao
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        =& 
                        
                          $dao
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLogs 
                        
                          (
                        
                        
                          $page
                        
                        =
                        
                          1
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $rows
                        
                        =
                        
                          20
                        
                        ;       
        
                        
                          $start
                        
                        =
                        
                          $rows
                        
                        *
                        
                          $page
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchAll
                        
                        
                          (
                        
                        
                          $start
                        
                        ,
                        
                          $rows
                        
                        
                          )
                        
                        ;
        
                        
                          $numRowsRes
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          totalRows
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRow
                        
                        =
                        
                          $numRowsRes
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRows
                        
                        =
                        
                          $numRow
                        
                        
                          [
                        
                        
                          'count'
                        
                        
                          ]
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        =
                        
                          
                            floor
                          
                        
                        
                          (
                        
                        
                          $numRows
                        
                        /
                        
                          $rows
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of pages in result set * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         getNumPages 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         searchLogs
                        
                          (
                        
                        
                          $searchBy
                        
                        =
                        
                          'address'
                        
                        ,
                        
                          $searchString
                        
                        
                          )
                        
                        
                          {
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $searchBy
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "country"
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByCountry
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByAddress
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLog 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByID
                        
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets the data from a single row * @return array a single log row */
                          
                        
                        
                          
                            function
                          
                        
                         getLog
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Controls the application */
                          
                        
                        
                          
                            class
                          
                        
                         LogController 
                        
                          {
                        
                        
                          
                            /** * Private * $model an instance of LogModel */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $model
                        
                        ;

    
                        
                          
                            /** * Private * $view an instance of LogView */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $view
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $logDao
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogDao
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogModel
                        
                          (
                        
                        
                          $logDao
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the view bound with data * @return string */
                          
                        
                        
                          
                            function
                          
                        
                         & getView 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            class
                          
                        
                         LogTableController extends LogController 
                        
                          {
                        
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogTableController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $getvars
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
        LogController::
                        
                          LogController
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                         !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        
                          )
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        =
                        
                          1
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogTableView
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        ,
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            ?>
                          
                        
                      

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                        
                          
                            <?php
                          
                        
                        
                          
                            /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                          
                        
                        
                          
                            // A function to create static instances of the DOM API
                          
                        
                        
                          
                            function
                          
                        
                         staticDom
                        
                          (
                        
                        
                          $type
                        
                        =
                        
                          
                            null
                          
                        
                        ,
                        
                          $source
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Declare a static variable to hold the dom object
                          
                        
                        
                          
                            static
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            // If the instance is not there, create one
                          
                        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Deal with the possible ways DOM can be constructed
                          
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $type
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "file"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_file
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $source: path to file
                          
                        
                        
                          break
                        
                        ;
            
                        
                          case
                        
                        
                          "mem"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_mem
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $sounce: XML as string
                          
                        
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $dom
                        
                        =domxml_new_doc
                        
                          (
                        
                        
                          '1.0'
                        
                        
                          )
                        
                        ; 
                        
                          
                            // create a new one
                          
                        
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          return
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        ;

                        
                          }
                        
                        
                          
                            /** * Base Widget class */
                          
                        
                        
                          
                            class
                          
                        
                         Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $dom an instance of the DOM API */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            /** * Private * $out whether to return a DOM object or an XML string */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $out
                        
                        ;

    
                        
                          
                            /** * Private * $widget stores a widget object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $widget
                        
                        ;

    
                        
                          
                            //! A Constructor
                          
                        
                        
                          
                            /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         Widget 
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        =& staticDom
                        
                          (
                        
                        
                          )
                        
                        ; 
                        
                          
                            // Construct DOM from static instance
                          
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Change the current value of $this->out * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         setOut 
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic widget to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addWidget
                        
                          (
                        
                        
                          $newWidget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $newWidget
                        
                        ->
                        
                          setOut
                        
                        
                          (
                        
                        
                          'object'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $newWidget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic attibute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addAttribute
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Places text in the widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addText
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a class="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addClass
                        
                          (
                        
                        
                          $class
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'class'
                        
                        ,
                        
                          $class
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a style="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addStyle
                        
                          (
                        
                        
                          $style
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'style'
                        
                        ,
                        
                          $style
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns either XML as a string or a DOM object * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         &fetch 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          finalizeWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        ==
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          dump_node
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        
                          )
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            class
                          
                        
                         PageWidget extends Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $head XML object for <head /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $head
                        
                        ;

    
                        
                          
                            /** * Private * $body XML object for <body /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $body
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         PageWidget
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
        Widget::
                        
                          Widget
                        
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          createWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'html'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'head'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'body'
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a title element * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addTitle 
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $title
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'title'
                        
                        
                          )
                        
                        ;
        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $title
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $title
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a link tag for CSS files * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         cssLink 
                        
                          (
                        
                        
                          $url
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $cssLink
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'link'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'href'
                        
                        ,
                        
                          $url
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'type'
                        
                        ,
                        
                          'text/css'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'rel'
                        
                        ,
                        
                          'stylesheet'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $cssLink
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Appends a widget to $this->body * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addBody
                        
                          (
                        
                        
                          $widget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $widget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ... etc. etc.
                          
                        
                      

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                        ___FCKpd___7
                      

Of particular interest is this line;

                                $table->addRow(LogTableWidget::getPager($this->page,$numPages));

                      

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                        ___FCKpd___9
                      

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] ) ; $link -> addText ( 'Start Over' ) ; $this -> output -> addBody ( $link ) ; } //! An manipulator /** * Abstract method with completes the page * @return void */ function finalize ( ) { // Empty } //! An accessor /** * Returns the page * @return void */ function display ( ) { $this -> finalize ( ) ; return $this -> output -> fetch ( ) ; } } // ... class LogTableView extends LogView { /** * Private * $page the page we're viewing */ var $page ; //! A constructor. /** * Constucts a new LogView object * @param $model an instance of the LogModel class */ function LogTableView ( & $model , $page = 1 ) { LogView:: LogView ( $model ) ; $this -> page = $page ; $this -> create ( ) ; } //! A manipulator /** * Renders a log table * @return void */ function logTable ( ) { $this -> model -> listLogs ( $this -> page ) ; $heading = new HeadingWidget ( 'object' , 2 ) ; $heading -> addText ( 'Paged Log Result Set' ) ; $heading -> addAttribute ( 'align' , 'center' ) ; $this -> output -> addBody ( $heading ) ; // Build result table $table = new TableWidget ( 'object' ) ; $table -> addAttribute ( 'align' , 'center' ) ; $table -> addAttribute ( 'width' , '750' ) ; // Build result pager $numPages = $this -> model -> getNumPages ( ) ; $table -> addRow ( LogTableWidget:: getPager ( $this -> page , $numPages ) ) ; // Build table rows $table -> addRow ( LogTableWidget:: getHeader ( ) ) ; while ( $log = $this -> model -> getLog ( ) ) { if ( $alt == '#f6f7f8' ) $alt = '#ffffff' ; else $alt = '#f6f7f8' ; $table -> addRow ( LogTableWidget:: getRow ( $log , $alt ) ) ; } $this -> output -> addBody ( $table ) ; } //! An manipulator /** * Runs the logItem method * @return void */ function finalize ( ) { $this -> logTable ( ) ; } } // ... ?>

Of particular interest is this line;

                  ___FCKpd___8
                

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                  ___FCKpd___9
                

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] . '?page=' . $i ) ; $link -> addText ( $i . ' ' ) ; $cell -> addWidget ( $link ) ; } } if ( ( $page + 5 ) < $numPages ) { $cell -> addText ( '...' ) ; } if ( $numPages != 1 && $page != $numPages ) { $link = new LinkWidget ( 'object' ) ; $link -> addLink (

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * A simple class for querying MySQL */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccess 
                  
                    {
                  
                  
                    
                      /** * Private * $db stores a database resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $db
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                    
                  
                  
                    
                      function
                    
                  
                   DataAccess 
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  ,
                  
                    $db
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  =
                  
                    
                      mysql_pconnect
                    
                  
                  
                    (
                  
                  
                    $host
                  
                  ,
                  
                    $user
                  
                  ,
                  
                    $pass
                  
                  
                    )
                  
                  ;
        
                  
                    
                      mysql_select_db
                    
                  
                  
                    (
                  
                  
                    $db
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & fetch
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      new
                    
                  
                   DataAccessResult
                  
                    (
                  
                  
                    $this
                  
                  ,
                  
                    
                      mysql_query
                    
                  
                  
                    (
                  
                  
                    $sql
                  
                  ,
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns any MySQL errors * @return string a MySQL error */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_error
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    db
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      /** * Fetches MySQL database rows as objects */
                    
                  
                  
                    
                      class
                    
                  
                   DataAccessResult 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;
    
                  
                    
                      /** * Private * $query stores a query resource */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $query
                  
                  ;

    
                  
                    
                      function
                    
                  
                   DataAccessResult
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $query
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =& 
                  
                    $da
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    query
                  
                  =
                  
                    $query
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns an array from query row or false if no more rows * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   getRow 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    if
                  
                  
                    (
                  
                  
                    $row
                  
                  =
                  
                    
                      mysql_fetch_array
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  ,MYSQL_ASSOC
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $row
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of rows affected * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   rowCount 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      mysql_num_rows
                    
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    query
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   isError 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $error
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      empty
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  
                    )
                  
                  
                    return
                  
                  
                    $error
                  
                  ;
        
                  
                    else
                  
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Base class for data access objects */
                    
                  
                  
                    
                      class
                    
                  
                   Dao 
                  
                    {
                  
                  
                    
                      /** * Private * $da stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $da
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the Dao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   Dao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    da
                  
                  =
                  
                    $da
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                    
                  
                  
                    
                      function
                    
                  
                   & retrieve 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $result
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                    
                  
                  
                    
                      function
                    
                  
                   update 
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $result
                  
                  =
                  
                    $this
                  
                  ->
                  
                    da
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $error
                  
                  =
                  
                    $result
                  
                  ->
                  
                    isError
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      trigger_error
                    
                  
                  
                    (
                  
                  
                    $error
                  
                  
                    )
                  
                  ;
            
                  
                    return
                  
                  
                    
                      false
                    
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    
                      true
                    
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                  CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Data Access Object for Log Table */
                    
                  
                  
                    
                      class
                    
                  
                   LogDao extends Dao 
                  
                    {
                  
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogDao 
                  
                    (
                  
                   & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
        Dao::
                  
                    Dao
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a log files * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchAll 
                  
                    (
                  
                  
                    $start
                  
                  =
                  
                    
                      false
                    
                  
                  ,
                  
                    $rows
                  
                  =
                  
                    
                      false
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log ORDER BY date DESC"
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $start
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  .=
                  
                    " LIMIT "
                  
                  .
                  
                    $start
                  
                  ;
            
                  
                    if
                  
                  
                    (
                  
                  
                    $rows
                  
                  
                    )
                  
                  
                    $sql
                  
                  .=
                  
                    ", "
                  
                  .
                  
                    $rows
                  
                  ;
        
                  
                    }
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by IP address * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByAddress 
                  
                    (
                  
                  
                    $address
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE address='"
                  
                  .
                  
                    $address
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by country * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByCountry 
                  
                    (
                  
                  
                    $country
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE country='"
                  
                  .
                  
                    $country
                  
                  .
                  
                    "'"
                  
                  .
            
                  
                    " ORDER BY date DESC"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches logs by id * @return object a result object */
                    
                  
                  
                    
                      function
                    
                  
                   & searchByID 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT * FROM log WHERE id='"
                  
                  .
                  
                    $id
                  
                  .
                  
                    "'"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      function
                    
                  
                   & totalRows 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $sql
                  
                  =
                  
                    "SELECT count(*) as count FROM log"
                  
                  ;
        
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    retrieve
                  
                  
                    (
                  
                  
                    $sql
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Modelling log data */
                    
                  
                  
                    
                      class
                    
                  
                   LogModel 
                  
                    {
                  
                  
                    
                      /** * Private * $dao stores data access object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dao
                  
                  ; 

    
                  
                    
                      /** * Private * $result stores result object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $result
                  
                  ;

    
                  
                    
                      /** * Private * $rowCount stores number of rows returned */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $numPages
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                    
                  
                  
                    
                      function
                    
                  
                   LogModel 
                  
                    (
                  
                   & 
                  
                    $dao
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  =& 
                  
                    $dao
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLogs 
                  
                    (
                  
                  
                    $page
                  
                  =
                  
                    1
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $rows
                  
                  =
                  
                    20
                  
                  ;       
        
                  
                    $start
                  
                  =
                  
                    $rows
                  
                  *
                  
                    $page
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchAll
                  
                  
                    (
                  
                  
                    $start
                  
                  ,
                  
                    $rows
                  
                  
                    )
                  
                  ;
        
                  
                    $numRowsRes
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    totalRows
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRow
                  
                  =
                  
                    $numRowsRes
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $numRows
                  
                  =
                  
                    $numRow
                  
                  
                    [
                  
                  
                    'count'
                  
                  
                    ]
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  =
                  
                    
                      floor
                    
                  
                  
                    (
                  
                  
                    $numRows
                  
                  /
                  
                    $rows
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the number of pages in result set * @return int */
                    
                  
                  
                    
                      function
                    
                  
                   getNumPages 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    numPages
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   searchLogs
                  
                    (
                  
                  
                    $searchBy
                  
                  =
                  
                    'address'
                  
                  ,
                  
                    $searchString
                  
                  
                    )
                  
                  
                    {
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $searchBy
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "country"
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByCountry
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByAddress
                  
                  
                    (
                  
                  
                    $searchString
                  
                  
                    )
                  
                  ;
                
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   listLog 
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  =& 
                  
                    $this
                  
                  ->
                  
                    dao
                  
                  ->
                  
                    searchByID
                  
                  
                    (
                  
                  
                    $id
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Gets the data from a single row * @return array a single log row */
                    
                  
                  
                    
                      function
                    
                  
                   getLog
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    result
                  
                  ->
                  
                    getRow
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      ?>
                    
                  
                

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Controls the application */
                    
                  
                  
                    
                      class
                    
                  
                   LogController 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of LogModel */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $view an instance of LogView */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $view
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $logDao
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogDao
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogModel
                  
                    (
                  
                  
                    $logDao
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns the view bound with data * @return string */
                    
                  
                  
                    
                      function
                    
                  
                   & getView 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    view
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      class
                    
                  
                   LogTableController extends LogController 
                  
                    {
                  
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                    
                  
                  
                    
                      function
                    
                  
                   LogTableController 
                  
                    (
                  
                  & 
                  
                    $da
                  
                  ,
                  
                    $getvars
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
        LogController::
                  
                    LogController
                  
                  
                    (
                  
                  
                    $da
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                   !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  
                    )
                  
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  =
                  
                    1
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    view
                  
                  =& 
                  
                    
                      new
                    
                  
                   LogTableView
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  ,
                  
                    $getvars
                  
                  
                    [
                  
                  
                    'page'
                  
                  
                    ]
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ...
                    
                  
                  
                    
                      ?>
                    
                  
                

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                  
                    
                      <?php
                    
                  
                  
                    
                      /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                    
                  
                  
                    
                      // A function to create static instances of the DOM API
                    
                  
                  
                    
                      function
                    
                  
                   staticDom
                  
                    (
                  
                  
                    $type
                  
                  =
                  
                    
                      null
                    
                  
                  ,
                  
                    $source
                  
                  =
                  
                    
                      null
                    
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Declare a static variable to hold the dom object
                    
                  
                  
                    
                      static
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      // If the instance is not there, create one
                    
                  
                  
                    if
                  
                  
                    (
                  
                  !
                  
                    
                      isset
                    
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Deal with the possible ways DOM can be constructed
                    
                  
                  
                    switch
                  
                  
                    (
                  
                  
                    $type
                  
                  
                    )
                  
                  
                    {
                  
                  
                    case
                  
                  
                    "file"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_file
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $source: path to file
                    
                  
                  
                    break
                  
                  ;
            
                  
                    case
                  
                  
                    "mem"
                  
                  :
                
                  
                    $dom
                  
                  =domxml_open_mem
                  
                    (
                  
                  
                    $source
                  
                  
                    )
                  
                  ; 
                  
                    
                      // $sounce: XML as string
                    
                  
                  
                    break
                  
                  ;
            
                  
                    
                      default
                    
                  
                  :
                
                  
                    $dom
                  
                  =domxml_new_doc
                  
                    (
                  
                  
                    '1.0'
                  
                  
                    )
                  
                  ; 
                  
                    
                      // create a new one
                    
                  
                  
                    break
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    return
                  
                  
                    (
                  
                  
                    $dom
                  
                  
                    )
                  
                  ;

                  
                    }
                  
                  
                    
                      /** * Base Widget class */
                    
                  
                  
                    
                      class
                    
                  
                   Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $dom an instance of the DOM API */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $dom
                  
                  ;

    
                  
                    
                      /** * Private * $out whether to return a DOM object or an XML string */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $out
                  
                  ;

    
                  
                    
                      /** * Private * $widget stores a widget object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $widget
                  
                  ;

    
                  
                    
                      //! A Constructor
                    
                  
                  
                    
                      /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   Widget 
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  =& staticDom
                  
                    (
                  
                  
                    )
                  
                  ; 
                  
                    
                      // Construct DOM from static instance
                    
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Abstract widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    
                      // Empty
                    
                  
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Change the current value of $this->out * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   setOut 
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  =
                  
                    $out
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic widget to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addWidget
                  
                    (
                  
                  
                    $newWidget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $newWidget
                  
                  ->
                  
                    setOut
                  
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $newWidget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a generic attibute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addAttribute
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    $name
                  
                  ,
                  
                    $value
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Places text in the widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addText
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a class="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addClass
                  
                    (
                  
                  
                    $class
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'class'
                  
                  ,
                  
                    $class
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a style="" attribute to the current widget * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addStyle
                  
                    (
                  
                  
                    $style
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'style'
                  
                  ,
                  
                    $style
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Returns either XML as a string or a DOM object * @return mixed */
                    
                  
                  
                    
                      function
                    
                  
                   &fetch 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    finalizeWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    if
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    out
                  
                  ==
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    dump_node
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  
                    )
                  
                  ;
        
                  
                    }
                  
                  
                    else
                  
                  
                    {
                  
                  
                    return
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ;
        
                  
                    }
                  
                  
                    }
                  
                  
                    }
                  
                  
                    
                      class
                    
                  
                   PageWidget extends Widget 
                  
                    {
                  
                  
                    
                      /** * Private * $head XML object for <head /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $head
                  
                  ;

    
                  
                    
                      /** * Private * $body XML object for <body /> */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $body
                  
                  ;

    
                  
                    
                      //! A constructor
                    
                  
                  
                    
                      /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                    
                  
                  
                    
                      function
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    $out
                  
                  =
                  
                    'string'
                  
                  
                    )
                  
                  
                    {
                  
                  
        Widget::
                  
                    Widget
                  
                  
                    (
                  
                  
                    $out
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    createWidget
                  
                  
                    (
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget creation method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   createWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'html'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'head'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    body
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'body'
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Page widget finalization method * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   finalizeWidget 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    head
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    widget
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a title element * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addTitle 
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $title
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'title'
                  
                  
                    )
                  
                  ;
        
                  
                    $text
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_text_node
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $title
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $text
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $title
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Adds a link tag for CSS files * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   cssLink 
                  
                    (
                  
                  
                    $url
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $cssLink
                  
                  =
                  
                    $this
                  
                  ->
                  
                    dom
                  
                  ->
                  
                    create_element
                  
                  
                    (
                  
                  
                    'link'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'href'
                  
                  ,
                  
                    $url
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'type'
                  
                  ,
                  
                    'text/css'
                  
                  
                    )
                  
                  ;
        
                  
                    $cssLink
                  
                  ->
                  
                    set_attribute
                  
                  
                    (
                  
                  
                    'rel'
                  
                  ,
                  
                    'stylesheet'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    head
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $cssLink
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! An accessor
                    
                  
                  
                    
                      /** * Appends a widget to $this->body * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   addBody
                  
                    (
                  
                  
                    $widget
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    body
                  
                  ->
                  
                    append_child
                  
                  
                    (
                  
                  
                    $widget
                  
                  ->
                  
                    fetch
                  
                  
                    (
                  
                  
                    )
                  
                  
                    )
                  
                  ;
    
                  
                    }
                  
                  
                    }
                  
                  
                    
                      // ... etc. etc.
                    
                  
                

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                  
                    
                      <?php
                    
                  
                  
                    
                      /** * Binds log data to HTML content */
                    
                  
                  
                    
                      class
                    
                  
                   LogView 
                  
                    {
                  
                  
                    
                      /** * Private * $model an instance of the LogModel class */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $model
                  
                  ;

    
                  
                    
                      /** * Private * $output contains instance of DOM page object */
                    
                  
                  
                    
                      var
                    
                  
                  
                    $output
                  
                  ;

    
                  
                    
                      //! A constructor.
                    
                  
                  
                    
                      /** * Constucts a new LogView object * @param $model an instance of the LogModel class */
                    
                  
                  
                    
                      function
                    
                  
                   LogView 
                  
                    (
                  
                  &
                  
                    $model
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    model
                  
                  =& 
                  
                    $model
                  
                  ;
    
                  
                    }
                  
                  
                    
                      //! A manipulator
                    
                  
                  
                    
                      /** * Builds the top of an HTML page * @return void */
                    
                  
                  
                    
                      function
                    
                  
                   create 
                  
                    (
                  
                  
                    )
                  
                  
                    {
                  
                  
                    $this
                  
                  ->
                  
                    output
                  
                  =
                  
                    
                      new
                    
                  
                   PageWidget
                  
                    (
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    addTitle
                  
                  
                    (
                  
                  
                    'IP Log Files'
                  
                  
                    )
                  
                  ;
        
                  
                    $this
                  
                  ->
                  
                    output
                  
                  ->
                  
                    cssLink
                  
                  
                    (
                  
                  
                    'css/style.css'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  =
                  
                    
                      new
                    
                  
                   LinkWidget
                  
                    (
                  
                  
                    'object'
                  
                  
                    )
                  
                  ;
        
                  
                    $link
                  
                  ->
                  
                    addLink
                  
                  
                    (
                  
                  
                    

from url: http://www.phppatterns.com/docs/design/data_access_object_pattern_more_widgets?s=dao


Tired of writing the same SQL statements over and over again? The Data Access Object pattern provides a useful way to abstract data fetching operations. In this article we’ll implement a simple Dao, adding a layer of abstraction to further seperate our application logic from the underlying database. We’ll also see more DOM generated widgets in action...

1)

The Need for DAO

In PHP , database abstraction is a fairly well known concept, a number of abstraction libraries such as PEAR::DB and ADOdb , or even PHP ‘s dbx extension providing a partial mechanism to make PHP code database independent.

“Partial” because it’s not just about being able to connect and run queries on anywhere - SQL query syntax also varies from database to database. As we saw when looking at the Adapter Pattern the difference between MySQL and Oracle when fetching a “limited” result set is significant. It’s also awkward to determine the number of rows in a SELECT statement with PHP ‘s Oracle OCI extension, without running a query twice - compare ocirowcount() with mysql_num_rows() . Finally there’s MySQL’s allowing the use of slashes as an escape character in SQL statements (commonly used with PHP ‘s mysql_escape_string() or addslashes() ) which doesn’t conform with ANSI SQL (single quotes being escaped by another single quote).

In other words, to be database independent, we need not only to be able to connect our code to any database but also seperate it from SQL statements. Here’s the first reason for the DAO pattern.

When we looked at the MVC pattern, in the “Model” classes where we placed our “Application Logic” (as opposed to Presentation Logic), we placed SQL statements in those classes. For the simple application we built there, this wasn’t a problem but what happens as our application grows? We start placing more and more SQL statements in our Model classes, no doubt frequently reproducing the same statement in multiple classes. Aside from any resultant finger strain, if we later need to change the underlying database table structure, we’ll need to update all the SQL statements in our code to reflect the change.

The Data Access Object pattern is a strategy for constructing a single API which encapsulates all data fetching operations (such as SQL queries), allowing our Application logic to be a client to that API without needing to be concerned with SQL syntax.

Further more, when we looked at the Adapter Pattern , we created an adapter for our Model classes to allow us to fetch data from multiple sources. Doing so may result in a lot of reproduction of code - we really only wanted to make data fetching abstract from the rest of our code. Implementing a DAO pattern would have made the use of the Adapter pattern, in that example, easier.

DAO Positioning

Referring to our MVC design, the DAO pattern sits between the Model and Data Access classes. In the PHP application coming below, here’s a Dao pattern between the classes that set up the database connection and the “Model” class from the MVC pattern;

DAO in Action

Now we have an idea why we might want to use the Data Access Object pattern and where it sits in an applications architecture, it’s time to lay down some PHP .

Updating the Data Access Class

In earlier pattern examples, we used a class DataAccess to dealing with connecting to MySQL and performing functions like mysql_query(). The first version of this class suited our earlier purposes but now, as we’re implementing a further layer of abstraction in data fetching operations, it needs a little modification;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * A simple class for querying MySQL */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccess 
                        
                          {
                        
                        
                          
                            /** * Private * $db stores a database resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $db
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new DataAccess object * @param $host string hostname for dbserver * @param $user string dbserver user * @param $pass string dbserver user password * @param $db string database name */
                          
                        
                        
                          
                            function
                          
                        
                         DataAccess 
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        ,
                        
                          $db
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        =
                        
                          
                            mysql_pconnect
                          
                        
                        
                          (
                        
                        
                          $host
                        
                        ,
                        
                          $user
                        
                        ,
                        
                          $pass
                        
                        
                          )
                        
                        ;
        
                        
                          
                            mysql_select_db
                          
                        
                        
                          (
                        
                        
                          $db
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Fetches a query resources and stores it in a local member * @param $sql string the database query to run * @return object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & fetch
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            new
                          
                        
                         DataAccessResult
                        
                          (
                        
                        
                          $this
                        
                        ,
                        
                          
                            mysql_query
                          
                        
                        
                          (
                        
                        
                          $sql
                        
                        ,
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns any MySQL errors * @return string a MySQL error */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_error
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          db
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            /** * Fetches MySQL database rows as objects */
                          
                        
                        
                          
                            class
                          
                        
                         DataAccessResult 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;
    
                        
                          
                            /** * Private * $query stores a query resource */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $query
                        
                        ;

    
                        
                          
                            function
                          
                        
                         DataAccessResult
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $query
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =& 
                        
                          $da
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        =
                        
                          $query
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns an array from query row or false if no more rows * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         getRow 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          if
                        
                        
                          (
                        
                        
                          $row
                        
                        =
                        
                          
                            mysql_fetch_array
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        ,MYSQL_ASSOC
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $row
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of rows affected * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         rowCount 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            mysql_num_rows
                          
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          query
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns false if no errors or returns a MySQL error message * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         isError 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $error
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            empty
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        
                          )
                        
                        
                          return
                        
                        
                          $error
                        
                        ;
        
                        
                          else
                        
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

The thing to notice now is we’ve seperated the placing of a query to a MySQL database from the result fetching. DataAccess now returns an instance of the DataAccessResult class, which we can use to fetch rows from the query. 2) The reason for doing this, rather than returning the data itself, is our application only needs deal with a single row at a time, MySQL doing the work of “keeping track” of the result set, rather than having to store the entire result set in a PHP variable. When we perform a “SELECT * FROM table”, holding the entire result set in memory is likely to bring our application to a crashing halt.

The Way of the Dao

Now we need to build the Data Access Object, which will be a client to DataAccess classes.

We’ll implement this by starting with a parent Dao class;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Base class for data access objects */
                          
                        
                        
                          
                            class
                          
                        
                         Dao 
                        
                          {
                        
                        
                          
                            /** * Private * $da stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $da
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the Dao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         Dao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          da
                        
                        =
                        
                          $da
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For SELECT queries * @param $sql the query string * @return mixed either false if error or object DataAccessResult */
                          
                        
                        
                          
                            function
                          
                        
                         & retrieve 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $result
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * For INSERT, UPDATE and DELETE queries * @param $sql the query string * @return boolean true if success */
                          
                        
                        
                          
                            function
                          
                        
                         update 
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $result
                        
                        =
                        
                          $this
                        
                        ->
                        
                          da
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $error
                        
                        =
                        
                          $result
                        
                        ->
                        
                          isError
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            trigger_error
                          
                        
                        
                          (
                        
                        
                          $error
                        
                        
                          )
                        
                        ;
            
                        
                          return
                        
                        
                          
                            false
                          
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          
                            true
                          
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

Notice how simple this is? We provide only two methods, the first for retrieving data which will return the instance for DataAccessResult from the DataAccess class. The second method is used for anything that changes the database, i.e. INSERT, UPDATE and DELETE.

Now let’s say we have a table called “log” described by;

                        CREATE TABLE log (
  id int(11) NOT NULL auto_increment,
  host char(100) NOT NULL default '',
  address char(100) NOT NULL default '',
  agent char(100) NOT NULL default '',
  date datetime default NULL,
  country char(50) NOT NULL default '',
  provider char(100) NOT NULL default '',
  os char(50) NOT NULL default '',
  wb char(50) NOT NULL default '',
  PRIMARY KEY  (id),
  KEY id (id)
) TYPE=MyISAM;

                      

To access this table, we’ll provide a specific Data Access Object which extends the Dao class we’ve already defined.

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Data Access Object for Log Table */
                          
                        
                        
                          
                            class
                          
                        
                         LogDao extends Dao 
                        
                          {
                        
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogDao * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogDao 
                        
                          (
                        
                         & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
        Dao::
                        
                          Dao
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a log files * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchAll 
                        
                          (
                        
                        
                          $start
                        
                        =
                        
                          
                            false
                          
                        
                        ,
                        
                          $rows
                        
                        =
                        
                          
                            false
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log ORDER BY date DESC"
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $start
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        .=
                        
                          " LIMIT "
                        
                        .
                        
                          $start
                        
                        ;
            
                        
                          if
                        
                        
                          (
                        
                        
                          $rows
                        
                        
                          )
                        
                        
                          $sql
                        
                        .=
                        
                          ", "
                        
                        .
                        
                          $rows
                        
                        ;
        
                        
                          }
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by IP address * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByAddress 
                        
                          (
                        
                        
                          $address
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE address='"
                        
                        .
                        
                          $address
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by country * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByCountry 
                        
                          (
                        
                        
                          $country
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE country='"
                        
                        .
                        
                          $country
                        
                        .
                        
                          "'"
                        
                        .
            
                        
                          " ORDER BY date DESC"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches logs by id * @return object a result object */
                          
                        
                        
                          
                            function
                          
                        
                         & searchByID 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT * FROM log WHERE id='"
                        
                        .
                        
                          $id
                        
                        .
                        
                          "'"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            function
                          
                        
                         & totalRows 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $sql
                        
                        =
                        
                          "SELECT count(*) as count FROM log"
                        
                        ;
        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          retrieve
                        
                        
                          (
                        
                        
                          $sql
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

LogDao will now provide the sole point of access to the log table.

We’ll see this in action in a moment, when we write an MVC pattern which will be a client to LogDao.

3)

Designing Daos

Data access objects are an area of application design where it’s easy to go into overkill as a developer. In general it’s best to keep Dao classes as simple as possible.

The easiest approach to adopt is to define an empty Dao class for every table in a database, extending the parent Dao. Then get to work on the application logic classes (the “Models” in MVC) and every time a new query, against some table, is needed, simply add it to the Dao class for that table.

Notice in the LogDao class we have the methods searchByAddress() and searchByCountry() - we could easily combine them into a single method but we don’t for two main reasons.

Firstly say the need for the searchByAddress() method makes itself apparent long before the need for searchByCountry(). If we merge the two methods into one, we’re probably going need to modify LogDao’s API , which may break any client code using it.

Secondly, we make life alot easier for other developers using our Dao, if we use names that make it clear exactly what’s happening.

Although we may end with Dao classes containing many methods (which means more work if we change the table structure), this general “down to earth” will suit applications where the underlying database schema is “small” (i.e. a typical PHP application). For bigger projects, there are further techniques for adding yet more abstraction, using DaoFactories for example, which are well documented for use with Java.

As far as maintaining foreign key relationships between tables goes, it’s generally best to keep this to the SQL statements placed in our Dao classes, the class “owning” the query being the one that contains a foreign key. There’s a whole science of Object Relational Mapping which may or may not be a good thing, depending on who’s talking. For typical PHP apps, this will likely be overkill.

Adding an MVC pattern

Going back the code, we’ll now implement an MVC pattern as a client to our Dao. The code download shows all - we’ll just look at a few classes here.

First a LogModel class which deals with transforming data into something specific to our application;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Modelling log data */
                          
                        
                        
                          
                            class
                          
                        
                         LogModel 
                        
                          {
                        
                        
                          
                            /** * Private * $dao stores data access object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dao
                        
                        ; 

    
                        
                          
                            /** * Private * $result stores result object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $result
                        
                        ;

    
                        
                          
                            /** * Private * $rowCount stores number of rows returned */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $numPages
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constructs the LogModel * @param $da instance of the DataAccess class */
                          
                        
                        
                          
                            function
                          
                        
                         LogModel 
                        
                          (
                        
                         & 
                        
                          $dao
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        =& 
                        
                          $dao
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a paged result set * @param $page the page to view from result set * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLogs 
                        
                          (
                        
                        
                          $page
                        
                        =
                        
                          1
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $rows
                        
                        =
                        
                          20
                        
                        ;       
        
                        
                          $start
                        
                        =
                        
                          $rows
                        
                        *
                        
                          $page
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchAll
                        
                        
                          (
                        
                        
                          $start
                        
                        ,
                        
                          $rows
                        
                        
                          )
                        
                        ;
        
                        
                          $numRowsRes
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          totalRows
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRow
                        
                        =
                        
                          $numRowsRes
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          $numRows
                        
                        =
                        
                          $numRow
                        
                        
                          [
                        
                        
                          'count'
                        
                        
                          ]
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        =
                        
                          
                            floor
                          
                        
                        
                          (
                        
                        
                          $numRows
                        
                        /
                        
                          $rows
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the number of pages in result set * @return int */
                          
                        
                        
                          
                            function
                          
                        
                         getNumPages 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          numPages
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Searches for logs by different conditions * @param $searchBy type of search to perform * @param $searchString string to use in search * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         searchLogs
                        
                          (
                        
                        
                          $searchBy
                        
                        =
                        
                          'address'
                        
                        ,
                        
                          $searchString
                        
                        
                          )
                        
                        
                          {
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $searchBy
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "country"
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByCountry
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByAddress
                        
                        
                          (
                        
                        
                          $searchString
                        
                        
                          )
                        
                        ;
                
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets a single log row by it's id * @param $id of the log row * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         listLog 
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        =& 
                        
                          $this
                        
                        ->
                        
                          dao
                        
                        ->
                        
                          searchByID
                        
                        
                          (
                        
                        
                          $id
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Gets the data from a single row * @return array a single log row */
                          
                        
                        
                          
                            function
                          
                        
                         getLog
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          result
                        
                        ->
                        
                          getRow
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            ?>
                          
                        
                      

It’s worth reminding ourselves that (something this example won’t fully demonstrate) seperating data fetching operations from Model type classes allows us to construct multiple models all using a single Dao. This becomes clear in more complex applications.

Look looking at a section of the Controller code we have;

                        
                          
                            <?php
                          
                        
                        
                          
                            /** * Controls the application */
                          
                        
                        
                          
                            class
                          
                        
                         LogController 
                        
                          {
                        
                        
                          
                            /** * Private * $model an instance of LogModel */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $model
                        
                        ;

    
                        
                          
                            /** * Private * $view an instance of LogView */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $view
                        
                        ;

    
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $logDao
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogDao
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogModel
                        
                          (
                        
                        
                          $logDao
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns the view bound with data * @return string */
                          
                        
                        
                          
                            function
                          
                        
                         & getView 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            class
                          
                        
                         LogTableController extends LogController 
                        
                          {
                        
                        
                          
                            //! A constructor.
                          
                        
                        
                          
                            /** * Constucts a new LogTableController object * @param $model an instance of the LogModel class * @param $getvars the incoming HTTP GET method variables */
                          
                        
                        
                          
                            function
                          
                        
                         LogTableController 
                        
                          (
                        
                        & 
                        
                          $da
                        
                        ,
                        
                          $getvars
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
        LogController::
                        
                          LogController
                        
                        
                          (
                        
                        
                          $da
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                         !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        
                          )
                        
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        =
                        
                          1
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          view
                        
                        =& 
                        
                          
                            new
                          
                        
                         LogTableView
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          model
                        
                        ,
                        
                          $getvars
                        
                        
                          [
                        
                        
                          'page'
                        
                        
                          ]
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ...
                          
                        
                        
                          
                            ?>
                          
                        
                      

Widgets in Action

We’ve seen “widgets” when looking at DOM and also with the Singleton Pattern . This time we’ll use widgets in our views.

To start off here’s a section of the widget library;

                        
                          
                            <?php
                          
                        
                        
                          
                            /* DOM Widget Library Sample usage $page=new PageWidget(); $page->addTitle('Test Page'); $page->cssLink('/style.css'); $heading=new HeadingWidget('object',3); $heading->addText('Log Files'); $heading->addAttribute('align','center'); $page->addBody($heading); $link=new LinkWidget('object'); $link->addLink('http://www.php.net'); $link->addText('Powered by PHP'); $page->addBody($link); $table=new TableWidget('object'); $row=new RowWidget('object'); $cell=new CellWidget('object'); $cell->addText('A test cell'); $row->addCell($cell); $table->addRow($row); $page->addBody($table); echo ( $page->fetch()); */
                          
                        
                        
                          
                            // A function to create static instances of the DOM API
                          
                        
                        
                          
                            function
                          
                        
                         staticDom
                        
                          (
                        
                        
                          $type
                        
                        =
                        
                          
                            null
                          
                        
                        ,
                        
                          $source
                        
                        =
                        
                          
                            null
                          
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Declare a static variable to hold the dom object
                          
                        
                        
                          
                            static
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            // If the instance is not there, create one
                          
                        
                        
                          if
                        
                        
                          (
                        
                        !
                        
                          
                            isset
                          
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Deal with the possible ways DOM can be constructed
                          
                        
                        
                          switch
                        
                        
                          (
                        
                        
                          $type
                        
                        
                          )
                        
                        
                          {
                        
                        
                          case
                        
                        
                          "file"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_file
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $source: path to file
                          
                        
                        
                          break
                        
                        ;
            
                        
                          case
                        
                        
                          "mem"
                        
                        :
                
                        
                          $dom
                        
                        =domxml_open_mem
                        
                          (
                        
                        
                          $source
                        
                        
                          )
                        
                        ; 
                        
                          
                            // $sounce: XML as string
                          
                        
                        
                          break
                        
                        ;
            
                        
                          
                            default
                          
                        
                        :
                
                        
                          $dom
                        
                        =domxml_new_doc
                        
                          (
                        
                        
                          '1.0'
                        
                        
                          )
                        
                        ; 
                        
                          
                            // create a new one
                          
                        
                        
                          break
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          return
                        
                        
                          (
                        
                        
                          $dom
                        
                        
                          )
                        
                        ;

                        
                          }
                        
                        
                          
                            /** * Base Widget class */
                          
                        
                        
                          
                            class
                          
                        
                         Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $dom an instance of the DOM API */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $dom
                        
                        ;

    
                        
                          
                            /** * Private * $out whether to return a DOM object or an XML string */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $out
                        
                        ;

    
                        
                          
                            /** * Private * $widget stores a widget object */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $widget
                        
                        ;

    
                        
                          
                            //! A Constructor
                          
                        
                        
                          
                            /** * Constucts an abstract Widget object * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         Widget 
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        =& staticDom
                        
                          (
                        
                        
                          )
                        
                        ; 
                        
                          
                            // Construct DOM from static instance
                          
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Abstract widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          
                            // Empty
                          
                        
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Change the current value of $this->out * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         setOut 
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        =
                        
                          $out
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic widget to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addWidget
                        
                          (
                        
                        
                          $newWidget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $newWidget
                        
                        ->
                        
                          setOut
                        
                        
                          (
                        
                        
                          'object'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $newWidget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a generic attibute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addAttribute
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          $name
                        
                        ,
                        
                          $value
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Places text in the widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addText
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a class="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addClass
                        
                          (
                        
                        
                          $class
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'class'
                        
                        ,
                        
                          $class
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a style="" attribute to the current widget * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addStyle
                        
                          (
                        
                        
                          $style
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'style'
                        
                        ,
                        
                          $style
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Returns either XML as a string or a DOM object * @return mixed */
                          
                        
                        
                          
                            function
                          
                        
                         &fetch 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          finalizeWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
        
                        
                          if
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          out
                        
                        ==
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          dump_node
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        
                          )
                        
                        ;
        
                        
                          }
                        
                        
                          else
                        
                        
                          {
                        
                        
                          return
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ;
        
                        
                          }
                        
                        
                          }
                        
                        
                          }
                        
                        
                          
                            class
                          
                        
                         PageWidget extends Widget 
                        
                          {
                        
                        
                          
                            /** * Private * $head XML object for <head /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $head
                        
                        ;

    
                        
                          
                            /** * Private * $body XML object for <body /> */
                          
                        
                        
                          
                            var
                          
                        
                        
                          $body
                        
                        ;

    
                        
                          
                            //! A constructor
                          
                        
                        
                          
                            /** * Constucts a new PageWidget object building head and body * @param $out switch between XML as string or DOM object */
                          
                        
                        
                          
                            function
                          
                        
                         PageWidget
                        
                          (
                        
                        
                          $out
                        
                        =
                        
                          'string'
                        
                        
                          )
                        
                        
                          {
                        
                        
        Widget::
                        
                          Widget
                        
                        
                          (
                        
                        
                          $out
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          createWidget
                        
                        
                          (
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget creation method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         createWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'html'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'head'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'body'
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! A manipulator
                          
                        
                        
                          
                            /** * Page widget finalization method * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         finalizeWidget 
                        
                          (
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          widget
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a title element * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addTitle 
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $title
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'title'
                        
                        
                          )
                        
                        ;
        
                        
                          $text
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_text_node
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $title
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $text
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $title
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Adds a link tag for CSS files * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         cssLink 
                        
                          (
                        
                        
                          $url
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $cssLink
                        
                        =
                        
                          $this
                        
                        ->
                        
                          dom
                        
                        ->
                        
                          create_element
                        
                        
                          (
                        
                        
                          'link'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'href'
                        
                        ,
                        
                          $url
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'type'
                        
                        ,
                        
                          'text/css'
                        
                        
                          )
                        
                        ;
        
                        
                          $cssLink
                        
                        ->
                        
                          set_attribute
                        
                        
                          (
                        
                        
                          'rel'
                        
                        ,
                        
                          'stylesheet'
                        
                        
                          )
                        
                        ;
        
                        
                          $this
                        
                        ->
                        
                          head
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $cssLink
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          
                            //! An accessor
                          
                        
                        
                          
                            /** * Appends a widget to $this->body * @return void */
                          
                        
                        
                          
                            function
                          
                        
                         addBody
                        
                          (
                        
                        
                          $widget
                        
                        
                          )
                        
                        
                          {
                        
                        
                          $this
                        
                        ->
                        
                          body
                        
                        ->
                        
                          append_child
                        
                        
                          (
                        
                        
                          $widget
                        
                        ->
                        
                          fetch
                        
                        
                          (
                        
                        
                          )
                        
                        
                          )
                        
                        ;
    
                        
                          }
                        
                        
                          }
                        
                        
                          
                            // ... etc. etc.
                          
                        
                      

There’s a whole lot more than that, some of which may be regarded as unnecessary, such as a widget class for creating heading tags; &lt;h* /&gt;. These classes are wrapping the DOM extension (which needs to be installed to use this code by the way) which already has all we need for creating (X) HTML tags, creating a class for every possible tag is unnecessary.

The idea is simply to provide a simplified API which reduces the amount of code we need, makes it easier to understand in terms of HTML rendering and encourages re-use of widgets.

Here’s a section of the LogView classes using widgets;

                        ___FCKpd___7
                      

Of particular interest is this line;

                                $table->addRow(LogTableWidget::getPager($this->page,$numPages));

                      

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                        ___FCKpd___9
                      

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] ) ; $link -> addText ( 'Start Over' ) ; $this -> output -> addBody ( $link ) ; } //! An manipulator /** * Abstract method with completes the page * @return void */ function finalize ( ) { // Empty } //! An accessor /** * Returns the page * @return void */ function display ( ) { $this -> finalize ( ) ; return $this -> output -> fetch ( ) ; } } // ... class LogTableView extends LogView { /** * Private * $page the page we're viewing */ var $page ; //! A constructor. /** * Constucts a new LogView object * @param $model an instance of the LogModel class */ function LogTableView ( & $model , $page = 1 ) { LogView:: LogView ( $model ) ; $this -> page = $page ; $this -> create ( ) ; } //! A manipulator /** * Renders a log table * @return void */ function logTable ( ) { $this -> model -> listLogs ( $this -> page ) ; $heading = new HeadingWidget ( 'object' , 2 ) ; $heading -> addText ( 'Paged Log Result Set' ) ; $heading -> addAttribute ( 'align' , 'center' ) ; $this -> output -> addBody ( $heading ) ; // Build result table $table = new TableWidget ( 'object' ) ; $table -> addAttribute ( 'align' , 'center' ) ; $table -> addAttribute ( 'width' , '750' ) ; // Build result pager $numPages = $this -> model -> getNumPages ( ) ; $table -> addRow ( LogTableWidget:: getPager ( $this -> page , $numPages ) ) ; // Build table rows $table -> addRow ( LogTableWidget:: getHeader ( ) ) ; while ( $log = $this -> model -> getLog ( ) ) { if ( $alt == '#f6f7f8' ) $alt = '#ffffff' ; else $alt = '#f6f7f8' ; $table -> addRow ( LogTableWidget:: getRow ( $log , $alt ) ) ; } $this -> output -> addBody ( $table ) ; } //! An manipulator /** * Runs the logItem method * @return void */ function finalize ( ) { $this -> logTable ( ) ; } } // ... ?>

Of particular interest is this line;

                  ___FCKpd___8
                

Here the code for rendering the “Google-like” result pager is farmed off to another class;

                  ___FCKpd___9
                

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] . '?page=' . $numPages ) ; $link -> addText ( '>>' ) ; $cell -> addWidget ( $link ) ; } $row -> addCell ( $cell ) ; return $row ; } // ... ?>

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used --> SERVER
[ 'PHP_SELF' ] ) ; $link -> addText ( 'Start Over' ) ; $this -> output -> addBody ( $link ) ; } //! An manipulator /** * Abstract method with completes the page * @return void */ function finalize ( ) { // Empty } //! An accessor /** * Returns the page * @return void */ function display ( ) { $this -> finalize ( ) ; return $this -> output -> fetch ( ) ; } } // ... class LogTableView extends LogView { /** * Private * $page the page we're viewing */ var $page ; //! A constructor. /** * Constucts a new LogView object * @param $model an instance of the LogModel class */ function LogTableView ( & $model , $page = 1 ) { LogView:: LogView ( $model ) ; $this -> page = $page ; $this -> create ( ) ; } //! A manipulator /** * Renders a log table * @return void */ function logTable ( ) { $this -> model -> listLogs ( $this -> page ) ; $heading = new HeadingWidget ( 'object' , 2 ) ; $heading -> addText ( 'Paged Log Result Set' ) ; $heading -> addAttribute ( 'align' , 'center' ) ; $this -> output -> addBody ( $heading ) ; // Build result table $table = new TableWidget ( 'object' ) ; $table -> addAttribute ( 'align' , 'center' ) ; $table -> addAttribute ( 'width' , '750' ) ; // Build result pager $numPages = $this -> model -> getNumPages ( ) ; $table -> addRow ( LogTableWidget:: getPager ( $this -> page , $numPages ) ) ; // Build table rows $table -> addRow ( LogTableWidget:: getHeader ( ) ) ; while ( $log = $this -> model -> getLog ( ) ) { if ( $alt == '#f6f7f8' ) $alt = '#ffffff' ; else $alt = '#f6f7f8' ; $table -> addRow ( LogTableWidget:: getRow ( $log , $alt ) ) ; } $this -> output -> addBody ( $table ) ; } //! An manipulator /** * Runs the logItem method * @return void */ function finalize ( ) { $this -> logTable ( ) ; } } // ... ?>

Of particular interest is this line;

      ___FCKpd___8
    

Here the code for rendering the “Google-like” result pager is farmed off to another class;

      ___FCKpd___9
    

Re-using the above “paging” widget in other views is now easy.

Control of appearance is all left to an external CSS file in this example. If we want to allow a designer to control layout of pages we need some kind of simple template system, not unlike ASP .NET, were we plant tags that “bind” to a widget (control) class.

One further side effect of our widgets (which is not unique to widgets of course) is by delivering XML compliant HTML , we’re able to use XSL to transform it to other content types, such as WML .

The rest is up to the code to explain...

Further Reading

PHP Architect Jan 2003 Edition - has an excellent article on Database Persistence in PHP . Codewalkers: A Framework for Persisting Data Relationships - inspired by the PHP Architect article, this tutorial puts some of the ideas into practice. PHPEverywhere: Object Related Mania - the counter view on Object Relational Database Mapping. Write once, persist anywhere - Implement a Data Access Object pattern framework (Java) O/R, JDO, and Database engineering tools - collection of links on Persistance Layers and Object Relational Mapping (Java) The Goag Dao Pattern useful for project management... ;)

2) For an PHP class library providing database abstraction of this kind (and generally as a excellent design example) try Eclipse
3) For PHP class library which encourages use of Data Access Objects, try eXtremePHP - documentation includes a tutorial of building Daos
<!-- cachefile /home/patterns/dw_data/cache/0/0ca855fe420f7c964a3ca4b40e988554.xhtml used -->

[介绍]PHP设计模式:DAO(数据访问对象模式)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论