Search This Blog

Monday, August 15, 2011

Putting it all Together

You'll recall from the encapsulation discussion in Chapter 12 that it's always a good idea to protect your data in private member variables and use functions to access all of the properties of your classes. You also looked at using __get and __set to make this process a bit easier. You can see just by looking at the class diagrams shown earlier that all of your classes have fairly simple property requirements and that most of them could benefit from the approach you took in implementing the Entity class earlier.

Because you know about inheritance and how to use it to your advantage in PHP, let's remove all that clever functionality from being tied to just the Entity class and create a class that will enable you to reuse the code in other classes. You originally called this class PropertyObject.

The PropertyObject Class

The original PropertyObject class and the Entity class that followed it had no unified facility for data validation, so you need an interface for anything that can be validated. Here's the code:

<?php
  interface Validator {
    abstract function validate();
  }
?>

Name the file interface.Validator.php. Now put it to some use with the new and improved PropertyObject class. The code that follows shows how to integrate the Validator interface with the PropertyObject class. Enter the code in a file called class.PropertyObject.php.

<?php
  require_once('interface.Validator.php');

  abstract class PropertyObject implements Validator {

     protected $propertyTable = array();    //stores name/value pairs
                                            //that hook properties to
                                            //database field names

    protected $changedProperties = array(); //List of properties that:
                                            //have been modified

    protected $data;                        //Actual data from
                                            //the database

    protected $errors = array();            //Any validation errors
                                            //that might have occurred

    public function __construct($arData) {
      $this->data = $arData;
    }

    function __get($propertyName) {
      if(!array_key_exists($propertyName, $this- propertyTable))
        throw new Exception("Invalid property\"$propertyName\"!");

      if(method_exists($this, 'get' . $propertyName)) {
        return call_user_func(array($this, 'get' . $propertyName));
      } else {
        return $this->data[$this->propertyTable[$propertyName]];
      }
   }

     function __set($propertyName, $value) {
       if(!array__key_exists ($propertyName, $this->propertyTable) )
          throw new Exception("Invalid property\"$propertyName\"!");

       if(method_exists ($this, 'set'.$propertyName) ) {
          return call_user_func(
                    array($this, 'set'.$propertyName),
                    $value
                        );
       } else {

         //If the value of the property really has changed
         //and it's not already in the changedProperties array,
         //add it.
         if($this->propertyTable[$propertyName] != $value &&
            !in_array ($propertyName, $this->changedPropertirs)) {
            $this->ChangedProperties [ ] = $propertyName;
         }

          //Now set the new value
         $this->data[$this->propertyTable[$propertyName]] = $value;

       }
     }

     function validate()
     {}

   }
?>

Take a closer look at what's happening here. You've created four protected member variables. Protected member variables are only visible to subclasses of a class; they aren't visible to the code that uses those objects.

$propertyTable will contain a mapping of real property names to field names in your database. Often the names of database fields have been prefixed with a naming convention to indicate their data type. For example, entities.sname1 might be a field in the table entities of type string, containing the first name. However, sname1 isn't a terribly friendly name for an object property, so you need to provide a mechanism to translate database field naming conventions to friendly property names.

$changedProperties serves the same purpose as in previous implementations of this class. It's an array that stores a list of the names of the properties that have been modified.

$data will be an associative array of database field names and values. The array will be supplied to the constructor with the data structure coming directly from pg_fetch_assoc() (or mysql_fetch_assoc(), or any of the other [x] _fetch_assoc() database functions). This makes constructing a useful object directly from a database query a lot easier, as you'll see shortly.
The last member variable, $errors, will contain an array of field names and error messages in the event that the validate method (required by the Validate interface) should fail.

The class is declared abstract for two reasons. The first is that the PropertyObject class on its own is not very useful. The classes that extend PropertyObject still have some work to do before you can use them. The second is that you have not provided an implementation of the required method validate(). Because you are still labeling that method abstract in PropertyObject, you must also label the class itself abstract, forcing all inheriting classes to implement that function. Any attempts to use classes that extend PropertyObject but not implement that function will cause a runtime error.

Next, you see the greatly simplified constructor. All the constructor does is accept the associative array that will most likely be populated from a database query and assign it to the protected member variable $data. Most subclasses of PropertyObject will need to override the constructor and do something a little more interesting.

