patternphpMinor
CRUD operation using Object relational model and Data Mapper pattern for a Matrimonial website
Viewed 0 times
websitematrimonialmapperoperationrelationalpatternusingforandobject
Problem
Database.php
This creates a connection and executes a query. Although, I think I should make it a static/singleton class.
```
class Database extends PDO
{
/**
* constant DSN contains database driver, host and database name.
*/
const DSN = 'mysql:host=localhost;dbname=matrimonial';
/**
* @var string $userName username of database.
* @var string $passWord password of database.
*/
private $userName;
private $passWord;
/**
* @var object $dbHandler An object of PDO class contain connection
* @var object $stHandler An object of PDOStatement class
*/
private $dbHandler;
private $stHandler;
/**
* @var boolean $connected Contain true if connection is OK otherwise false
*/
private $connected = FALSE;
/**
* @var string $query contain the sql query to be performed
*/
private $query;
/**
* @var mixed[]|null contain the result set returned from database or nothing/null
*/
private $result;
/**
* @var array contain error code, message etc stored by $e->getMessage(); statement
*/
private $error = array();
/**
* Constructor of class.
*
* It create a PDO (ie connection to database) object and store it to $db_handler
* set attributes to throw exception, create a PDOStatement object $st_handler
* and assign true or false to $connected according to established connection
*
* @param string $username username of database
* @param string $password
*/
function __construct($username = "root", $password = '')
{
$this->userName = $username;
$this->passWord = $password;
$opt = array( PDO::ATTR_PERSISTENT=>TRUE );
try {
$this->dbHandler = new PDO(self::DSN, $this->userName, $this->passWord, $opt);
$this->dbHandler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$this->error = $
This creates a connection and executes a query. Although, I think I should make it a static/singleton class.
```
class Database extends PDO
{
/**
* constant DSN contains database driver, host and database name.
*/
const DSN = 'mysql:host=localhost;dbname=matrimonial';
/**
* @var string $userName username of database.
* @var string $passWord password of database.
*/
private $userName;
private $passWord;
/**
* @var object $dbHandler An object of PDO class contain connection
* @var object $stHandler An object of PDOStatement class
*/
private $dbHandler;
private $stHandler;
/**
* @var boolean $connected Contain true if connection is OK otherwise false
*/
private $connected = FALSE;
/**
* @var string $query contain the sql query to be performed
*/
private $query;
/**
* @var mixed[]|null contain the result set returned from database or nothing/null
*/
private $result;
/**
* @var array contain error code, message etc stored by $e->getMessage(); statement
*/
private $error = array();
/**
* Constructor of class.
*
* It create a PDO (ie connection to database) object and store it to $db_handler
* set attributes to throw exception, create a PDOStatement object $st_handler
* and assign true or false to $connected according to established connection
*
* @param string $username username of database
* @param string $password
*/
function __construct($username = "root", $password = '')
{
$this->userName = $username;
$this->passWord = $password;
$opt = array( PDO::ATTR_PERSISTENT=>TRUE );
try {
$this->dbHandler = new PDO(self::DSN, $this->userName, $this->passWord, $opt);
$this->dbHandler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$this->error = $
Solution
Database.php is a bit confusing. The class inherits from PDO, but in the constructor it creates a direct instance of PDO. Furthermore, your Mapper classes appear to be juggling queries that are then delegated to the parent class, but do no actual object mapping as their names suggest. The MatEducation class as an arrayToObject method which does data mapping.What you've basically created is an implementation of the Repository Design Pattern:
(Image credit: Patkos Csaba, http://code.tutsplus.com)
- Client: This is implemented by profile.php
- Repository: This is currently implemented by your Mapper classes
- Factory: This concern is split between your Mapper classes, and the
arrayToObjectmethod in MatEducation. Really, this is the "data mapper."
- Gateway: This is implemented by your Database class
Separating your Concerns
Instead, you want the following classes and interfaces:
-
MatEducation (Class) This is your Domain Model and represents data from the database-
Gateway (Interface) This is the interface that all database gateways must implement. Now you can abstract away database specific code and hide it behind an interface-
MySqlGateway (Class, implements Gateway) The concrete gateway class that knows how to interact with a MySQL database-
DataMapper (Interface) The interface that all data mappers must implement, which takes the place of the "Factory" in the Repository Pattern diagram.-
MySqlDataMapper (Class, implements DataMapper) The data mapper object that knows how to map MySQL database rows to MatEducation objects (and can be expanded to other objects as well)-
MatEducationRepository (Class) The repository object allowing CRUD operations on MatEducation objectsWiring the Concerns Together
Let's look at each component individualy:
MatEducation Domain Model
Nothing special here. Just a plain old PHP object (POPO) and some methods that operate on that data.
class MatEducation {
private $id;
private $eduId;
private $degree;
private $institute;
public __construct($userId, $eduId) {
$this->userId = $userId;
$this->eduId = $eduId;
}
// more methods
}The Gateway Interface and Concrete Class
The Gateway is just that, the gateway to your database. We want to decouple the database vendor from the rest of your application. What lays in MySQL today, could get thrown into PostgreSQL tomorrow. Or Oracle. Or SQL Server. We can do this by first defining an interface:
interface Gateway {
public function prepareAndExecute($query, $values = array(), $fetchMode = PDO::FETCH_ASSOC);
public function lastInsertId($name = null);
public function exec($query);
}Next, we want a concrete class that implements this interface:
class MySqlGateway implements Gateway {
private $pdo;
public __construct($username = null, $password = null) {
// Get username and password from config, if null
$this->pdo = new PDO(...);
}
public function prepareAndExecute($query, $values = array(), $fetchMode = PDO::FETCH_ASSOC) {
// ...
}
public function lastInsertId($name = null) {
// ...
}
public function exec($query) {
// ...
}
}The significance of the interface will become more apparent later.
The DataMapper Interface and Concrete Class
The Data Mapper is the object that can be the "Factory" in the Repository pattern. More importantly, it knows how to translate your MySQL database schema to your domain object model. To promote decoupling your database schema from your object model, we first create an interface:
interface DataMapper {
public function toMatEducation(MatEducation $entity, $data);
}And then a concrete class for your MySQL database:
class MySqlDataMapper implements DataMapper {
public function toMatEducation(MatEducation $entity, $data) {
$entity->id = $data['id'];
$entity->eduId = $data['edu_id'];
$entity->degree = $data['edu_degree'];
$entity->institute = $data['edu_institute'];
return $entity;
}
}As with the Gateway interface, the significance of the DataMapper interface will become apparent in the next section.
The MatEducationRepository class
The repository class is where things are finally wired together. The repository class needs two other objects in order to do its job:
- The Gateway object
- The DataMapper object
Since both objects are implemented using interfaces, you can switch out database vendors and schemas without touching the code in MatEducationRepository.
```
class MatEducationRepository {
private $map;
private $gateway;
public MatEducationRepository(DataMapper $map = null, Gateway $gateway = null) {
$this->map = $map || new MySqlDataMapper();
$this->gateway = $gateway || new MySqlGateway();
}
public function add(MatEducation $entity) {
// Prepare and execute query using
Code Snippets
class MatEducation {
private $id;
private $eduId;
private $degree;
private $institute;
public __construct($userId, $eduId) {
$this->userId = $userId;
$this->eduId = $eduId;
}
// more methods
}interface Gateway {
public function prepareAndExecute($query, $values = array(), $fetchMode = PDO::FETCH_ASSOC);
public function lastInsertId($name = null);
public function exec($query);
}class MySqlGateway implements Gateway {
private $pdo;
public __construct($username = null, $password = null) {
// Get username and password from config, if null
$this->pdo = new PDO(...);
}
public function prepareAndExecute($query, $values = array(), $fetchMode = PDO::FETCH_ASSOC) {
// ...
}
public function lastInsertId($name = null) {
// ...
}
public function exec($query) {
// ...
}
}interface DataMapper {
public function toMatEducation(MatEducation $entity, $data);
}class MySqlDataMapper implements DataMapper {
public function toMatEducation(MatEducation $entity, $data) {
$entity->id = $data['id'];
$entity->eduId = $data['edu_id'];
$entity->degree = $data['edu_degree'];
$entity->institute = $data['edu_institute'];
return $entity;
}
}Context
StackExchange Code Review Q#104719, answer score: 2
Revisions (0)
No revisions yet.