Construction of Domain Objects

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
The_Anomaly
Forum Contributor
Posts: 196
Joined: Fri Aug 08, 2008 4:56 pm
Location: Tirana, Albania

Construction of Domain Objects

Post by The_Anomaly »

Quick question here, that has been haunting me for a while. I referenced it in an older thread of mine, but I didn't get back to the thread due to an insane amount of work that came up. Basically, it has to do with Domain Objects. Say I have a domain object that is a medical Note about a patient visit. This Note can be persisted to the database from data from a form, and retrieved from the database through a Data Mapper. Say it has five properties, i.e. Name, age, problem, etc.

My issue is that I'd like to have one domain object Note, but it comes from two places. So, in the constructor, sometimes I'll be passing a Request object, and sometimes I'll be passing an array from a database query in the Mapper. So, in the former, the controller is filled with:

Code: Select all

 
$this->name = $argument->getPost('name');
$this->age   = $argument->getPost('age')
And in the latter case:

Code: Select all

 
$this->name  = $argument['name'];
$this->age  = $argument['age'];
 
So, I have a conditional that sees whether the passed argument is a Request object or an array, and then use one of the above blocks of code to assign the variables. This, clearly, is messed up.

Basically, how do you assign values to your properties when the domain object which contains them is instantiated by different sources (i.e. db vs. request)?
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Construction of Domain Objects

Post by josh »

I'm not following you, but what I think you mean is 2 different schema sources? I'd make 2 mapper sub-classes, one for each source of data.
User avatar
The_Anomaly
Forum Contributor
Posts: 196
Joined: Fri Aug 08, 2008 4:56 pm
Location: Tirana, Albania

Re: Construction of Domain Objects

Post by The_Anomaly »

Code: Select all

 
class model_domain_Note{
        //Defining protected properties
 
    protected $id;
    protected $soapID;
    protected $med;
    protected $hpi;
    protected $ppsmh;
    protected $allergy;
    protected $physical;
    protected $lab;
    protected $timeValue;
    protected $plan;
   
function __construct ( $array ) {
        if ( is_array( $array ) ) {
 
            foreach($array as $property=>$value) {
                if($property) {
                    $this->$property    = $value;
                }
            }
 
        }elseif( $array instanceOf Zend_Controller_Request_Http ) {
            //Array is really the request object
            $this->ppsmh        = $array->getValidPost( 'ppsmh');
            $this->hpi          = $array->getValidPost( 'hpi');
            $this->med          = $array->getValidPost( 'med' );
            $this->allergy      = $array->getValidPost( 'allergy' );
            $this->physical     = $array->getValidPost( 'physical' );
            $this->lab          = $array->getValidPost( 'lab' );
            $this->timeValue    = time();
            $this->plan         = $array->getValidPost( 'plan' );
            $this->id           = $array->getQuery( 'id' );
 
 
        }
 
 
    }
 
}
 
Basically, I pass an array/object to the domain object when I instantiate it. In the second case, $array is a Request object, and in the first one $array is an array from a database query. Basically, is there a better way to populate domain objects that have different "Origins?"
User avatar
allspiritseve
DevNet Resident
Posts: 1174
Joined: Thu Mar 06, 2008 8:23 am
Location: Ann Arbor, MI (USA)

Re: Construction of Domain Objects

Post by allspiritseve »

The_Anomaly wrote:Basically, I pass an array/object to the domain object when I instantiate it. In the second case, $array is a Request object, and in the first one $array is an array from a database query. Basically, is there a better way to populate domain objects that have different "Origins?"
My suggestion, since you don't seem to be able to just pass the request as an array, is to build an array from the request object before instantiating the Note, and then passing the array in. That way, all the Note knows is to pull specific keys from an array of data, and has no knowledge of the request (which doesn't belong in the model anyways). That also eliminates the need for your conditional.
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Construction of Domain Objects

Post by inghamn »

I use the constructor to actually load data, if any, from the database. Populating each of the properties from something posted from a form is the job of the controller. But we can make the controller's job easier by using good form names; names that match the class property names.

Assuming the property names of your class match the field names in the database, your mode's constructor could look like:

Code: Select all

 
/**
 * This will load all fields in the table as properties of this class.
 * You may want to replace this with, or add your own extra, custom loading
 */
public function __construct($id=null)
{
    if ($id)
    {
        $PDO = Database::getConnection();
        $sql = "select * from notes where id=?";
 
        $query = $PDO->prepare($sql);
        $query->execute(array($id));
 
        $result = $query->fetchAll(PDO::FETCH_ASSOC);
        if (!count($result)) { throw new Exception('notes/unknownNote'); }
        foreach($result[0] as $field=>$value) { if ($value) $this->$field = $value; }
    }
    else
    {
        # This is where the code goes to generate a new, empty instance.
        # Set any default values for properties that need it here
    }
}
 
Then, in your controller, assuming all the form fields are named like name="Note[med]", you can loop over all of them at set the appopriate property:

Code: Select all

 
if (isset($_POST['Note']))
{
    $note = new Note($_POST['note_id']);
    foreach($_POST['Note'] as $field=>$value)
    {
        $note->$field = $value;
    }
}
 
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Re: Construction of Domain Objects