The other major change is in the internals of the_get() and_set() functions. Because you are storing data in the $data member, you need to be able to map property names to the actual field names in the database. The lines that contain the code $this->data [ $this->propertyTable [ $ propertyName ] ] are doing just that. By fetching and assigning values to the $data member using their database field names, rather than their property names you can easily implement the sort of automatic database persistence that you saw previously in the Entity class.

If the workings of the $data and $propertyTable members aren't clear to you yet, don't worry. They will be as soon as you see an example.

The Contact Type Classes

Now that you have the handy-dandy PropertyObject class, you can start putting it to some use. The files that follow are for the Address, EmailAddress, and PhoneNumber classes.
In the code you'll see a reference to a class called DataManager, which is going to be a wrapper class for all the database functions you need to use. That enables you to have one central place for all your data-interaction code. You'll examine that class in just a bit.
Enter the code that follows (the Address class) into a file called class. Address.php:

<?php
  require_once('class.PropertyObject_php');

  class Address extends ProportyObject {

    function__construct($addressid) {
      $arData = DataManager: :get.AddressData ($addressid);

      parent::__construct($arData);

      $this->propertyTable['addressid'] ='addressid';
      $this->proportyTable['id'] ='addressid';
      $this-->propertyTable['entityid'] = 'entityid';
      $this->propertyTable['address1'] ='aaddress1';
      $this->propertyTable['address2'] = 'saddress2';
      $this->propertyTable['city'] = 'scity';
      $this->propertyTable['state'] ='cstate';
      $this->proportyTable['zipcode'] = 'spostalcode';
      $this->proportyTable['type'] = 'stype';
    }

    function validate() {
      if(strlen($this->state)!=2) {
        $this->errors['state'] = 'Please choose a valid state.';
      }

      if(strlen($this->zipcode) < 5 ||
        strlen($this->zipcode)>10){
        $this->errors['zipcode'] = 'Please enter a 5 or 9 digit zipcode';
     }

     if(!$this->address1) {
           $this->errors['address1'] = 'Address 1 is a required field.';
     }

     if(!$this->city) {
           $this->errors['city'] = 'City is a required field.';
     }

     if(sizeof (this->errors)) {
       return false;
     } else {
       return true;
     }
   }

   function __toString() {
     return $this->address1 . ', ' .
            $this->address2 . ', ' .
            $this->city . ', ' .
            $this->state . ' ' . $this->zipcode;
   }
  }
?>

Because the PropertyObject class took care of so much of the work, only two methods in the Address class needed to be implemented (a __toString() implementation was thrown in just for fun). In the constructor, you see for the first time how the $propertyTable array works. The list of properties required in the class was specified in the UML diagram created during the initial architecture of the application (in the beginning of this chapter). Based on the properties this object has, you can also make some decisions about the structure of the database table. Generally, you need one field for each property and because this class has to relate back to the Entity class, you also need to store some reference to the parent Entity. Let's assume that you have the following SQL statement to create the Address table:

CREATE TABLE entityaddress (
  addressid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  saddress1 varchar(255),
  saddress2 varchar(255),
  scity varchar(255),
  cstate char(2),
  spostalcode varchar(10),
  stype varchar(50),
  CONSTRAINT fk__entityaddress_entityid
    FOREIGN KEY (entityid) REFERENCES entity(entityid)
);

A properly named database field indicates its data type by using a one-character prefix, letting you know what kind of data goes in the field. Having naming conventions is just as important for database design as it is for your code.

The propertyTable array is set up in the Address class to map friendly property names (like city, state, and ZIP code) to the less friendly database field names (like scity, estate, and spostalcode). Note that you can map multiple property names to the same database field name in propertyTable. This enables you to refer to the primary key of the address by either $objAddress->addressid or $objAddress->id.

