I'm working on an ORM experiment, and I'm looking for ideas to help choose which path to take in it's development. I currently have an index showing how to use it, two interfaces, a database class, a table base class and two auto-generated classes (representing a database table).
The first on my list is to implement the Update method (Read and Delete work). I'm also thinking of extending the BaseTable with methods, so the restrictions from MySQL are automatically used. It also needs a database interface check, so the database class can be swapped, as long as it is implementing the IDatabase interface (similar to how the database checks for a valid IBaseTable interface).
Looking forward to your thoughts.
Here's the code so far:
index.php
<html>
<body>
<pre>
<?php
/**
* Interfaces.
*/
include 'IDatabase.interface.php';
include 'IBaseTable.interface.php';
/**
* Classes.
*/
include 'Database.class.php';
include 'BaseTable.class.php';
/**
* Auto-generated classes.
*/
include 'companies.class.php';
include 'users.class.php';
/**
* Connect to the database.
*/
$database = new Database('mysql:dbname=mydatabase;host=localhost', 'myuser', 'mypassword');
if (is_a($database, 'IDatabase'))
{
/**
* Create and read a users record into a users object.
*/
$user = $database->Create('users');
if (is_a($user, 'IBaseTable'))
{
if ($user->Read(3))
print_r($user);
}
/**
* Create and read a companies record into a companies object.
*/
$company = $database->Create('companies');
if (is_a($company, 'IBaseTable'))
{
if ($company->Read(1))
print_r($company);
}
}
?>
</pre>
</body>
</html>
IDatabase.interface.php
<?php
/**
* Interface IDatabase.
* Defines required functionality for the Database class.
*/
interface IDatabase
{
function Read($object, $id);
function Delete($object, $id);
}
?>
IBaseTable.interface.php
<?php
/**
* Interface IBaseTable.
* Defines required functionality for BaseTable classes.
*/
interface IBaseTable
{
function Read($id);
function Delete();
function Copy($object);
}
?>
Database.class.php
<?php
/**
* Class Database.
* Wrapper class for creating, reading and deleting table objects/records.
*/
class Database implements IDatabase
{
private $pdo;
/**
* Constructor
* Connects using PDO.
*/
public function __construct($dsn, $username, $password)
{
$this->pdo = new PDO($dsn, $username, $password);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
* Destructor.
*/
function __destruct()
{
unset($this->pdo);
}
/**
* Create returns a new table object, based on the class/table name (a factory).
*/
public function Create($tableName)
{
if (!$this->isValidClassOrObject($tableName))
return null;
/**
* Create/return a new object.
*/
$result = new $tableName();
$result->database = $this;
return $result;
}
/**
* Read copies a table record into a table object.
*/
public function Read($object, $id)
{
if (!$this->isValidClassOrObject($object))
return false;
/**
* Get the class name for $object.
*/
$reflector = new ReflectionClass($object);
$name = $reflector->getName();
/**
* Select a record.
*/
$query = "SELECT * FROM `{$name}` WHERE `id` = :id";
$statement = $this->pdo->prepare($query);
$statement->bindValue('id', $id, PDO::PARAM_INT);
$statement->execute();
$result = $statement->fetchObject($name);
$statement->closeCursor();
/**
* Copy the record into the current object.
*/
return $object->Copy($result);
}
/**
* Delete removes a table record, clears the table object.
*/
public function Delete($object, $id)
{
if (!$this->isValidClassOrObject($object))
return false;
/**
* Get the class name for $object.
*/
$reflector = new ReflectionClass($object);
$name = $reflector->getName();
/**
* Delete the record.
*/
$query = "DELETE FROM `{$name}` WHERE `id` = :id";
$statement = $this->pdo->prepare($query);
$statement->bindValue('id', $id, PDO::PARAM_INT);
$statement->execute();
/**
* Clear the current object.
*/
return $object->Copy(new $name());
}
/**
* isValidClassOrObject checks whether the passed class name or object
* implements the IBaseTable interface.
*/
private function isValidClassOrObject($classOrObject)
{
/**
* ReflectionClass is used to retrieve information about a class/object.
*/
$reflector = new ReflectionClass($classOrObject);
/**
* Get the class name.
*/
$className = $reflector->getName();
/**
* Get the implemented interfaces.
*/
$interfaces = $reflector->getInterfaceNames();
/**
* Check that class name is recognized (included),
* and that it implements the IBaseTable interface.
*/
if (empty($className) or !in_array('IBaseTable', $interfaces))
{
return false;
}
return true;
}
}
?>
BaseTable.class.php
<?php
/**
* Class BaseTable.
* Implements IBaseTable.
* Can read, delete and copy a table object/record.
*/
class BaseTable implements IBaseTable
{
/**
* Reference to a Database object.
*/
public $database;
/**
* Call the Read from the Database object.
*/
public function Read($id)
{
return $this->database->Read($this, $id);
}
/**
* Call the Delete from the Database object.
*/
public function Delete()
{
return $this->database->Delete($this, $this->id);
}
/**
* Copy the contents of $object into the current object ($this).
* Does not copy inherited properties.
*/
public function Copy($object)
{
$reflector = new ReflectionClass($this);
$reflectorCopy = new ReflectionClass($object);
if ($reflector->getName() == $reflectorCopy->getName())
{
$properties = array_filter(
$reflector->getProperties(ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PUBLIC),
function($prop) use($reflector)
{
return $prop->getDeclaringClass()->getName() == $reflector->getName();
}
);
foreach ($properties as $property)
{
$name = $property->name;
$this->$name = $object->$name;
}
return true;
}
return false;
}
}
?>
companies.class.php
<?php
/**
* Class companies.
* Defines properties for table companies.
* Auto-generated.
*/
class companies extends BaseTable
{
protected $id;
protected $name;
protected $website;
protected $email;
}
?>
users.class.php
<?php
/**
* Class users.
* Defines properties for table users.
* Auto-generated.
*/
class users extends BaseTable
{
protected $id;
protected $username;
protected $email;
protected $active;
protected $joined;
}
?>
MySQL dump
CREATE TABLE `companies` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`website` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
INSERT INTO `companies` VALUES ('1', 'comment included', null, null);
CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`fullname` varchar(255) DEFAULT NULL,
`email` varchar(255) NOT NULL,
`active` int(1) NOT NULL,
`joined` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `users` VALUES ('3', 'pritaeas', 'pritaeas', 'pritaeas@example.com', '1', '2013-08-31');