Post by John Cartwright »

It seems what you are leaning towards is that your data sources should conform to a standard data container interface. This will also allow you to seperate your domain models actual properties from the data you are passing (and avoid injecting properties).
User avatar
inghamn
Forum Contributor
Posts: 174
Joined: Mon Apr 16, 2007 10:33 am
Location: Bloomington, IN, USA

Re: Construction of Domain Objects

Post by inghamn »

I typically don't set properties directly, but rather use setters in my models. That gives me a single place to do any input filtering.
$note->setMed('something or other');
So my controllers look more like:

Code: Select all

 
if (isset($_POST['Note']))
{
    $note = new Note($_POST['note_id']);
    foreach($_POST['Note'] as $field=>$value)
    {
        $set = 'set'.ucfirst($field);
        $note->$set($value);
    }
}
 
 
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Construction of Domain Objects

Post by josh »

If data is immutable ( cannot change once it is set, like the id ), set it in the constructor by having 1 parameter per immutable property, not an array. Use setters for all your other properties not an array. This is called an explicit not an implicit interface, meaning the model clearly expresses what it can do. If you're going to pass by array the model should at least:
1. throw exceptions if invalid properties are set, it will save you lots of debugging later
2. have an easy method of finding the "legal" properties, perhaps an array declared at the top of the script, the 1st exception throwing mechanism can use this array.
User avatar
The_Anomaly
Forum Contributor
Posts: 196
Joined: Fri Aug 08, 2008 4:56 pm
Location: Tirana, Albania

Re: Construction of Domain Objects

Post by The_Anomaly »

inghamn wrote:I typically don't set properties directly, but rather use setters in my models. That gives me a single place to do any input filtering.
$note->setMed('something or other');
So my controllers look more like:

Code: Select all

 
if (isset($_POST['Note']))
{
    $note = new Note($_POST['note_id']);
    foreach($_POST['Note'] as $field=>$value)
    {
        $set = 'set'.ucfirst($field);
        $note->$set($value);
    }
}
 
 
Ah, I like this. Sweet.
If data is immutable ( cannot change once it is set, like the id ), set it in the constructor by having 1 parameter per immutable property, not an array. Use setters for all your other properties not an array. This is called an explicit not an implicit interface, meaning the model clearly expresses what it can do. If you're going to pass by array the model should at least:
1. throw exceptions if invalid properties are set, it will save you lots of debugging later
2. have an easy method of finding the "legal" properties, perhaps an array declared at the top of the script, the 1st exception throwing mechanism can use this array.
Yeah, I've been using getters and setters, but not when I construct the object. However, using what inghamm is talking about, it would make this a lot easier. Just gotta add some exception handling if I screw up the names. Thanks for the response, and it's good to know the proper terminology.
It seems what you are leaning towards is that your data sources should conform to a standard data container interface. This will also allow you to seperate your domain models actual properties from the data you are passing (and avoid injecting properties).
Well, they did. For some reason I just didn't think of using that when constructing the objects.
My suggestion, since you don't seem to be able to just pass the request as an array, is to build an array from the request object before instantiating the Note, and then passing the array in. That way, all the Note knows is to pull specific keys from an array of data, and has no knowledge of the request (which doesn't belong in the model anyways). That also eliminates the need for your conditional.
This is a pretty good thought. Not sure why that never ocurred to me. It would certainly mean less code duplication than what I was doing before.

Thanks for the responses everyone. I feel like I have a better grasp now on how to populate these domain objects.
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Construction of Domain Objects

Post by josh »

My thing is just, we're supposed to refactor our code to replace arrays with objects, for enumerated "sets" of data, so if you pass an array to an object in theory it would be conceptually cleaner to pass an object that acts as that array, factor away the redundant object and you arrive at the conclusion you should not rely on passing an array to objects, and instead you should use the setter pattern just identified, using the reflective syntax of the language. This is just my conclusions / thoughts after philosophizing about this. OOP is about combining state & behavior into one "concept". the array kind of defeats that. The array syntax is tempting but it doesn't do anything you can't do with the setter pattern, conceptually the array is more limiting. Also in the "off chance" you want to replace the array with an object later, you're better off creating it as an object in the first place. Just my attempt at deductive reasoning. Hope this makes sense
User avatar
allspiritseve
DevNet Resident
Posts: 1174
Joined: Thu Mar 06, 2008 8:23 am
Location: Ann Arbor, MI (USA)

Re: Construction of Domain Objects

Post by allspiritseve »

jshpro2 wrote:My thing is just, we're supposed to refactor our code to replace arrays with objects, for enumerated "sets" of data, so if you pass an array to an object in theory it would be conceptually cleaner to pass an object that acts as that array, factor away the redundant object and you arrive at the conclusion you should not rely on passing an array to objects, and instead you should use the setter pattern just identified, using the reflective syntax of the language.
Ideally, yes, that's how things should work, but considering $_POST data is already in an array, it makes rapid development possible to just accept an array in the constructor. If there's not much business logic, a getter and setter for each property is overkill anyways.
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Construction of Domain Objects

Post by josh »

Why use objects at all then :twisted: ( this is what overloading is for - lazy people :D )
Post Reply