What's incredibly exciting about the Address class is that the overwhelming majority of the code is spent implementing business logic and data validation. There's almost no extraneous code here. Its sole responsibility is to populate itself and validate its own contents. Everything else is left up to the DataManager class (which you'll see in detail shortly) and the PropertyObject.

The code for the Email class, which looks very similar, follows. Enter it into a file called class.EmailAddress.php.

<?php
  require_once('class.PropertyObject.php');

  class EmailAddress extends PropertyObject {

    function __construct($emailid) {
      $arData = DataManager::getEmailData($emailid);

      parent::__construct($arData);

      $this->propertyTable['emailid'] = 'emailid';
      $this->propertyTable('id'] ='emailid';
      $this->propertyTable['entityid'] ='entityid';
      $this->propertyTable['email'] = 'semail';
      $this->propertyTable ['type' ] = 'stype';
    }

    function validate() {
      if(!$this->email) {
            $this->errors['email']='You must set an email address.';
      }

      if(sizeof($this-->errors)) {
         return false;
      } else {
        return true;
      }
    }

    function __toString() (
      return $this->email;
    }
  }
?>

There's very little ancillary code here, just fetching the code from the database and setting up the propertyTable. Everything else is data validation. Again, the UML diagram was the guide to deciding on the properties of the Email class and the structure of the corresponding database table.

The database table for the entityemail table looks like this:

CREATE TABLE entityemail   (
  emailid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  semail varchar(255),
  stype varchar(50),
  CONSTRAINT fk_entityemail_entityid
    FOREIGN KEY (entityid) REFERENCES entity(entityid)
);

The PhoneNumber class works very much like Address and Email. Here's the code to enter the code into class.PhoneNumber.php:

<?php
  require_once('class.PropertyObject.php');

  class PhoneNumber extends PropertyObject {

    function__construct($phoneid) {
      $arData = DataManager::getPhoneNumberData($phoneid);

      parent::__construct($arData);

      $this->propertyTable['phoneid'] = 'phoneid';
      $this->propertyTable['id'] = 'phoneid';
      $this->propertyTable['entityid'] = 'entityid';
      $this->propertyTable['number'] = 'snumber';
      $this->propertyTable['extension'] = 'sextension';
      $this->propertyTable['type'] = 'stype';
    }

    function validate() {
      if(!$this->number) {
          $this-->errors['number'] = 'You must supply a phone number.';
      }

      if(sizeof($this->errors)) {
        return false;
      } else {
        return true;
      }
    }

    function__toString() {
      return $this-->number .
             ($this->extension ? ' x' . $this->extension : '');
    }
  }
?>

And here's the SQL statement to create the entityphone table:

CREATE TABLE entityphone (
  phoneid int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  entityid int,
  snumber varchar(20) ,
  sextension varchar(20),
  stype varchar(50),
  CONSTRAINT   fk_entityemail_entityid
    FOREIGN KEY (entityid) REFFRENCES entity (entityid)
);

The DataManager is used to self-populate the data, set up the propertyTable, and define some validation rules. A__toString implementation is created to make it easier to print out complete phone numbers with their extensions later on.

The DataManager Class

Let's have a look at that DataManager class. It and the other database code samples in this chapter have been using MySQL, although a class like it would work just as well with PostgresSQL, Oracle, or any other RDBMS. The primary responsibility of the DataManager class is to put all the data access code into a single location, making it much easier to change the database type or connection parameters later on. All of the class's methods have been declared static because the class doesn't rely on any member variables. Note the use of the static function variable in getConnection(). This is done to ensure that only one database connection is open during a single page request. There's a lot of overhead associated with establishing a database connection so eliminating unnecessary connections helps improve performance. Create a file class.DataManager.php: and enter the following class code:

<?php
require_once('clasa.Entity.php');//this will be needed later
require_once('class.Individual.php');
require__once('class.Organisation.php');

class DataManager {
  private static function_getConnection(){
    static $hDB;

    if(isset ($hDB)) {
      return $hDB;
    }

    $hDB = mysql_connect('localhost', 'phpuser', 'phppass')
      or die("Failure connecting to the database!");
$Link = mysqI select_db('sample_db') or die('Failure selecting the
database!');
    return $hDB;
  }

  public static   function getAddresaData($addressID) {
    $sql = "SELECT * FROM entityaddress WHERE addressid = $addressID" ;
    $res = mysql_query($spl, DataManager::_getConnection());
    if(! ($res && mysql_num_rows($res)}) {
      die("Failed getting address data for address $addressID");
    }

    return mysql_fetch_assoc($res);
  }

  public static function getEmailData($emailID) {
    $sql = "SELECT * FROM entityemail WHERE emailid = $emailID";
    $res = mysql_query($sql, DataManager::_getConnection());

    if(! ($res && mysql_num rows($res))) {
      die("Failed getting email data for *email $emailID");
    }

    return mysql_fetch_assoc($res);
  }
}
?>

The DataManager class provides the data structures used to populate the $data member of your PropertyObject subclasses. There are separate functions to return the data for each of the types. You'll be adding a few new functions to this class a bit later.

All of the methods of this class are declared to be static. Remember, static methods are those requiring all member variables to be static. You don't need to instantiate static classes to use their methods. There are several cases where this makes sense. Consider a class called Math that exposes methods such as squareRoot(), power(), and cosine(), and has properties including the mathematical constants e and pi. All instances of this class perform the same math. The square root of 2 doesn't change, 4 raised to the 3rd power is always going to be 64, and the two constants are, well, constant. There's no need to create separate instances of this class because its state and properties never change. A class called Math implemented in this manner should allow for all its functions to be called statically.

The DataManager class is much the same. All of the functions are self-contained, and all of the member variables they interact with are static. The class exposes no properties. You can invoke the methods of the class using the static method operator :: as a result. Because all the methods you've created are static, you never need to instantiate an object with $obj = new DataManager(); you can use the syntax DataManager::getEmail() instead.
Note the use of a static function variable in the private method_getConnection(). The use of static function variables was discussed in Chapter 6.

The Entity, Individual, and Organization Classes

With all the supporting classes in place, you can move on to the core of the application: the Entity class and its subclasses.

First, make sure you're updating your UML diagram as you make changes to the object hierarchy. You've created the PropertyObject class and made all of your classes subclasses of it with one exception—the new DataManager class, which does not inherit from anything. The PropertyObject implements an abstract interface called Validator

Now you can begin to develop the Entity, Individual, and Organization classes. The following code shows the fully implemented Entity class:

<?php

require_once ('class.PropertyObject.php');
require_once ('class.PhoneNumber.php');
require_Once('class.Address.php');
require_once('class.Email.Address.php');

abstract class Entity extends PropertyObject {

  private $_emails;
  private $_addresses;
  private $_phonenumbers;
  public function__construct($entityID) {
    $arData = DataManager::get,EntityData ($entity.ID);

    parent::__construct($arData);

    $this->propertyTable['entityid'] = 'entityid';
    $this->propertyTablet'id'] = 'entityid';
    $this->propertyTable['name1'] = 'sname1';
    $this->propertyTable['name2'] = 'sname2';
    $this->propertyTable['type'] = 'ctype';

    $this->_emails = DataManager::getEmailObjectsForEntity($entityID);
    $this->_addresses = DataManager::getAddressObjectsForEntity($entityID);
    $this->_phonenumbers = DataManager::getPhoneNumberObjectsForEntity
    ($entity!D);

  }

    function setID($val) {
      throw new Exception('You may not alter the value of the ID field!');
    }

    function setEntityID($val) {
    $this->setID($val);
    }

    function phonenumbers{$index) {
      if(!isset ($this->_phonenumbers[$index])){
        throw new Exception('Invalid phone number specified!');
    } else {
        return $this->_phonenumbers[$index];
      }
    }

    function getNumberOfPhoneNumbers() {
      return sizeof ($this->_phonenumbers);
    }

    function addPhoneNumber(PhoneNumber $phone) {
      $this->_phonenumbers[] = $phone;
    }

    function addresses($index) {
      if(!isset($this->__addresses [$index]) ) {
        throw new Exception('Invalid address specified!');
      } else {
        return $this-->,_addresses[$index];
      }
    }

    function getNumberOfAddresses() {
      return sizeof($this->_addresses);
    }
    function addAddress(Address $addresa) {
      $this->_addresses[] = $address;
    }

    function emails($index) {
      if(!inset($this->_emails[$index])} {
        throw new Exception('Invalid email specified!');
      } else {
         return $this->_emails[$index];
      }
    }

    function get.NumberOfEmails() {
      return sizeof($this->_emails);
    }

    function addEmail(Email $email) {
      $this->_emails[]=$email;
    }

    public function, validate() {
      //Add common validation routines
    }

  }
?>

By moving all of the property getting and setting functionality to the parent PropertyObject class, you simplify the Entity class and ensure that it is focused only on the code required to implement an entity.

The Entity class is declared abstract because it isn't useful on its own. All entities are either Individuals or Organizations. You do not want to be able to instantiate objects of class Entity. Declaring it abstract prevents the class from being instantiable.

You've added requests to a few new functions of the DataManager:getEntityData() and get[x]ObjectsForEntity.getEntityData() returns the data required to instantiate an entity, just like functions you've already seen for the contact types. The following shows the code for the new functions in class.DataManager.php:

// top of file omitted for brevity
   ...
      die("Failed getting phone number data for phone $phoneID");
   }

   return pg_fetch_assoc($res);
 }

