Diese Klasse ist lediglich inline-dokumentiert. Im Beitrag Prepared Statements Teil 3 wird die Klasse erklärt und man wird auch Beispiele für die Anwendung dort finden.
PHP:
<?php
/**
* ORM.php
*
* - Provides a basic class for accessing database using ORM technology.
*
* @author saftmeister
*
*/
class ORMException extends Exception {}
/**
* The OR-Mapper class
*
* Provides the basic database access functionality
*/
class ORM
{
private static $dbhost = '127.0.0.1';
private static $dbuser = 'test';
private static $dbpass = '';
private static $dbname = 'test';
protected $table;
private $pkCol = null;
private $db;
/**
* Set the credentials for the database access
*
* @param string $dbhost
* @param string $dbuser
* @param string $dbpass
* @param string $dbname
*/
public static function setCredentials($dbhost, $dbuser, $dbpass, $dbname)
{
self::$dbhost = $dbhost;
self::$dbname = $dbname;
self::$dbpass = $dbpass;
self::$dbuser = $dbuser;
}
/**
* Create a new instance of our ORM mapper
*
* @param string $table
* The database table name
* If not given, we use the class name of entity class in lower case
* @param PDO $db
* A valid database connection to fulfil a dependency injection request
* If not given, we create a connection inside for you
*
* @throws ORMException
*/
public function __construct($table = null, PDO $db = null)
{
try
{
$this->db = $db;
$this->table = $table;
$this->init ();
$this->initMeta ();
}
catch ( Exception $ex )
{
if ($ex instanceof ORMException)
{
throw $ex;
}
throw new ORMException ( $ex->getMessage (), $ex->getCode (), $ex );
}
}
/**
* Initialization of OR mapper
*
* @throws ORMException
*/
private function init()
{
if ($this->db)
return;
try
{
$this->db = new PDO ( sprintf ( "mysql:dbname=%s;host=%s", self::$dbname, self::$dbhost ), self::$dbuser, self::$dbpass );
$this->db->setAttribute ( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(Exception $ex)
{
throw new ORMException($ex->getMessage(), $ex->getCode(), $ex);
}
}
/**
* Initialize entity meta data
*
* @throws ORMException
*/
private function initMeta()
{
$this->init ();
if ($this->table == null)
{
$this->table = strtolower ( get_called_class () );
}
$metaStatement = $this->db->query ( sprintf('SELECT * FROM %s LIMIT 0', $this->table) );
for($i = 0; $i < $metaStatement->columnCount (); $i ++)
{
$meta = $metaStatement->getColumnMeta ( $i );
if (count ( $meta ['flags'] ))
{
if (array_search ( 'primary_key', $meta ['flags'] ))
{
$this->pkCol = $meta ['name'];
break;
}
}
}
if (! $this->pkCol)
{
throw new ORMException ( 'Could not find column which contains primary key!' );
}
}
/**
* Retrieve the name of column which contains the table primary key.
*
* @throws ORMException
* @return string
*/
protected function getPrimaryKeyColumn()
{
if (! $this->pkCol)
$this->initMeta ();
return $this->pkCol;
}
/**
* Retrieve the value of primary key for this entity
*
* @throws ORMException
* @return mixed
*/
protected function getPrimaryKeyValue()
{
$pk = $this->getPrimaryKeyColumn ();
return $this->getProperty ( $pk );
}
/**
* Evil hack to retrieve the properties from the derived class
*
* This method uses a hack to get class properties without the
* use of reflection to minimize the dependencies to php functionality.
*
* First a object dump is retrieved.
* Then we try to find all property names from the object dump by regular
* expression.
* Then we retrieve the object vars from the base class using get_object_class().
* Due to limitation the method get_object_class will return only properties
* of class ORM, which is the base class.
* We walk over all derived class properties to find match in base class. If
* found, we skip that property.
* After all walks we have only properties, which exists in the derived class.
*
* @return array All properties known in the derived class.
*/
private function getObjectProperties()
{
$s = var_export ( $this, true );
$propertyNames = array ();
$matches = array ();
preg_match_all ( '/\'([^\']+?)\' => .*/m', $s, $matches );
if (count ( $matches ) > 1)
{
array_shift ( $matches );
$baseClassProperties = get_object_vars ( $this );
foreach ( $matches [0] as $match )
{
if (! array_key_exists ( $match, $baseClassProperties ))
$propertyNames [] = $match;
}
}
return $propertyNames;
}
/**
* Retrieve a particular property value by calling getter of property
*
* @param string $propertyName
*
* @throws ORMException
* @return mixed
*/
private function getProperty($propertyName)
{
$getter = "get" . ucfirst ( $propertyName );
if (! method_exists ( $this, $getter ))
{
throw new ORMException ( "Could not find getter for property $propertyName" );
}
return $this->$getter ();
}
/**
* Set the given property to a particular value
*
* @param string $propertyName
* @param mixed $value
*
* @throws ORMException
*/
private function setProperty($propertyName, $value)
{
$setter = "set" . ucfirst ( $propertyName );
if (! method_exists ( $this, $setter ))
{
throw new ORMException ( "Could not find setter for property $propertyName" );
}
$this->$setter ( $value );
}
/**
* Create an ORM instance for internal use
*
* @throws ORMException
* @return ORM
*/
private static function create()
{
$concrete = get_called_class ();
$orm = new self ( strtolower ( $concrete ) );
return $orm;
}
/**
* Retrieve a particular entry by given id
*
* @throws ORMException
* @param mixed $id
* @return mixed
*/
public static function get($id)
{
try
{
$orm = self::create ();
$query = sprintf("SELECT * FROM %s WHERE %s = :pk", $orm->table, $orm->getPrimaryKeyColumn ());
$statement = $orm->db->prepare ( $query );
$statement->setFetchMode ( PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, $concrete );
$statement->bindValue ( ":pk", $id );
$statement->execute ();
$result = $statement->fetch ( PDO::FETCH_CLASS );
if ($result)
{
$result->table = $orm->table;
$result->pkCol = $orm->getPrimaryKeyColumn ();
$result->db = $orm->db;
}
}
catch ( Exception $ex )
{
if ($ex instanceof ORMException)
{
throw $ex;
}
throw new ORMException ( $ex->getMessage (), $ex->getCode (), $ex );
}
return $result;
}
/**
* Find a particular entry by passing specific criteria
*
* @param array $criteria
* @throws ORMException
* @return Ambigous <boolean, unknown, mixed>
*/
public static function find(array $criteria)
{
$orm = self::create ();
try
{
$concrete = get_called_class ();
$wheres = "";
$values = array ();
if (count ( $criteria ))
{
foreach ( $criteria as $column => $value )
{
$wheres .= ($wheres ? ' AND ' : ' WHERE ') . "$column = ?";
$values [] = $value;
}
}
$query = sprintf("SELECT * FROM %s %s", $orm->table, $wheres);
$statement = $orm->db->prepare ( $query );
$statement->setFetchMode ( PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, $concrete );
$statement->execute ( $values );
$results = false;
while ( $result = $statement->fetch ( PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE ) )
{
$results [] = $result;
}
if ($results === false)
{
throw new ORMException ( "No element found!" );
}
for($i = 0; $i < count ( $results ); $i ++)
{
$results [$i]->pkCol = $orm->getPrimaryKeyColumn ();
$results [$i]->table = $orm->table;
$results [$i]->db = $orm->db;
}
if (count ( $results ) == 1)
{
$results = $results [0];
}
}
catch ( Exception $ex )
{
if ($ex instanceof ORMException)
{
throw $ex;
}
throw new ORMException ( $ex->getMessage (), $ex->getCode (), $ex );
}
return $results;
}
/**
* Save the current entity
*
* If entity does not exist, it will be inserted.
* Otherwise it will be updated.
*
* @throws ORMException
*/
public function persist()
{
try
{
$pk = $this->getPrimaryKeyColumn ();
$where = "";
$query = "";
if ($pk)
{
if ($this->getProperty ( $pk ))
{
$where = "WHERE " . $pk . " = :pk";
$query = "UPDATE " . $this->table . " SET ";
$values [':pk'] = $this->getPrimaryKeyValue ();
}
else
{
$query = "INSERT INTO " . $this->table . " SET ";
}
}
else
{
throw new ORMException ( "Did not find any primary key identifier for updating!" );
}
$sets = "";
foreach ( $this->getObjectProperties () as $propertyName )
{
$sets .= ($sets ? ", " : "") . $propertyName . " = :" . $propertyName . " ";
$values [':' . $propertyName] = $this->getProperty ( $propertyName );
}
$query .= $sets . $where;
$statement = $this->db->prepare ( $query );
$statement->execute ( $values );
if (! $this->getProperty ( $pk ))
{
$this->setProperty ( $pk, $this->db->lastInsertId () );
}
}
catch ( Exception $ex )
{
if ($ex instanceof ORMException)
{
throw $ex;
}
throw new ORMException ( $ex->getMessage (), $ex->getCode (), $ex );
}
}
/**
* Remove particular entry by primary key
*
* @throws ORMException
*/
public function remove()
{
try
{
$pk = $this->getPrimaryKeyColumn ();
$query = sprintf("DELETE FROM %s WHERE %s = :%s", $this->table, $pk, $pk);
$statement = $this->db->prepare ( $query );
$statement->bindValue ( ':' . $pk, $this->getProperty ( $pk ) );
$statement->execute ();
}
catch ( Exception $ex )
{
if($ex instanceof ORMException)
{
throw $ex;
}
throw new ORMException ( $ex->getMessage (), $ex->getCode (), $ex );
}
}
}