Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- require_once('interface.ActiveRecord.php');
- /**
- * ActiveRecordEngine implements ActiveRecord, IteratorAggregate, and
- * ArrayAccess. This means that all subclasses have CRUD ability, can be used
- * in a foreach loop, and can be indexed as if they were an array.
- *
- * It is important to note that classes which extend this one must have three
- * protected static variables named $columns, $table, and $primaryKey.
- * $columns contains the database columns that you to manage with the class.
- * $table is the name of the database table that is associated with the class.
- * $primaryKey is the column which is the primary key for the table.
- *
- * @author Levi Morrison <[email protected]>
- */
- abstract class ActiveRecordEngine implements ActiveRecord, IteratorAggregate, ArrayAccess {
- /**
- * @var boolean $createNew
- */
- protected $createNew = true;
- /**
- * @var array $properties
- */
- private $properties = array();
- /**
- * @var array $changed
- */
- private $changed = array();
- /**
- * @var PDO $connection
- */
- private static $connection = null;
- public function __construct(array $properties = null) {
- $class = get_class($this);
- foreach($class::$columns as &$name) {
- $this->properties[$name] = null;
- }
- if($properties!=null) {
- foreach($properties as $name=>&$value) {
- if($this->hasProperty($name) && $name!==$class::$primaryKey)
- $this->changed[$name] = $value;
- }
- }
- //does a case-insensitive sort on the array keys
- uksort($this->properties, "strnatcasecmp");
- uksort($this->changed, "strnatcasecmp");
- }
- /**
- * @param int $id
- * @return ActiveRecord A class that implements FslActiveRecord
- */
- public static function find($id) {
- try {
- $PDO = self::connection();
- $class = get_called_class();
- $statement = "SELECT " . implode(', ', $class::$columns) . ' FROM ' . $class::$table . ' WHERE '.$class::$primaryKey.'=:'.$class::$primaryKey;
- $PDOStatement = $PDO->prepare($statement);
- $PDOStatement->execute(array($class::$primaryKey=>$id));
- $obj = new $class();
- $obj->createNew = false;
- $PDOStatement->setFetchMode(PDO::FETCH_INTO, $obj);
- $PDOStatement->fetch();
- //copy, then clear out the changes made by PDO
- $obj->properties = $obj->changed;
- $obj->changed = array();
- return $obj;
- } catch(PDOException $error) {
- self::log($error);
- return null;
- }
- }
- /**
- * Deletes the record associated with $id from the database.
- * @param int $id
- * @return boolean Returns true if the data was successfully deleted.
- */
- public static function delete($id) {
- try {
- $PDO = self::connection();
- $PDO->beginTransaction();
- $class = get_called_class();
- $statement = 'DELETE FROM '.$class::$table.' WHERE '.$class::$primaryKey.'=:'.$class::$primaryKey;
- $PDOStatement = $PDO->prepare($statement);
- $PDOStatement->execute(array($class::$primaryKey=>$id));
- } catch(PDOException $error) {
- if($PDO instanceof PDO)
- $PDO->rollBack();
- return self::log($error) && false;
- }
- }
- /**
- * Saves the object to the database. If the object is a new instance, it
- * inserts a new record into the database. Calling farther saves on that
- * object results in updates, not new inserts.
- * If the object is not a new instance, it simply saves the data to the
- * database. For safety, you cannot update the primary key.
- *
- * @return boolean Returns true if the save was successful.
- */
- public function save() {
- return $this->createNew
- ? $this->insert()
- : $this->update();
- }
- /**
- * @return boolean If the data was inserted successfully, it returns true.
- */
- private function insert() {
- //if there's nothing to insert, this fails.
- if(empty($this->changed))
- return false;
- try {
- $PDO = self::connection();
- $PDO->beginTransaction();
- $class = get_class($this);
- $statement = 'INSERT INTO ' . $class::$table . ' (' . implode(', ', array_keys($this->changed)) . ') VALUES (';
- $i=0;
- foreach($this->changed as $name=>&$value) {
- if($i++>0) {
- $statement .= ', ';
- }
- $statement .= ":$name";
- }
- $statement .= ') WHERE '.$class::$primaryKey."= :".$class::$primaryKey;
- $PDOStatement = $PDO->prepare($statement);
- $parameters = array_merge(
- $this->changed,
- array(
- $class::$primaryKey=>$this->__get($class::$primaryKey)
- )
- );
- $PDOStatement->execute($parameters);
- echo"<pre>";
- print_r($class::find($PDO->lastInsertId()));
- echo"</pre>";
- } catch(PDOException $error) {
- if($PDO instanceof PDO)
- $PDO->rollBack();
- return self::log($error) && false;
- }
- }
- /**
- * Saves the object to the database. This implementation only updates
- * values which have been changed.
- * @return boolean If the data was updated successfully, it returns true.
- */
- private function update() {
- //if there's nothing to update, this worked, right?
- if(empty($this->changed))
- return true;
- try {
- $PDO = self::connection();
- $PDO->beginTransaction();
- $class = get_class($this);
- $statement = 'UPDATE ' . $class::$table . ' SET';
- $i=0;
- foreach($this->changed as $name=>&$value) {
- if($i++>0)
- $statement .= ',';
- $statement .= " $name=:$name";
- }
- $statement .= ' WHERE '.$class::$primaryKey.'= :'.$class::$primaryKey;
- $PDOStatment = $PDO->prepare($statement);
- $parameters = array_merge(
- $this->changed,
- array(
- $class::$primaryKey=>$this->__get($class::$primaryKey)
- )
- );
- $PDOStatment->execute($parameters);
- $PDO->commit();
- foreach($this->changed as $name=>&$value) {
- $this->properties[$name] = $value;
- }
- $this->changed = array();
- return true;
- } catch(PDOException $error) {
- if($PDO instanceof PDO)
- $PDO->rollBack();
- return self::log($error) && false;
- }
- }
- /**
- * Used to determine whether the class has a given property. Properties
- * are defined as the column names of the table the class represents.
- * @param string $name The name of the property you want to test for existence.
- * @return boolean Returns true if the property exists.
- */
- protected function hasProperty($name) {
- return array_key_exists($name, $this->properties);
- }
- /**
- * @param string $name
- * @return mixed
- * @throws DomainException if you try to get a property that doesn't exist.
- */
- public function __get($name) {
- if(!$this->hasProperty($name))
- throw new DomainException();
- return $this->properties[$name];
- }
- /**
- * @param string $name
- * @param mixed $value
- * @return void
- * @throws DomainException if you try to set a property that doesn't exist.
- */
- public function __set($name, $value) {
- if(!$this->hasProperty($name))
- throw new DomainException();
- $this->changed[$name] = $value;
- //$this->properties will contain the original value.
- }
- /**
- * @param string $name
- * @return boolean
- */
- public function __isset($name) {
- return isset($this->properties[$name]);
- }
- /**
- * You cannot unset properties
- * @param string $name
- * @return void
- * @throws UnsupportedOperationException
- */
- public function __unset($name) {
- throw new UnsupportedOperationException(
- 'You cannot unset properties of ' . get_class($this). '.'
- );
- }
- /**
- * Allows for easier error logging.
- * @param Exception $error
- * @return boolean
- */
- protected static function log(Exception $error) {
- return error_log(
- get_class($error) . ': ' .$error->getCode()
- . ', ' .$error->getMessage()
- . ' in ' . $error->getFile()
- . ' on line ' . $error->getLine()
- . ', stack trace: ' . $error->getTraceAsString()
- );
- }
- /**
- * Required for ArrayAccess
- * @param string $name The name of the property you'd like to test for existence.
- * @return boolean
- */
- public function offsetExists($name) {
- return $this->hasProperty($name);
- }
- /**
- * Required for ArrayAccess
- * @param string $name The name of the property you'd like to get.
- * @return mixed The value associated with that name.
- */
- public function offsetGet($name) {
- return $this->__get($name);
- }
- /**
- * Required for ArrayAccess
- * @param string $name The name of the property you'd like to set.
- * @param mixed $value
- */
- public function offsetSet($name, $value) {
- $this->__set($name, $value);
- }
- /**
- * Required for ArrayAccess. Unsupported.
- * @param string $name The name of the property you'd like to unset.
- * @throws UnsupportedOperationException
- */
- public function offsetUnset($name) {
- $this->__unset($name);
- }
- /**
- * Required for IteratorAggregate. Allows you to use any subclass to be
- * used in a foreach loop.
- * @return ArrayIterator
- */
- public function getIterator() {
- return new ArrayIterator($this->properties);
- }
- /**
- * @return PDO
- */
- protected static function connection() {
- if(!(self::$connection instanceof PDO)) {
- self::$connection = new PDO(
- "mysql:host=db;dbname=dbname",
- 'user',
- 'password',
- array(
- PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_PERSISTENT=>true
- )
- );
- }
- return self::$connection;
- }
- }
- ?>
Advertisement
Add Comment
Please, Sign In to add comment