public static function getEntityData($entityID) {
    $sql = "SELECT * FROM entities WHERE entityid = $entityID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(!($res && mysql_num_rows ($res))) {
      die("Failed getting entity $entityID");
    }
    return mysql_fetch_assoc($res);
 }
?>
To add the get[x]ObjectsForEntity functions, place the following code at the end of class.DataManager.php, just after the getEntityData function:
public static function getAddressObjectsForEntity($entityID) {
  $sql = "SELECT addressid from entityaddress WHERE entityid = $entityID";
  $res = mysql_query($sql, DataManager::_getConnection());
  if(!$res) {
    die("Failed getting address data for entity SentityID");
  }

  if(mysql_num_rows($rea)) {
    $objs = array();
    while($rec = mysql_fetch_assoc($res)) {
      $objs[] = new Address($rect'addressid']};
    }
    return $objs;
  } else {
    return array();
  }
}

public static function getEmailObjectsForEntity($entityID) {
  $sql = "SELECT emailid from entityemail WHERE entityid = $entityID";
  $res = mysql_query($sql, DataManager::_getConnection());
    if(!$res) {
    die("Failed getting email data for entity $entityID");
  }

  if (mysql_num_rows ($res)) {
     $objs = array();
     while($rec = mysql_fetch_assoc($res)) {
       $objs[] = new EmailAddress($rec['emailid']);
    }
    return $objs;
  } else {
    return array();
  }
}

