Using a unit of work with table data gateways for form processing
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!