How should this Class be written in php? Composition, Inheritance, Decorator?

I have a database where people might simultaneously be employees, customers and members of a company. My adminstration system allows me to edit the properties of the Person, Employee, Customer and Member objects on independent pages.

Now, I’d like to be able to call up a Person but be able to view and edit their employee, customer and member properties on the same page. Creating a view for this scenario is trivial, but I’d like to do it properly with the correct specification of base class, inheritances, compositions and aggregates, etc.

I do understand that the employees, customers and members can’t exist without the person, so for my admin page, I’m thinking I should create a new class, UberDude, that is a composition of the person, employee, customer and member classes. I would also like to use this new class as part of a collection so that I could draw a table of all persons and see whether they are employees, customers or members. Something doesn’t seem right and I can’t work out the code for the class structure. Any ideas?

My own preference would be to make Employee, Customer and Member extend the Person class - because they are a kind of person. I wouldn’t personally have a class which represents all three. I would also possibly make the Person an abstract class which can only exist as extensions - the above. What is a person doing on your system if they aren’t an Employee, Customer or Member?

However if you want that, creating a new class isn’t necessary; You already have a class which can contain the Employee, Customer and Member objects - the Person class! Those objects can in turn contain the Person object so that you can access public Person methods inside Employee, Customer and Member. However, that would make Employee, Customer and Member have something important in common: They all contain a Person object, so again I would make an abstract class which they extend, for symmetry.

Hi Jake, thanks for replying - good points!

I don’t think I was able to fully express in my post the reason I think I need new classes for a combined ‘person-employee-customer-member’, ‘person-employee-member’, ‘person-employee-customer’, ‘person-customer-member’ etc…

This particular example is for a sports facility. Say I want to offer employees a customer discount, but they must be members (they have completed basic training as all customers do). That is to say this object is an employee AND a customer AND a member. I would like a ‘person-employee-customer-member’ class. I’m not aware how I can subtype Person and combine the subtypes?

Another example is an online shop. Say now I want to offer customers premium membership for a limited time only. They are only customers until they become a member. Then they are a customer AND a member. Again, I’m not sure how extending person would let me combine customer and member into a ‘person-customer-member’ class?

Woah, woah, woah. Stop right there :stuck_out_tongue: New classes for every combination is over-complicating everything massively. It also kills the possibility of polymorphism, as a class can only extend one class.

Ok let’s start from the basics - what’s your database layout?

I’m assuming (hoping) it’d be something like:


[B][U]People[/U]
[/B]ID
Name
Address
Etc
[U]
Customers[/U]
Person ( = People.ID)
etc

[U][B]Members[/B][/U]
Person ( = Customers.ID)
etc

[U][B]Employees[/B][/U]
Person ( = People.ID)
etc

That way you could have something like:


interface iPerson{
    public function getID();
    public function getName();
    public function getAddress();
    public function setAddress($Address);
    public function isEmployee();
    public function isCustomer();
    public function isMember();
    public function getEmployee();
    public function getCustomer();
    public function getMember();
}
class Person implements iPerson{
    protected $ID, $Name, $Address;
    protected $Employee = null, $Customer = null, $Member = null;
    public function getID(){
        return $this->ID;
    }
    public function getName(){
        return $this->Name;
    }
    public function getAddress(){
        return $this->Address;
    }
    public function setAddress($Address){
        $this->Address = $Address;
    }
    public function isEmployee(){
        return !isNull($this->Employee);
    }
    public function isCustomer(){
        return !isNull($this->Customer);
    }
    public function isMember(){
        return !isNull($this->Member);
    }
    public function getEmployee(){
        return $this->Employee;
    }
    public function getCustomer(){
        return $this->Customer;
    }
    public function getMember(){
        return $this->Member;
    }
}
class PersonHolder implements iPerson{
    protected $Person;
    public function getID(){
        return $this->Person->getID();
    }
    public function getName(){
        return $this->Person->getName();
    }
    public function getAddress(){
        return $this->Person->getAddress();
    }
    public function setAddress($Address){
        return $this->Person->setAddress($Address);
    }
    public function isEmployee(){
        return $this->Person->isEmployee();
    }
    public function isCustomer(){
        return $this->Person->isCustomer();
    }
    public function isMember(){
        return $this->Person->isMember();
    }
    public function getEmployee(){
        return $this->Person->getEmployee();
    }
    public function getCustomer(){
        return $this->Customer->getCustomer();
    }
    public function getMember(){
        return $this->Member->getMember();
    }
}
class Customer extends PersonHolder{
    protected $SomethingCustomerRelated = null;
    function __Construct(....){
        $this->Person = $Person;
    }
    function getSomethingEmployeeRelated(){
        return $SomethingCustomerRelated;
    }
}
class Employee extends PersonHolder{
    protected $SomethingEmployeeRelated = null;
    function __Construct(....){
        $this->Person = $Person;
    }
    function getSomethingEmployeeRelated(){
        return $SomethingEmployeeRelated;
    }
}
class Member extends PersonHolder{
    protected $SomethingMemberRelated = null;
    function __Construct(....){
        $this->Person = $Person;
    }
    function getSomethingMemberRelated(){
        return $SomethingMemberRelated;
    }
}

By doing this, a customer, member and employee can all contain a person object which, once changed, will affect the customer, member and employee objects. It also means that all of them implement the iPerson interface, so passing a customer to a function which expects a person:

function assassinate(iPerson $Person){ ... }

You can assassinate (assuming your company is, in fact, an evil organisation) a customer, person or employee in the same way. The Person object also has access to the employee, member and customer objects, if they so exist, and has methods to see if they do or don’t exist.

There may be other ways of going about it, but that seems the most logical to me.

Thanks Jake - Correct me if I’m wrong in assuming that what you’re saying is keep things the way I have (as your code snippets show) so that, for example, the page where I can view ‘employee members’ and update ‘employee-member’ specific data uses a separate instance of ‘employee’ and an instance of ‘member’ rather than a single ‘employee-member’ instance. It’s down to my ‘view’ and business logic to compose the idea I have of an ‘employee-member’ ?