We recently started doing quite a few multi-page forms that store data in a database in a way that can be extracted to some form (PDF, plaintext, etc). Anyway, we got quite a few large ones coming up, and I've been thinking of a way to refactor a bit and come up with a reusable solution.

I've been thinking a bit of implementing a Unit of Work pattern to kind of hold on to several table row gateway objects, but a bit baffled how this should work. Essentially, I would like a collection of objects that do your normal update, insert, and delete, and have finder methods that, for example, know how to find a user by id, or by something like first name, last name, etc.

See, I plan on storing each value submitted on the next page as a hidden input, and then drop everything down into the database when the last page is submitted. what I would like to do is pass all the variables to the unit of work to sort out. Perhaps the class could have some kind of linked list of table data gateways in the order their needed, has some kind of registry to determine what actions need to take place on each insert, and what needs to be gained (i.e. an id used for a foreign key mapping in a latter entry), etc.

Anyway, anyone else implemented something like this? I'm using ADODB for the database backend as I tend to have it work with a multitude of databases, below is an example of a table data gateway I created for PersonalInfo:

PHP Code:
class PersonalInfoGateway
        
{
        function 
findByUID($uid)
                {
                
$GLOBALS['adodb']->SetFetchMode(ADODB_FETCH_ASSOC);
                
$sql "SELECT appid, first_name, last_name, mi, 
                        street1, street2, city, state, zip,ssn, 
                        home_phone,other_phone, email, auth_us 
                        FROM app_personal_info
                        WHERE uid = 
$uid";

                
$rs =& $GLOBALS['adodb']->Execute($sql);

                
/* query failed */
                
if(!$rs)
                  print 
$db->ErrorMsg();

                return 
$rs->fields;
                }

        function 
insert($person$appid)
                {
                if(!isset(
$appid))
                        die(
'No user id set!');
                
                
$sql "SELECT * FROM app_personal_info WHERE uid = -1";
                
$rs =& $GLOBALS['adodb']->Execute($sql);
                
$insertSQL =
                        
$GLOBALS['adodb']->GetInsertSQL($rs,$person);

                
$GLOBALS['adodb']->Execute($insertSQL);

                
/* return the userid? */
                
}

        function 
update($uid)
                {
                if(!isset(
$uid)
                        die(
'No user id set!');

                
$sql "SELECT * FROM app_personal_info WHERE uid = -1";
                
$rs =& $GLOBALS['adodb']->Execute($sql);
                
$updateSQL $GLOBALS['adodb']->GetUpdateSQL($rs,
                                                             
$person);
                
$conn->Execute($updateSQL);
                }
        function 
delete($uid)
                {
                
$sql "DELETE FROM app_personal_info WHERE uid = $uid";
                
$GLOBALS['adodb']->Execute($sql);
                }
        } 
I'm basically looking for a way to abstract things and chop development time for future multi page forms down a bit. I mean, something like a form that just gets the info mailed is easy enough, but when storing the data in a database, things get a bit more complex as you have to follow all the rules and domain logic as well, and as we all know business logic changes often.

I guess I just want some all in one solution... I've had alot of forms and while some data is different, alot is similar or the same (address, contact info, personal info, business info) and I'd like to find a way to reuse. I've created a few primitive data objects that I suppose I'll share as well:

Partent
PHP Code:
class Primitive 
        
{       
        function 
Primitive()
                {
                die(
'Is not called directly.');
                }
        
        function 
setData($data)
                {
                if(
is_array($data))
                  foreach(
$data as $key=>$value)
                    {
                     if((!
is_null($value)) &&
                        (
array_key_exists($key$this->_attrMap)))
                            {            
                            if(
strstr('object'$this->_attrMap[$key]))
                               {
                                
$object explode('.'$this->_attrMap[$key]);
                                
$method 'set'.$object[1];
                                
$this->$method($value);
                                }
                            else
                                
$this->$key $value;
                            }
                      }
                  }
        
        function 
getData()
                {
                
$data = array();

                foreach(
$this->_attrMap as $key => $value)
                        {
                        if(!
is_null($this->$key))
                                {
                                if(
strstr($value'object'))
                                        {
                                        foreach(
$this->$key->getData() as $k => $v)
                                                
$data[$k] = $v;
                                        }
                                else
                                        
$data[$key] =& $this->$key;
                                }
                        }
                   
                return 
$data;
                }

        } 
Subclasses:

PHP Code:
class Person extends Primitive
        
{
        var 
$first_name NULL;
        var 
$last_name  NULL;
        var 
$mi         NULL;
        var 
$ssn        NULL;
        var 
$home_phone NULL;
        var 
$work_phone NULL;
        var 
$email      NULL;

        
/* address object, not set directly */
        
var $address    NULL;

        
/* attribute map */
        
var $_attrMap = array('first_name' => 'string',
                                                
'last_name'  => 'string',
                                                
'mi'         => 'string',
                                                
'ssn'        => 'string',
                                                
'address'    => 'object.Address',
                                                
'home_phone' => 'string',
                                                
'work_phone' => 'string',
                                                
'email'      => 'string');

        function 
Person($person NULL)
                {
                
$this->setData($person);
                }

        function 
setAddress($address)
                {
                if(
is_object($address))
                        
$this->address $address;
                else
                        die(
'setAddress() expects object of type Address.');

                }
        } 
PHP Code:
class Address extends Primitive
        
{
        var 
$street1 NULL;
        var 
$street2 NULL;
        var 
$city    NULL;
        var 
$state   NULL;
        var 
$zip     NULL;
        var 
$country NULL;

        var 
$_attrMap = array('street1' => 'string',
                                                 
'street2' => 'string',
                                                 
'city'    => 'string',
                                                 
'state'   => 'string',
                                                 
'zip'     => 'string',
                                                 
'country'  => 'string');

        function 
Address($address NULL)
                {
                
$this->setData($address);
                }

        } 
PHP Code:
class Business extends Primitive
        
{
        var 
$company_name                  NULL;
        var 
$owner                 NULL;
        var 
$phone                 NULL;
        var 
$fax                   NULL;

        
/* address object */
        
var $address NULL;

        var 
$_attrMap = array('company_name'  => 'string',
                                                  
'owner' =>  'string',
                                                  
'phone' => 'string',
                                                  
'fax'   => 'string',
                                                  
'address' => 'object.Address');

        function 
Business($business=NULL)
                {
                
$this->address =& new Address;
                
$this->setData($business);
                }

        function 
setAddress($address)
                {
                if(
is_object($address))
                        
$this->address address;
                else
                        die(
'Method setAddress expects type Address.');
                }

        } 
I think you get the general idea. I'll probably have several private methods that do some data validation when setting data. Anyhow... suggestions please, thanks!