public static function getPhoneNumberObjectsForEntity($entityID) {
  $sql = "SELECT phoneid from entityphone WHERE entityid = $entityID";
  $res' = mysql_query($sql, DataManager::_getConnection());
  if(!$res) {
    die("Failed getting phone data for entity $entityID");
  }
  If(mysql_num_rows($res)) {
    $objs = array();
    while($rec = mysql_fetch_assoc($res)) {
      $objs[] = new PhoneNumber($rec['phoneid']);
    }
    return $objs;
  } else {
    return array();
  }
}

These functions take an entity ID value. They query the database to determine if any e-mails, addresses, or phone numbers exist for the entity in question. If so, they build an array of EmailAddress, Address, or PhoneNumber objects by passing each id to the constructor for the appropriate object type. This array is then passed back to the Entity object where it is stored in the appropriate private member variable.

With the Entity class doing all the heavy lifting, the remaining work is fairly simple. You just need to implement the Individual and Organization classes. Create a file called class.Individual.php and enter the following:

<?php
  require.once('class.Entity.php');
  require_Once('class.Organization.php');

  class Individual extends Entity {

    public function _construct($userID) {
      parent:: __eonstruct($userID);

      $this->propertyTable['firstname']='name1';
      $this->propertyTable['lastname']='name2';

    }

    public function __toString() {
      return $this->firstname . ' ' . $this->lastname;
    }

    public function.get'Employer(){
      return DataManager::getEmployer($this->Id);
    }

    public function validate() {
      parent::validate();

      //add individual-specific validation

    }

  }
?>

Short and sweet. Inheritance makes this easy. The Individual class sets up a few new properties that make it easier to access the first and last name of the individual, instead of having to use the rather ugly name1 and name2 properties defined in the Entity class. It also defines a new method, getEmployer(), which requires a new function in the DataManager. You get to that function as soon as you have your Organization class, which is shown in the following code. Create a file called class.Organization.php, and enter this code into it:

<?php
  require_once('class.Entity.php');
require_once ('class.Individual.php');

  class Organization extends Entity {

    public function __construct ($userID) {
      parent: __Construct($userID);

      $this->propertyTable['name'] = 'name1';

    }

    public function __toString() {
      return $this->name;
    }

    public function getEmployees() {
      return DataManager::get.Employees($this->id);
    }

    public function validate() {
      parent::validate();
      //do organization-specific validation
    }

  }

?>

Again, a fairly simple class, thanks to the power of inheritance. You declare a property called name that makes it easier to obtain the one and only name that an organization has (the sname2 property goes unused for organizations).

To add the functions getEmployer() and getEmployee() to the DataManager class, append the following code to the end of class.DataManager.php:

