Page 1 of 1

Keep relation between objects. And still be standalone

Posted: Tue Jul 18, 2006 4:39 am
by jmut
Hi,
I am looking for a design pattern to gracefully build relation between objects. one->many and one-one.
but make the objects standalone as well.
For example I have
Customer object....this customer object can containt many Contracts objects. One contract object can have one Product object because contract is made on per product bases...if you want to buy more products..you make more contracts. (thats just business rulz)

So right now..what I am doing is I search for any creterea for example contract_id and I find to which customer_id it belongs.
Then I build customer object....build contract objects within it in private $contracts = array() property.... and so on ...

I end up having one/many customer objects with contract objects within...wich in their turn magically contains one-to-one relation to product objects.


So my question is how is the right approach to build this relation and still be able to take contracts as separate thing for example. Because as separat entities they make perfect sense.
I was thinking maybe on creation of an object (using factory or whatever) I add the relative object in a container for contact, contracts etc. But then I kind of not sure how to make for example : relation between given contract and to which contact it belongs.

To much talk...hopefully this all makes some sense. :)

Posted: Tue Jul 18, 2006 5:25 am
by Jenk
I'm not overly great with terminology .. (if that isn't a perfect example of how much I suck at it..)

but I think what you are asking, is for relationships between customers and contracts, but to also maintain the ability for no relationship at all?

such as (key: '--' one:one, '-<' one:many and vice versa)

Customer -< Contract (but may not have any contracts?)
Product >- Contract (I'm assuming the same product can be on many contracts?)

If that's the case:

Table Customer:

Customer ID
Name
Address
etc

Table Contracts:

Contract ID
Product ID
Contract Description
etc

Table Products:

Product ID
Product Description
etc

So to find a customers products, the SQL:

Code: Select all

SELECT `products`.`productDescription` 
    FROM `customers` 
        (INNER JOIN `contracts` 
            (INNER JOIN `products` 
            ON `contracts`.`productId` = `products`.`productId`) 
        ON `customers`.`contractId` = `contracts`.`contractId`) 
    WHERE `customers`.`customerId` = '<customerid>';
[nb: completely off top of my head.. untested]

In PHP.. it can be done many ways, hence my (probable incorrect) assumption you are referring to database relationships?

Posted: Tue Jul 18, 2006 6:10 am
by jmut
I am referring to object relations :)
Did not mention DB anywhere.

Yes actually products can be used in many contracts.... but let assume it is something that has actually 1:1 relation.


So I mean.

Code: Select all

//wrote it on the fly...the importance is the idea...not if this exact will run with no errors 

$oCustomer = new Customer($customer_id);
foreach ($oCustomer->getAllContracts() as $contract_id) {
  $oCustomer->addContract(new Contract ($contract_id));
}


//....within addContract()
public function addContract(Contract $oContract) {
     $this->contracts[$oContract->getId()] = $oContract;
}
Now as we see here we have contracts as a part from a customer but this is not good. As contracts are standalone as well(actually not...since they need to be attached to some customer...but they should be able to be looked for as distinct entity). I mean if I wanted to search for a certain contract (not to mention some charachteristic of them) I have to go through all $oCustomers ....get all $oContracts within them and find them like that.
This just looks wrong.
Maybe if someone lead me to a pattern from "Patterns of Enterprise Application Architecture" or something. I got the book right next to me.
I will check on "Identity Map", "Plugin" now.

Posted: Tue Jul 18, 2006 6:30 am
by jmut
I guess what I lack is a back reference.
So for example having a $oContract to be able to say to which $oCustomer it belongs and have for example.

Code: Select all

public function getCustomerReference() {
  return $this->oCustomer;
}

$oCustomer = $oContract->getCustomerReference();
This will allow me..on oContract creation or whatever to add a contract to a $contractContainer as well as to $oCustomer

Posted: Tue Jul 18, 2006 6:48 am
by Jenk
You seem to be misusing the word standalone.. none of those classes are standalone, they all depend on the other classes.

Anyway..

some simple if ()'s will make your classes polymorphic, and able to distinguish if your Customer object has a contract, and if what product the contract is for..

Code: Select all

class Customer
{
    private $contract;

    public function __construct ()
    {
        if (func_num_args() == 1) {

            if (get_class(func_get_arg(0)) != 'Contract') throw new Exception ('erf.');

            $this->contract = func_get_arg(0);
        } elseif (func_num_args() > 1) {
            $this->contract = array();

            foreach (func_get_args() as $arg) {

                if (get_class($arg) != 'Contract') throw new Exception ('erf.');

                $this->contract[$arg->getContractID()] = $arg;
            }
        }
    }
}
this does unfortunately hinder the ability to use interfaces..

EDIT: You can use the instanceof comparison operator..

http://uk2.php.net/manual/en/language.o ... s.type.php

also when retrieving contracts, use a method to check if customer has multiples etc. etc.


EDIT2: scrap the above, although it is a potential solution.

your Contracts class will/can be instantiated independantly of the Customer class/object by either your controller, or other superior logic, instead of within the Customer object.
in pseudo-ish:

Code: Select all

/*
* queried DB for contracts assumed up here with 
* "SELECT * FROM `contracts` WHERE `custid` = <customerid>"
*/

$cust = new Customer;

if (mysql_num_rows($result) > 0) {
    while ($row = mysql_fetch_assoc($result)) {
        $cust->addContract(new Contract($row['contractId'], $row['contractDetails'], $row['etc']));
    }
}

//rest of script.