Just in case you haven’t read the two previous articles, where I explained in detail how to create two sample domain and data access layers, below I listed the definitions of the classes that compose the layers. This will allow you to take a close look at them and quickly understand how they work. First, here is the source code of the classes that comprise the domain layer, along with an example that shows how to use it for modeling some simple user objects. Here are the corresponding sample files: (DomainObjectAbstract.php)
<?php
abstract class DomainObjectAbstract { protected $_data = array();
public function __construct(array $data = NULL) { if ($data !== NULL) { // populate domain object with an array of data foreach ($data as $property => $value) { if (!empty($property)) { $this->$property = $value; } } } }
// set domain object property public function __set($property, $value) { if (!array_key_exists($property, $this->_data)) { throw new ModelObjectException('The specified property is not valid for this domain object.'); } if (strtolower($property) === 'id' AND $this->_data['id'] !== NULL) { throw new DomainObjectException('ID for this domain object is immutable.'); } $this->_data[$property] = $value; }
// get domain object property public function __get($property) { if (!array_key_exists($property, $this->_data)) { throw new DomainObjectException('The property requested is not valid for this domain object.'); } return $this->_data[$property]; }
// check if given domain object property has been set public function __isset($property) { return isset($this->_data[$property]); }
// unset domain object property public function __unset($property) { if (isset($this->_data[$property])) { unset($this->_data[$property]); } } }
(DomainObjectException.php)
<?php
class DomainObjectException extends Exception{} As its name implies, the above “DomainObjectAbstract” class defines the behavior and structure of generic domain objects, whose properties can be added and removed on the fly, thanks to the implementation of the “__set()”, __get()” and “__unset()” PHP magic functions. With this class seated on top of the hierarchy, creating a sub class that spawns concrete user objects is reduced to doing something as simple as this: (User.php)
<?php
class User extends DomainObjectAbstract { protected $_data = array('id' => NULL, 'fname' => '', 'lname' => '', 'email' => ''); } That was easy to code and read, wasn’t it? Now, if you ever want to employ the previous “User” class to create a concrete user object, it could be done in a snap, in the following way: $user = new User(); $user->fname = 'Julie'; $user->lname = 'Smith'; $user->email = 'julie@domain.com'; If you analyze the two earlier classes, you’ll realize that they’re the building blocks of a domain layer composed of objects that are nothing but basic data containers with a predictable behavior. This approach is very useful and efficient, as the objects are easy to test, and completely ignorant of the persistent storage mechanism. With the domain layer already set, it’s time to show the structure of the data access layer. Disappointingly, it is made up of only a trivial MySQL abstraction class called “MySQLAdapter,” whose definition is as follows: (MySQLAdapter.php)
<?php
class MySQLAdapter { private $_config = array(); private static $_instance = NULL; private static $_connected = FALSE; private $_link = NULL; private $_result = NULL;
// return Singleton instance of MySQLAdapter class public static function getInstance(array $config = array()) { if (self::$_instance === NULL) { self::$_instance = new self($config); } return self::$_instance; }
// private constructor private function __construct(array $config) { if (count($config) < 4) { throw new MySQLAdapterException('Invalid number of connection parameters'); } $this->_config = $config; }
// prevent cloning class instance private function __clone(){}
// connect to MySQL private function connect() { // connect only once if (self::$_connected === FALSE) { list($host, $user, $password, $database) = $this->_config; if ((!$this->_link = mysqli_connect($host, $user, $password, $database))) { throw new MySQLAdapterException('Error connecting to MySQL : ' . mysqli_connect_error()); } self::$_connected = TRUE; unset($host, $user, $password, $database); } }
// perform query public function query($query) { if (is_string($query) and !empty($query)) { // lazy connect to MySQL $this->connect(); if ((!$this->_result = mysqli_query($this->_link, $query))) { throw new MySQLAdapterException('Error performing query ' . $query . ' Error : ' . mysqli_error($this->_link)); } } }
// fetch row from result set public function fetch() { if ((!$row = mysqli_fetch_object($this->_result))) { mysqli_free_result($this->_result); return FALSE; } return $row; }
// get insertion ID public function getInsertID() { if ($this->_link !== NUlL) { return mysqli_insert_id($this->_link); } return NULL; }
// count rows in result set public function countRows() { if ($this->_result !== NULL) { return mysqli_num_rows($this->_result); } return 0; }
// close the database connection function __destruct() { is_resource($this->_link) AND mysqli_close($this->_link); } }
(MySQLAdapterException.php)
<?php
class MySQLAdapterException extends Exception{} Since all that the “MySQLAdapter” class does is perform some common database operations, such as connecting to the server, running hard-coded queries and fetching rows in result sets, I’m not going to spend more time explaining its driving logic. It’s valid to notice, though, that this class is the only element that comprises the data access layer. Naturally, if you’re like me, at this point you want an answer to the following question: if an application needs to save a user object (or any other kind of domain object) to the database via the previous “MySQLAdapter” class, how can this be done without breaking the rules of the persistence ignorance paradigm? Well, here’s where data mappers come in. Their mission is to keep those domain objects isolated from the data access layer. But this is theory that needs to be backed up with some functional code. Therefore, in the following segment I’m going to start coding a data mapper class, so you’ll be able to understand its underlying logic. Now, click on the link below and read the section to come.
blog comments powered by Disqus |
|
|
|
|
|
|
|