public static function getEmployer($individualID) {
    $sql = "SELECT organizationid FROM entityemployee " .
           "WHERE individualid = $individualID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(! ($res; && mysq1_num_rows($res))) {
       die("Failed getting employer info for individual $individualID");
    }

    $row = mysql_fetch_assoc($res);
    if($row) {
      return new Organization($row['organizationid']);
    } else {
      return null;
    }
}

  public static function getEmployees ($orgID) {
    $sql = "SELECT individualid FROM entityemployee" .
           "WHERE organizationid = $orgID";
    $res = mysql_query($sql, DataManager::_getConnection());
    if(! ($res && mysql__num_rows($res))) {
      die("Failed getting employee info for org $orgID");
    }

    if(py_num_rows($res)) {
       $objs = array();
       while($row = mysql_fetch_assoc($res)) {
         $objs[] = now Individual($rowt'individualid']);
       }
       return $objs;
     } else {
       return array();
     }
  }
These two functions rely on the presence of a table entityemployee, shown here:
CREATE TABLE entityemployee (
  individualid int NOT NULL,
  organizationid int NOT NULL,
  CONSTRAINT fk_entityemployee_individualid
    FOREIGN KEY (individualid) REFERENCES entity (entityid),
  CONSTRAINT fk_entityemployee_organizationid
    FOREIGN KEY (organizationid ) REFERENCES entity(entityid)
);

The table relates individuals to the organizations by which they are employed. The DataManager functions are similar to code you've already seen.

There's one last function needed to make the entire system workthe DataManager method for listing all the entities in the database. It's called getAllEntitiesAsObjects() and it finishes all the work you need to do on your objects:

public static function getAllEntitiesAsObjects() {
    $sql = "select entityid, type from entities";
    $res = mysql_query ($sql; DataManager: :_getConnection() );
    if(!$res) {
      die("Failed getting all entities");
    }
    if(mysql_num_rows($res)} {
      $objs = array();
      while($row = mysql__fetch_assoc($res)) {
        if($row['type'] == 'I') {
          $objs[] = new Individual($row['entityid']);
        } elseif ($row['type'] == '0') {
          $objs[] = new Organization($row['entityid']);
        } else {
          die("Unknown entity type {$row['type']} encountered!");
        }
      }
      return $objs;
    } else {
      return array();
    }
}
DataManager enables you to enumerate over all of the contacts in your system. It examines the value of the ctype field in the entity table to determine if the entry is an Individual or an Organization, and then instantiates an object of the appropriate type and adds it to the array that the function returns.

Making Use of the System

By now you can see the real power of an OOP approach. The following code (test.php) will display a view, albeit a rather ugly view, of all of the contacts in your database with all of their contact details:

<?php
  require_once('class.DataManager.php');//everything gets included by it

function println($data) {
  print $data . "<br>\n";
}

$arContacts = DataManager::getAllEntitiesAsObjects();
foreacb.($arContacts as $objEntity) {

  if(get_class($objEntity) == 'individual') {
    print "<h1>Individual - ($objEntity->_toString()}</h1>";
  } else {
    print "<h1>0rganization - {$objEntity->_toString() }</h1>";
  }

  if($objEntity->getNumberOfEmails()) {
    //We have emails! Print a header
    print "<h2>Emails</h2>";

    for($x=0; $x < $objEntity->get.NumberOfEmails(); $x++) {
      println($objEntity->emails($x) ->_toString());
    }
  }

  if($objEntity->getNumberofAddresses()) {
    //We have addresses!
    print "<h2>Addresses</h2>";
    for($x=0; $x < $objEntity->getNumberOfAddresses(); $x++) {
      println($objEntity->addresses($x)->__toString());
    }
  }

  if ($objEntity->getNumberOfPhoneNumbers()) {
     //We have phone numbers!
     print "<h2>Phones</h2>";

     for($x=0; $x < $objEntity->getNumberOfPhoneNumberS(); $x++) {
       println($objEntity->phonenumbers($x)->__toString());
     }
  }
  print "<hr>\n";

}
?>

In only 36 lines of code you can display nearly everything there is to show about the entities in your system. Employer and employees aren't shown here, but are left as an exercise for the reader. The line that calls the get_class function should give you some ideas for figuring out which class you're dealing with so you'll know whether to call getEmployer() on an Individual, or getEmployees() on an Organization.