As I said at the beginning of this article, the sample MySQL-driven program developed in the preceding tutorial showed pretty clearly how to use the wrong approach to the dependency of a persistent class. In this particular example, the dependency in question was a basic database handler, which was created inside the constructor of the persistent class. This is definitely a very bad thing; this approach doesn’t stick to the schema imposed by the Inversion of Control principle referenced in the introduction. Therefore, it’s important to spend a few minutes recalling how this sample application was built originally, so you can discover for yourself why the classes that compose this program establish a incorrect relationship with each other. First off, here’s the definition of the database handler. Pay close attention to it: class MySQL { private $result = NULL; private $link = NULL; private static $instance = NULL;
// returns a singleton instance of MySQL class (chainable) public static function factory($host, $user, $password, $database) { if (self::$instance === NULL) { self::$instance = new MySQL($host, $user, $password, $database); } return self::$instance; } // connect to MySQL public function __construct($host, $user, $password, $database) { if (FALSE === ($this->link = mysqli_connect($host, $user, $password, $database))) { throw new Exception('Error : ' . mysqli_connect_error()); } }
// perform query public function query($query) { if (is_string($query) AND empty($query) === FALSE) { if (FALSE === ($this->result = mysqli_query($this->link, $query))) { throw new Exception('Error performing query ' . $query . ' Error message :' .mysqli_error($this->link)); } } }
// fetch row from result set public function fetch() { if (FALSE === ($row = mysqli_fetch_assoc($this->result))) { mysqli_free_result($this->result); return FALSE; } return $row; }
// get insertion ID public function getInsertID() { return mysqli_insert_id($this->link); } // count rows in result set public function countRows() { if ($this->result !== NULL) { return mysqli_num_rows($this->result); } } // implement destructor to close the database connection function __destruct() { mysqli_close($this->link); } } Obviously, the set of tasks performed by the above “MySQL” class are easy to follow. It allows you to run queries against a selected database, retrieve and count rows in a result set, and find insertion IDs. So far, there’s nothing special about the way that this abstraction class functions, right? Moving on, below there’s the definition of the persistent class, which uses the previous database handler for saving and fetching its properties from a “users” MySQL table. Here’s how this class looks: class User { private $data = array(); private $id = NULL; private $db = NULL;
// constructor public function __construct($id = NULL) { $this->db = MySQL::factory('host', 'user', 'password', 'database'); if ($id !== NULL) { $this->id = $id; $this->db->query('SELECT * FROM users WHERE id=' . $this->id); $this->data = $this->db->fetch(); } }
// set undeclared property public function __set($property, $value) { if ($property !== 'name' and $property !== 'email') { return; } $this->data[$property] = $value; }
// get undeclared property public function __get($property) { if (isset($this->data[$property]) === TRUE) { return $this->data[$property]; } }
// save object to session variable public function __destruct() { if ($this->id === NULL) { $this->db->query("INSERT INTO users (id, name, email) VALUES (NULL, '$this->name', '$this->email')"); } else { $this->db->query("UPDATE users SET name = '$this->name', email = '$this->email' WHERE id = $this->id"); } } } As seen above, there are no major complaints about the logic implemented by this class. It simply uses some magic methods provided by PHP 5 for making its own properties persist across multiple HTTP requests. The following script demonstrates how to accomplish this process in a friendly way: // create a user object $user1 = new User(); $user1->name = 'Alejandro Gervasio'; $user1->email = 'alejandro@domain.com';
// create another user object $user2 = new User(); $user2->name = 'Susan Norton'; $user2->email = 'susan@domain.com'; Regardless of the fact that the “User” class does a decent job when it comes to building some persistent objects, it exposes a serious drawback. Yes, you guessed right! Its constructor is responsible for creating an instance of its dependency, that is a MySQL object, to have access to the storage table. This is a poor approach where an object controls its context, when in reality the opposite should happen. So, how can this issue be addressed? Well, here’s where the dependency injection pattern comes in, since it’s possible to implement a version of it called “injection by constructor,” which allows you to apply the Inversion of Control principle very easily. Want to see how this principle will be put to work with the two classes shown before? Then read the following segment.
blog comments powered by Disqus |
|
|
|
|
|
|
|