As usual, before I provide the user mapper class with the capability to delete users from the associated MySQL table, I'd like to spend a few moments reintroducing the definitions of all of the sample classes developed so far. This includes the ones that allow you to create both domain objects and mappers, and the basic database driver that abstracts the access to MySQL. First, here's the base "DomainObjectAbstract" class that models generic domain objects: (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{} Did you recall how this class does its thing? Good. Now, it's time to show the one that creates user domain objects. Here it is: (User.php)
<?php
class User extends DomainObjectAbstract { protected $_data = array('id' => NULL, 'fname' => '', 'lname' => '', 'email' => ''); } The above class was easy to code and read, indeed. Therefore, let's move on and look at the following one, which is the MySQL driver that makes up the data access layer: (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{} Having shown the definition of the previous "MySQLAdapter" class, it's time now to list the full source code of the data mappers. Here you have them: (DataMapperAbstract.php)
<?php
abstract class DataMapperAbstract { protected $_db = NULL; protected $_table = ''; protected $_identityMap = array();
public function __construct(MySQLAdapter $db) { $this->_db = $db; }
// get domain object by ID (implemented by concrete domain object subclasses) abstract public function find($id);
// insert/update domain object (implemented by concrete domain object subclasses) abstract public function save(DomainObjectAbstract $domainObject);
// delete domain object (implemented by concrete domain object subclasses) abstract public function delete(DomainObjectAbstract $domainObject); } (UserMapper.php)
<?php
class UserMapper extends DataMapperAbstract { protected $_table = 'users';
// fetch domain object by ID public function find($id) { // if the requested domain object exists in the identity map, get it from the there if (array_key_exists($id, $this->_identityMap)) { return $this->_identityMap[$id]; } // if not, get domain object from the database $this->_db->query("SELECT * FROM $this->_table WHERE id = $id"); if ($row = $this->_db->fetch()) { $user = new User; $user->id = $row->id; $user->fname = $row->fname; $user->lname = $row->lname; $user->email = $row->email; // save domain object to the identity map $this->_identityMap[$id] = $user; return $user; } }
// save domain object public function save(DomainObjectAbstract $user) { // update domain object if ($user->id !== NULL) { $this->_db->query("UPDATE $this->_table SET fname = '$user->fname', lname = '$user->lname', email = '$user->email' WHERE id = $user->id"); } // insert domain object else { $this->_db->query("INSERT INTO $this->_table (id, fname, lname, email) VALUES (NULL, '$user->fname', '$user->lname', '$user->email')"); } } } That was truly a lot of code to read! Yet the effort was really worthwhile, as the process reminded you how we built a few simple data mappers in PHP. What's more, you recalled how to implement an identity map within a user mapper class, in order to keep track of what user objects have been retrieved from the database. What else can you ask for? Well, the earlier user mapper still lacks the ability to delete users from the corresponding MySQL table. This issue obviously must be addressed properly. Therefore, in the following section I'm going to implement an additional method that will perform this deletion task in a comprehensive fashion. Now, to see how this whole new method will be defined, click on the link below and keep reading.
blog comments powered by Disqus |
|
|
|
|
|
|
|