12.27
php
saved
jacwright
Note
This was just done up yesterday and today, so still buggy. But I got inherited statics to work through a hack with back_trace(). Will still need to benchmark it to verify if it is worth it.
This was just done up yesterday and today, so still buggy. But I got inherited statics to work through a hack with back_trace(). Will still need to benchmark it to verify if it is worth it.
- <?php
- /**
- * Implementation of the ActiveRecord pattern. Uses the PDO database drivers
- * which come with PHP 5.1 and can be installed with PHP 5.0+
- *
- * This implementation of ActiveRecord determines object relationships on the
- * fly and caches the database metadata for optimization. Using the location
- * of foreign keys and the plurality of the lookup (e.g. $person->addresses or
- * $person->address) ActiveRecord can know how the two objects relate.
- *
- * In addition, this implementation of ActiveRecord keeps all non-model properties
- * out of the class (such as $table, $class, etc) in order to keep the object
- * clean for passing to other applications such as webservices or Flash remoting.
- *
- * The default database schema may be modified for different
- * database layouts. Variables which may be included in the strings are:
- * %table% %plural_table% %singular_table%
- *
- * Example of use:
- *
- * require("ActiveRecord.php");
- * ActiveRecord::$db = new PDO("mysql:host=localhost;dbname=mystickies", "root", "");
- *
- * class Tag extends ActiveRecord {}
- *
- * class Note extends ActiveRecord {}
- *
- * $n = Note::findFirst(array("user_id = ?", $_GET['user_id']));
- * foreach ($note->tags as $tag) {
- * echo $tag->name;
- * }
- *
- */
- abstract class ActiveRecord {
- // database layout rules
- public static $tableFormat = "under_score"; // camelBack, CamelBack (capitalized first letter), or under_score
- public static $modifiedField = "updated"; // the standard date field which is set each time the object is saved
- public static $creationField = "created"; // the standard date field which is set when object is first saved
- /**
- * PDO Database connection
- * @var PDO
- */
- public $id;
- /*****************************************************/
- /* METHODS TO BE OVERRIDDEN */
- /*****************************************************/
- public function preload() {
- }
- public function postload() {
- }
- public function presave() {
- }
- public function postsave() {
- }
- // stores the fields for each table (part of the cache
- protected static $tables;
- "date|time" => "date",
- "int|double|decimal|long|short" => "numeric",
- "string|blob",
- "string"
- );
- /**
- * Constructor of object
- *
- * @param int $id [Optional] If id is present, will load object
- */
- public function __construct($id = null) {
- $this->load($id);
- $this->setProperties($id);
- } else {
- $this->setProperties();
- }
- }
- /**
- * Loads the object from the database by the id passed
- *
- * @param int $id
- * @return boolean Whether the object was successfully loaded
- */
- public function load($id) {
- $pk = self::getPKName($tableName);
- $stmt = self::getStatement("SELECT * FROM $tableName WHERE $pk = ?");
- $stmt->setFetchMode(PDO::FETCH_ASSOC);
- if ($found) {
- $row = $stmt->fetch();
- $stmt->closeCursor();
- $this->setProperties($row);
- }
- return $found;
- }
- /**
- * Loads the object from the database by the conditions passed
- *
- * @param string $conditions Conditions to be passed
- * @param [mixed $properties...]
- * @return boolean Whether the object was succesfully loaded
- */
- public function loadBy($conditions) {
- $this->preload();
- $stmt = self::getStatement("SELECT * FROM $tableName WHERE $conditions");
- $stmt->setFetchMode(PDO::FETCH_ASSOC);
- $found = $stmt->execute($conditionsValues);
- if ($found) {
- $row = $stmt->fetch();
- $stmt->closeCursor();
- $this->setProperties($row);
- }
- $this->postload();
- return $found;
- }
- /**
- * Saves the object to the database.
- *
- * @return boolean Whather the object successfully saved
- */
- public function save() {
- $this->presave();
- $pk = self::getPKName($table);
- // set the automatic timestamps
- if ($this->hasProperty(self::$modifiedField))
- $props = $this->getProperties();
- $stmt = self::getStatement("UPDATE $table SET " .
- " = ? WHERE $pk = ?"
- );
- $prop_values[] = $this->id;
- } else {
- // TODO generate a new primary key here for databases without autoincrement
- $stmt = self::getStatement("INSERT INTO $table (" .
- ") VALUES (?" .
- ")"
- );
- }
- if (!$stmt->execute($prop_values)) {
- $error = $stmt->errorInfo();
- throw new Exception($error[2]);
- }
- $this->load(self::$db->lastInsertId());
- }
- $this->postsave();
- return $success;
- }
- public function hasProperty($propName) {
- }
- public function getProperties($join = "") {
- $pk = self::getPKName($table);
- $fields = self::getFields($table);
- if ($join) {
- $fields = self::getFields($join);
- }
- foreach ($fields as $field => $info) {
- if ($field == $pk) {
- continue;
- }
- $value = $this->$field;
- $value = $value ? "1" : "0";
- continue;
- }
- $properties[$field] = $value;
- }
- return $properties;
- }
- $this->preload();
- $pk = self::getPKName($table);
- $fields = self::getFields($table);
- if (!$fields) {
- return false;
- }
- foreach ($fields as $field => $info) {
- if ($field == $pk) {
- $this->id = $properties[$pk];
- $this->id = $properties["id"];
- continue;
- }
- $this->$field = null;
- continue;
- }
- $value = $properties[$field];
- if ($info->type == "date") {
- $value = $value ? true : false;
- }
- $this->$field = $value;
- }
- $this->postload();
- }
- /*****************************************************/
- /* STATIC METHODS */
- /*****************************************************/
- /**
- * Return object found based on id
- *
- * @param string $class The class of the object to load
- * @param int $id The id of the object in the database
- * @return Model Subclass object of type class
- */
- $class = self::getCaller();
- $pk = self::getPKName(self::getTableName($class));
- $result = self::findAll("$pk = $id", null, 1);
- return $result[0];
- }
- /**
- * Returns first object found based on parameters
- *
- * @param string $class The class of the object to load
- * @param string $conditions
- * @param string $order
- * @param string $joins
- * @return Model
- */
- $result = self::findAll($conditions, $order, 1, $joins);
- return $result[0];
- }
- /**
- * Returns array of objects based on parameters
- *
- * @param string $class The class of the objects to load
- * @param string $conditions
- * @param string $order
- * @param string $limit
- * @param string $joins
- * @return array
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $pk = self::getPKName($table);
- // get the fields cached in advance
- self::getFields($table);
- //sql (make sure we have the original table's pk last so if there are joins, they don't interfere
- $sql = "SELECT *, $table.$pk FROM $table";
- $stmt = self::executeSQL($sql, $conditions, $limit, $order, $joins);
- $i = 0;
- $num = 0;
- if ($limit) {
- $total = $limit[0];
- }
- while ($row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, $i++)) {
- $result[] = new $class($row);
- if (++$num == $total)
- break;
- }
- $stmt->closeCursor();
- return $result;
- }
- /**
- * Returns array of objects based on the full sql statement
- *
- * @param string $class The class of the objects to load
- * @param string $sql
- * @param array $params
- * @param string $limit
- * @return array
- */
- $class = self::getCaller();
- if (!$stmt->execute($params)) {
- $error = $stmt->errorInfo();
- throw new Exception($error[2]);
- }
- $i = 0;
- $num = 0;
- $total = 0;
- if ($limit) {
- $i = $limit[0];
- }
- while ($row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, $i++)) {
- $result[] = new $class($row);
- if (++$num == $total) break;
- }
- $stmt->closeCursor();
- return $result;
- }
- /**
- * Returns whether or not the object exists in the database
- *
- * @param string $class The class of the objects to load
- * @param int $id
- * @return boolean
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $pk = self::getPKName($table);
- }
- /**
- * Creates new object, populates the attributes from the array,
- * saves it if it validates, and returns it
- *
- * @param string $class The class of the object to create
- * @param array $properties
- * @return Model
- */
- $class = self::getCaller();
- $obj = new $class($properties);
- $obj->save();
- return $obj;
- }
- /**
- * Updates an object already stored in the database with the properties passed
- *
- * @param string $class The class of the object to update
- * @param int $id The id of the class in the database
- * @param string/array $properties
- * @return boolean Whether it was successfully updated
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $pk = self::getPKName($table);
- // the properties element should be the same format as conditions
- $properties = self::prepareConditions($properties);
- return $stmt->execute($properties);
- }
- /**
- * Updates all records with properties by conditions
- *
- * @param string $class The class of the objects to update
- * @param string $conditions
- * @param array $properties
- * @return int Number of successful updates
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $properties = self::prepareConditions($properties);
- $conditions = self::prepareConditions($conditions);
- $stmt = self::executeSQL($sql, array_merge(array_splice($conditions, 0, 1), $properties, $conditions));
- return $stmt->rowCount();
- }
- /**
- * Delete object by id
- *
- * @param string $class The class of the object to delete
- * @param int $id The id of the object in the database
- * @return boolean Whether object was deleted
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $pk = self::getPKName($table);
- $stmt = self::getStatement("DELETE FROM $table WHERE $pk = ?");
- return $stmt->execute($id);
- }
- /**
- * Deletes all records by conditions
- *
- * @param string $class The class of the objects to delete
- * @param string $conditions
- * @param string $limit
- * @param string $deleteFrom Tables to delete records from
- * @param string $joins Table joins needing to be added
- * @return int Number of successful deletes
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- if (!$deleteFrom) {
- $deleteFrom = $table;
- }
- //sql
- $sql = "DELETE $deleteFrom FROM $table";
- $stmt = self::executeSQL($sql, $conditions, null, null, $joins);
- return $stmt->rowCount();
- }
- /**
- * Returns the number of records that meet the conditions
- *
- * @param string $class
- * @param string $conditions
- * @param string $joins
- * @return int
- */
- $class = self::getCaller();
- $table = self::getTableName($class);
- $sql = "SELECT COUNT(*) FROM $table";
- $stmt = self::executeSQL($sql, $conditions, null, null, $joins);
- // return first row, first field
- $stmt->setFetchMode(PDO::FETCH_COLUMN, 0);
- $count = $stmt->fetch();
- $stmt->closeCursor();
- return $count;
- }
- /**
- * Returns the number of records returned by the sql statement
- *
- * @param string $class
- * @param string $sql
- * @param array $params
- * @return int
- */
- $class = self::getCaller();
- $stmt = self::getStatement($sql);
- if (!$stmt->execute($params)) {
- $error = $stmt->errorInfo();
- throw new Exception($error[2]);
- }
- // return first row, first field
- $stmt->setFetchMode(PDO::FETCH_COLUMN, 0);
- $count = $stmt->fetch();
- $stmt->closeCursor();
- return $count;
- }
- /**
- * Increment a property in a Model class
- *
- * @param string $class
- * @param int $id
- * @param string $counter The property of the class to be incremented
- */
- }
- /**
- * Decrements a counter in a record
- *
- * @param string $class
- * @param int $id
- * @param string $counter The property of the class to be decremented
- */
- }
- //TODO EVERYTHING BELOW THIS
- // MAGIC METHODS FOR MODEL ******************************************
- /**
- * Catches all methods called and forwards on certain types to their
- * defined method. e.g. $this->doSomething(10) will call
- * $this->_do("Something", 10) if the _do method is defined.
- *
- * @param string $func The name of the method
- * @param array $args The arguments passed to it
- * @return mixed The return value of the resolved method
- */
- protected function __call($func, $args) {
- $catchFunc = '_' . $matches[1];
- // push the property name to the front of the args array
- } else {
- }
- }
- /**
- * Catches all set actions to undefined properties and assumes they are related
- * objects. Tries to save related object or array of objects.
- *
- * @param string $property Name of unset property we are setting.
- * @return mixed The object or array of objects we are trying to set.
- */
- protected function __set($property, $value) {
- $this->_set($property, $value);
- }
- /**
- * Catches all get requests to undefined properties and assumes they are related
- * objects. Tries to find and load related object or array of objects.
- *
- * @param string $property Name of unset property we are getting.
- * @return mixed The object or array of objects we are trying to get (or null if not found).
- */
- protected function __get($property) {
- return $this->_get($property);
- }
- /**
- * Does automatic date conversion (to int) for database date properties and
- * save relational objects on the fly. Calls $this->loadProperty which
- * may be provided by subclass if custom loading is required
- *
- * @param string $prop
- * @param mixed $value
- * @param unknown_type $force
- */
- protected function _set($prop, $value) {
- $fields = self::getFields($table);
- }
- $this->$prop = $value;
- /*
- // $this->$prop could be set if _set is reached through $this->setProperty($value);
- if ((is_object($value) || is_array($value)) && !isset($this->$prop)) {
- $this->{"save" . ucfirst($prop)}();
- }*/
- }
- /**
- * Loads relational objects on the fly. Calls $this->loadProperty which
- * may be provided by subclass if custom loading is required
- *
- * @param string $property Property to get
- * @return mixed The value returned from this object
- */
- protected function _get($property) {
- }
- return $this->$property;
- }
- /**
- * Adds an object to an array and saves relational objects on the fly
- *
- * @param string $property
- * @param mixed $value
- */
- protected function _add($property, $value) {
- $property = Inflector::pluralize($property);
- }
- }
- /**
- * Removes an object from an array and removes the relationship of relational objects
- *
- * @param string $property
- * @param obj/int $objOrNum
- * @return mixed Returns the object removed from the array
- */
- protected function _remove($property, $objOrNum) {
- $property = Inflector::pluralize($property);
- }
- // TODO save object after removing it (save this or that object, depending on where Fkey is)
- return $removed[0];
- }
- foreach ($this->$property as $index => $tempObj) {
- if ($tempObj === $objOrNum) {
- return $removed[0];
- }
- }
- return false;
- }
- /**
- * Gives the size of an array property, loading relational objects on the fly
- *
- * @param string $property
- * @return int
- */
- protected function _sizeof($property) {
- }
- // Database automatic methods
- protected function _load($property, $conditions = null, $order = null, $limit = null) {
- return $this->$property;
- }
- return false;
- }