Here’s something I often ponder over: If an object contains other objects, which is responsible for grabbing database information for the child objects?
eg we have a Product class, which contains a $supplier of type Supplier class. If we instantiate the Product from an ID, would we also grab the details for its Supplier, or would we wait and see if the Supplier needs to be got? I’m talking in general here, as my system (and most system) are obviously much larger than indicated by this code snippet:
First off, the Supplier does need to be able to get it’s own information from the database:
class Supplier
{
private $supplierID;
public $name;//probably define some setters & getters instead of using public
public $emailAddress;
public __construct($supplierID = null)
{
if(is_numeric($supplierID))
{
$this->supplierID = $supplierID;
$this->load();
}
}
private function load()
{
$database_result = something_database_function("SELECT s.supplierID, s.supplierName, s.emailAddress FROM suppliers s WHERE s.supplierID=".$this->supplierID);
$this->supplierName = $database_result->supplierName;
$this->emailAddress = $database_result->emailAddress;
}
}
When we instantiate the Product from an ID, we could also grab the Supplier:
class Product
{
private $productID;
private $productName;
private $supplier; //instance of Supplier class
public __construct($arg = null)
{
if(is_numeric($arg))
{
$this->productID = $arg;
$this->load();
}
}
private function load()
{
$database_result = something_database_function("SELECT p.productID, p.supplierName, s.supplierID, s.supplierName, s.emailAddress FROM products p JOIN suppliers s ON p.supplierID = s.supplierID WHERE p.productID=".$this->productID);
$this->productName = $database_result->productName;
//create supplier
//not that we could also allow the Supplier construct to accept these values. Or failing that, make some setter methods in the supplier
$this->supplier = new Supplier();
$this->supplier->supplierName = $database_result->supplierName;
$this->supplier->emailAddress = $database_result->emailAddress;
}
public function getSupplier()
{
return $this->supplier;
}
}
Or we could just load the product information and only grab the supplier information when needed:
class Product
{
private $productID;
private $name;
private $supplier;
private $_loaded_supplier = false;
public __construct($arg = null)
{
if(is_numeric($arg))
{
$this->productID = $arg;
$this->load();
}
}
private function load()
{
$database_result = something_database_function("SELECT p.productID, p.supplierName, p.supplierID FROM products p WHERE p.productID=".$this->productID);
$this->productName = $database_result->productName;
$this->supplierID = $database_result->supplierID;
}
public function getSupplier()
{
if($!this->_loaded_supplier){
$this->supplier = new Supplier($this->supplierID);
$this->_loaded_supplier = true;
}
return $this->supplier;
}
}
These classes are used in many parts of the system, so it’s hard to know now how they will be used. There’s also the situations where a list of (eg) products will be grabbed from the database with a single query, and Products being passed their instance data directly, rather than each one grabbing from the database. How would you handle a reference to a missing class property $product->some_property_we_havent_populated? Throw an error? return null? detect that this hasn’t been given the data and grab it on the fly?