diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/database/classes/database/expression.php | 46 | ||||
-rw-r--r-- | modules/database/classes/database/query.php | 818 | ||||
-rw-r--r-- | modules/database/classes/database/result.php | 200 | ||||
-rw-r--r-- | modules/database/classes/db.php | 252 | ||||
-rw-r--r-- | modules/database/classes/driver/mysql/db.php | 214 | ||||
-rw-r--r-- | modules/database/classes/driver/mysql/query.php | 5 | ||||
-rw-r--r-- | modules/database/classes/driver/mysql/result.php | 90 | ||||
-rw-r--r-- | modules/database/classes/driver/pdo/db.php | 228 | ||||
-rw-r--r-- | modules/database/classes/driver/pdo/query.php | 443 | ||||
-rw-r--r-- | modules/database/classes/driver/pdo/result.php | 90 | ||||
-rw-r--r-- | modules/orm/classes/orm.php | 1575 | ||||
-rw-r--r-- | modules/orm/classes/orm/result.php | 412 |
12 files changed, 2192 insertions, 2181 deletions
diff --git a/modules/database/classes/database/expression.php b/modules/database/classes/database/expression.php index a041967..1d4de19 100644 --- a/modules/database/classes/database/expression.php +++ b/modules/database/classes/database/expression.php @@ -7,27 +7,29 @@ */
class Expression_Database
{
- /**
- * Part of query that should not be escaped
- * @var mixed
- * @access public
- */
- public $value;
- /**
- * Marks a part of query as a database specific expression,
- * e.g. calls to SQL functions like MAX(), SUBSTR() etc.
- * Example
- * <code>
- * $q->fields(DB::expr('COUNT(*)'));
- * </code>
- *
- * @param mixed $value Part of query that should not be escaped
- * @return Expression_Database
- * @access public
- */
- public function __construct($value)
- {
- $this->value = $value;
- }
+ /**
+ * Part of query that should not be escaped
+ * @var mixed
+ * @access public
+ */
+ public $value;
+
+ /**
+ * Marks a part of query as a database specific expression,
+ * e.g. calls to SQL functions like MAX(), SUBSTR() etc.
+ * Example
+ * <code>
+ * $q->fields(DB::expr('COUNT(*)'));
+ * </code>
+ *
+ * @param mixed $value Part of query that should not be escaped
+ * @return Expression_Database
+ * @access public
+ */
+ public function __construct($value)
+ {
+ $this->value = $value;
+ }
+
}
diff --git a/modules/database/classes/database/query.php b/modules/database/classes/database/query.php index 23b9876..e83a4bd 100644 --- a/modules/database/classes/database/query.php +++ b/modules/database/classes/database/query.php @@ -5,7 +5,7 @@ * Database drivers extend this class so that they can generate database specific queries. * The idea is to provide a database agnostic interface to query writing. * - * @method mixed table(string $table = null) Set table to query. + * @method mixed table(string $table = null) Set table to query. * Without arguments returns current table, returns self otherwise. * * @method mixed data(array $data = null) Set data for insert or update queries. @@ -29,103 +29,104 @@ */ abstract class Query_Database { - /** - * Array of conditions that rows must meet - * @var array - * @access protected - */ - protected $_conditions = array(); - - /** - * Table to query - * @var unknown - * @access protected - */ - protected $_table; - - /** - * Fields to return in the query - * @var array - * @access protected - */ - protected $_fields; - - /** - * Data for row insertion or update - * @var unknown - * @access protected - */ - protected $_data; - - /** - * Query type. Available types: select, update, insert, delete, count - * @var string - * @access protected - */ - protected $_type; - - /** - * Parameters for tables to join - * @var array - * @access protected - */ - protected $_joins = array(); - - /** - * Number of rows to return - * @var int - * @access protected - */ - protected $_limit; - - /** - * Offset of the first row - * @var int - * @access protected - */ - protected $_offset; - - /** - * Columns and directions to order by - * @var array - * @access protected - */ - protected $_orderby = array(); - - /** - * Database connection - * @var DB - * @access protected - */ - protected $_db; - - /** - * Conditions for aggregator functions - * @var array - * @access protected - */ - protected $_having = array(); - - /** - * Column to group by for aggregator functions - * @var string - * @access protected - */ - protected $_group_by; - - /** - * Last alias used on the table - * @var string - * @access protected - */ - protected $_alias = null; - - /** - * Methods and type of value they allow that are available via __call - * @var array - * @access protected - */ - protected $methods = array('data' => 'array','limit' => array('integer','NULL'),'offset' => array('integer','NULL'),'group_by' => array('string','NULL'),'type' => 'string'); + + /** + * Array of conditions that rows must meet + * @var array + * @access protected + */ + protected $_conditions = array(); + + /** + * Table to query + * @var unknown + * @access protected + */ + protected $_table; + + /** + * Fields to return in the query + * @var array + * @access protected + */ + protected $_fields; + + /** + * Data for row insertion or update + * @var unknown + * @access protected + */ + protected $_data; + + /** + * Query type. Available types: select, update, insert, delete, count + * @var string + * @access protected + */ + protected $_type; + + /** + * Parameters for tables to join + * @var array + * @access protected + */ + protected $_joins = array(); + + /** + * Number of rows to return + * @var int + * @access protected + */ + protected $_limit; + + /** + * Offset of the first row + * @var int + * @access protected + */ + protected $_offset; + + /** + * Columns and directions to order by + * @var array + * @access protected + */ + protected $_orderby = array(); + + /** + * Database connection + * @var DB + * @access protected + */ + protected $_db; + + /** + * Conditions for aggregator functions + * @var array + * @access protected + */ + protected $_having = array(); + + /** + * Column to group by for aggregator functions + * @var string + * @access protected + */ + protected $_group_by; + + /** + * Last alias used on the table + * @var string + * @access protected + */ + protected $_alias = null; + + /** + * Methods and type of value they allow that are available via __call + * @var array + * @access protected + */ + protected $methods = array('data' => 'array', 'limit' => array('integer', 'NULL'), 'offset' => array('integer', 'NULL'), 'group_by' => array('string', 'NULL'), 'type' => 'string'); /** * UNION queries @@ -133,323 +134,326 @@ abstract class Query_Database * @access protected */ protected $_union = array(); - - /** - * Generates a query in format that can be executed on current database implementation - * - * @access public - */ - public abstract function query(); - - /** - * Creates a new query - * - * @param DB $db Database connection - * @param string $type Query type. Available types: select, update, insert, delete, count - * @return void - * @access public - */ - public function __construct($db, $type) - { - $this->_db = $db; - $this->_type = $type; - } - - /** - * Sets fields to be queried from the database. You can add aliases to the fields - * by passing them as: - * - * array('field_name','alias') - * - * Example: $query->fields('id', array('name', 'fairy_name')) - * - * @param mixed $field,... Fields to be selected from the table - * @return mixed If no parameters are passed returns current array of fields, - * otherwise returns self. - * @access public - */ - public function fields() - { - $p = func_get_args(); - if (empty($p)) - { - return $this->_fields; - } - else - { - $this->_fields=$p; - } - return $this; - } /** - * Sets the table to perform operations on, also supports subqueries - * + * Generates a query in format that can be executed on current database implementation + * + * @access public + */ + public abstract function query(); + + /** + * Creates a new query + * + * @param DB $db Database connection + * @param string $type Query type. Available types: select, update, insert, delete, count + * @return void + * @access public + */ + public function __construct($db, $type) + { + $this->_db = $db; + $this->_type = $type; + } + + /** + * Sets fields to be queried from the database. You can add aliases to the fields + * by passing them as: + * + * array('field_name','alias') + * + * Example: $query->fields('id', array('name', 'fairy_name')) + * + * @param mixed $field,... Fields to be selected from the table + * @return mixed If no parameters are passed returns current array of fields, + * otherwise returns self. + * @access public + */ + public function fields() + { + $p = func_get_args(); + if (empty($p)) + { + return $this->_fields; + } + else + { + $this->_fields = $p; + } + return $this; + } + + /** + * Sets the table to perform operations on, also supports subqueries + * * @param string|Query_database|Expression_database $table table to select from * @param string $alias Alias for this table - * @return mixed Returns self if a table is passed, otherwise returns the table - * @access public - */ - public function table($table=null,$alias=null) - { + * @return mixed Returns self if a table is passed, otherwise returns the table + * @access public + */ + public function table($table = null, $alias = null) + { if ($table == null) - { - return is_array($this->_table)?$this->_table[1]:$this->_table; - } - - if (!is_string($table) && $alias==null) - { + { + return is_array($this->_table) ? $this->_table[1] : $this->_table; + } + + if (!is_string($table) && $alias == null) + { $alias = $this->add_alias(); - } - $this->_table = $alias == null?$table:array($table, $alias); - + } + $this->_table = $alias == null ? $table : array($table, $alias); + return $this; } + # - /** - * Magic methods to create methods for all generic query parts - * - * @param string $method Name of the method to call - * @param array $args Array of parameters - * @return mixed If no arguments are passed returns the current value of the property, - * otherwise returns self. - * @access public - * @throws Exception If method doesn't exist - * @throws Exception If value is of incorrect type - * @see $methods - */ - public function __call($method, $args) - { - if (isset($this->methods[$method])) - { - $property = '_'.$method; - - if (empty($args)) - { - return $this->$property; - } - $val = $args[0]; - if (is_numeric($val)) - { - $val = (int) $val; - } - $allowed_types = $this->methods[$method]; - if (!is_array($allowed_types)) - { - $allowed_types=array($allowed_types); - } - if (!in_array(gettype($val), $allowed_types)) - { - throw new Exception("Method '{$method}' only accepts values of type: ".implode(' or ',$allowed_types).", '{$val}' was passed"); - } - $this->$property = $val; - return $this; - } - throw new Exception("Method '{$method}' doesn't exist."); - } - - /** - * Executes the query - * - * @return object Executes current query on its database connection - * @access public - * @see DB - */ - public function execute() - { - $query = $this->query(); - $result = $this->_db->execute($query[0], $query[1]); - if ($this->_type == 'count') - { - return $result->get('count'); - } - return $result; - } - - /** - * Adds a joined table to the query. - * - * @param string $table Table to join - * @param array $conds Conditions to join tables on, same behavior as with where() method - * @param string $type Type of join. Defaults to 'left' - * @return Query_Database Returns self - * @access public - * @see where() - */ - public function join($table,$conds,$type='left') - { - $this->_joins[] = array($table, $type, $this->get_condition_part($conds)); - return $this; - } - - /** - * Sets conditions for aggregate functions, same behavior as with where() method - * - * @return Query_Database Returns self - * @access public - * @see where() - */ - public function having() - { - $p = func_get_args(); - $cond = $this->get_condition_part($p); - $this->_having = array_merge($this->_having, array($cond)); - return $this; - } - - /** - * Adds a column to ordering parameters. - * - * @param string $column Column to order by - * @param string $dir Ordering direction. - * @return Query_Database Returns self - * @throws Exception If ordering direction isn't DESC or ASC - * @access public - */ - public function order_by($column, $dir) - { - $dir = strtoupper($dir); - if ($dir != 'DESC' && $dir != 'ASC') - { - throw new Exception("Invalid sorting direction {$dir} specified"); - } - $this->_orderby[] = array($column,$dir); - return $this; - } - - /** - * Sets conditions for the query. - * Can be called in many ways, examples: - * Shorthand equals condition: - * <code> - * $q->where('name', 'Tinkerbell') - * </code> - * Conditions with operator: - * <code> - * $q->where('id', '>', 3) - * </code> - * OR logic: - * <code> - * $q->where('or', array('name', 'Tinkerbell')) - * </code> - * OR logic with operator - * <code> - * ->where('or', array('id', '>', 3)) - * </code> - * Arrays represent brackets. e.g - * <code> - * $q->where('name', 'Tinkerbell') - * ->where('or', array( - * array('id', '>', 7), - * array('id', '<', 15) - * ); - * //Will produce "WHERE `name`='Tinkerbell' OR (`id` > 7 AND `id` < 15)" - * </code> - * Multiple calls to where() append new conditions to previous ones - * - * @param mixed $column Column name, logic parameter 'OR' or 'AND' or an array of conditions - * @param mixed $operator Condition value, operator or an array of parameters - * @param mixed $val Condition value - * - * @return Query_Database Returns self - * @access public - */ - public function where() - { - $p = func_get_args(); - $cond = $this->get_condition_part($p); - $this->_conditions = array_merge($this->_conditions, array($cond)); - - return $this; - } - - /** - * Recursively builds condition arrays for methods like where(), having() - * - * @param array $p Parameters passed to the method - * @return array Array in condition format - * @access private - * @throws Exception If condition format is incorrect - */ - private function get_condition_part($p) - { - if (is_string($p[0]) && (strtolower($p[0]) == 'or' || strtolower($p[0]) == 'and') && isset($p[1]) && is_array($p[1])) - { - $cond = $this->get_condition_part($p[1]); - $cond['logic'] = strtolower($p[0]); - return $cond; - } - - if (is_array($p[0])) - { - if (count($p) == 1) - { - return $this->get_condition_part($p[0]); - } - $conds = array(); - foreach($p as $q) - { - $conds[] = $this->get_condition_part($q); - } - if (count($conds) == 1) - { - return $conds; - } - return array('logic' => 'and', 'conditions' => $conds); - } - - if ((is_string($p[0]) || get_class($p[0]) == 'Expression_Database') && isset($p[1])) - { - if (strpos($p[0], '.') === false) - { - $p[0]=$this->last_alias().'.'.$p[0]; - } - return array( - 'logic' => 'and', - 'conditions' => array( - 'field' => $p[0], - 'operator' => isset($p[2]) ? $p[1] : '=', - 'value' => isset($p[2]) ? $p[2] : $p[1] - ) - ); - } - - throw new Exception('Incorrect conditional statement passed'); - } - - /** - * Gets last generated alias - * - * @return string Last generated alias. If no alias were created returns table name. - * @access public - */ - public function last_alias() - { - if ($this->_alias === null) - { - return $this->_table; - } - return 'a'.$this->_alias; - } - - /** - * Generates new alias. Useful for dynamically adding aliases to joins. - * Alias is just a letter 'a' with an incremented number. - * - * @return string New alias - * @access public - */ - public function add_alias() - { - if ($this->_alias === null) - { - $this->_alias = 0; - } - else - { - $this->_alias++; - } - return $this->last_alias(); - } + /** + * Magic methods to create methods for all generic query parts + * + * @param string $method Name of the method to call + * @param array $args Array of parameters + * @return mixed If no arguments are passed returns the current value of the property, + * otherwise returns self. + * @access public + * @throws Exception If method doesn't exist + * @throws Exception If value is of incorrect type + * @see $methods + */ + + public function __call($method, $args) + { + if (isset($this->methods[$method])) + { + $property = '_'.$method; + + if (empty($args)) + { + return $this->$property; + } + $val = $args[0]; + if (is_numeric($val)) + { + $val = (int) $val; + } + $allowed_types = $this->methods[$method]; + if (!is_array($allowed_types)) + { + $allowed_types = array($allowed_types); + } + if (!in_array(gettype($val), $allowed_types)) + { + throw new Exception("Method '{$method}' only accepts values of type: ".implode(' or ', $allowed_types).", '{$val}' was passed"); + } + $this->$property = $val; + return $this; + } + throw new Exception("Method '{$method}' doesn't exist."); + } + + /** + * Executes the query + * + * @return object Executes current query on its database connection + * @access public + * @see DB + */ + public function execute() + { + $query = $this->query(); + $result = $this->_db->execute($query[0], $query[1]); + if ($this->_type == 'count') + { + return $result->get('count'); + } + return $result; + } + + /** + * Adds a joined table to the query. + * + * @param string $table Table to join + * @param array $conds Conditions to join tables on, same behavior as with where() method + * @param string $type Type of join. Defaults to 'left' + * @return Query_Database Returns self + * @access public + * @see where() + */ + public function join($table, $conds, $type = 'left') + { + $this->_joins[] = array($table, $type, $this->get_condition_part($conds)); + return $this; + } + + /** + * Sets conditions for aggregate functions, same behavior as with where() method + * + * @return Query_Database Returns self + * @access public + * @see where() + */ + public function having() + { + $p = func_get_args(); + $cond = $this->get_condition_part($p); + $this->_having = array_merge($this->_having, array($cond)); + return $this; + } + + /** + * Adds a column to ordering parameters. + * + * @param string $column Column to order by + * @param string $dir Ordering direction. + * @return Query_Database Returns self + * @throws Exception If ordering direction isn't DESC or ASC + * @access public + */ + public function order_by($column, $dir) + { + $dir = strtoupper($dir); + if ($dir != 'DESC' && $dir != 'ASC') + { + throw new Exception("Invalid sorting direction {$dir} specified"); + } + $this->_orderby[] = array($column, $dir); + return $this; + } + + /** + * Sets conditions for the query. + * Can be called in many ways, examples: + * Shorthand equals condition: + * <code> + * $q->where('name', 'Tinkerbell') + * </code> + * Conditions with operator: + * <code> + * $q->where('id', '>', 3) + * </code> + * OR logic: + * <code> + * $q->where('or', array('name', 'Tinkerbell')) + * </code> + * OR logic with operator + * <code> + * ->where('or', array('id', '>', 3)) + * </code> + * Arrays represent brackets. e.g + * <code> + * $q->where('name', 'Tinkerbell') + * ->where('or', array( + * array('id', '>', 7), + * array('id', '<', 15) + * ); + * //Will produce "WHERE `name`='Tinkerbell' OR (`id` > 7 AND `id` < 15)" + * </code> + * Multiple calls to where() append new conditions to previous ones + * + * @param mixed $column Column name, logic parameter 'OR' or 'AND' or an array of conditions + * @param mixed $operator Condition value, operator or an array of parameters + * @param mixed $val Condition value + * + * @return Query_Database Returns self + * @access public + */ + public function where() + { + $p = func_get_args(); + $cond = $this->get_condition_part($p); + $this->_conditions = array_merge($this->_conditions, array($cond)); + + return $this; + } + + /** + * Recursively builds condition arrays for methods like where(), having() + * + * @param array $p Parameters passed to the method + * @return array Array in condition format + * @access private + * @throws Exception If condition format is incorrect + */ + private function get_condition_part($p) + { + if (is_string($p[0]) && (strtolower($p[0]) == 'or' || strtolower($p[0]) == 'and') && isset($p[1]) && is_array($p[1])) + { + $cond = $this->get_condition_part($p[1]); + $cond['logic'] = strtolower($p[0]); + return $cond; + } + + if (is_array($p[0])) + { + if (count($p) == 1) + { + return $this->get_condition_part($p[0]); + } + $conds = array(); + foreach ($p as $q) + { + $conds[] = $this->get_condition_part($q); + } + if (count($conds) == 1) + { + return $conds; + } + return array('logic' => 'and', 'conditions' => $conds); + } + + if ((is_string($p[0]) || get_class($p[0]) == 'Expression_Database') && isset($p[1])) + { + if (strpos($p[0], '.') === false) + { + $p[0] = $this->last_alias().'.'.$p[0]; + } + return array( + 'logic' => 'and', + 'conditions' => array( + 'field' => $p[0], + 'operator' => isset($p[2]) ? $p[1] : '=', + 'value' => isset($p[2]) ? $p[2] : $p[1] + ) + ); + } + + throw new Exception('Incorrect conditional statement passed'); + } + + /** + * Gets last generated alias + * + * @return string Last generated alias. If no alias were created returns table name. + * @access public + */ + public function last_alias() + { + if ($this->_alias === null) + { + return $this->_table; + } + return 'a'.$this->_alias; + } + + /** + * Generates new alias. Useful for dynamically adding aliases to joins. + * Alias is just a letter 'a' with an incremented number. + * + * @return string New alias + * @access public + */ + public function add_alias() + { + if ($this->_alias === null) + { + $this->_alias = 0; + } + else + { + $this->_alias++; + } + return $this->last_alias(); + } + } diff --git a/modules/database/classes/database/result.php b/modules/database/classes/database/result.php index 1c6b9fe..db58119 100644 --- a/modules/database/classes/database/result.php +++ b/modules/database/classes/database/result.php @@ -8,114 +8,114 @@ abstract class Result_Database implements Iterator { - /** - * Current row number - * @var integer - * @access protected - */ - protected $_position = -1; + /** + * Current row number + * @var integer + * @access protected + */ + protected $_position = -1; - /** - * Database result object - * @var mixed - * @access protected - */ - protected $_result; + /** + * Database result object + * @var mixed + * @access protected + */ + protected $_result; - /** - * Current row - * @var object - * @access protected - */ - protected $_row; + /** + * Current row + * @var object + * @access protected + */ + protected $_row; - /** - * If at least one row has been fetched - * @var object - * @access protected - */ - protected $_fetched = false; + /** + * If at least one row has been fetched + * @var object + * @access protected + */ + protected $_fetched = false; - /** - * Returns current row - * - * @return object Current row in result set - * @access public - */ - public function current() - { - $this->check_fetched(); - return $this->_row; - } + /** + * Returns current row + * + * @return object Current row in result set + * @access public + */ + public function current() + { + $this->check_fetched(); + return $this->_row; + } - /** - * Gets the number of the current row - * - * @return integer Row number - * @access public - */ - public function key() - { - $this->check_fetched(); - return $this->_position; - } + /** + * Gets the number of the current row + * + * @return integer Row number + * @access public + */ + public function key() + { + $this->check_fetched(); + return $this->_position; + } - /** - * Check if current row exists. - * - * @return bool True if row exists - * @access public - */ - public function valid() - { - $this->check_fetched(); - return $this->_row != null; - } + /** + * Check if current row exists. + * + * @return bool True if row exists + * @access public + */ + public function valid() + { + $this->check_fetched(); + return $this->_row != null; + } - /** - * Returns all rows as array - * - * @return array Array of rows - * @access public - */ - public function as_array() - { - $arr = array(); - foreach ($this as $row) - { - $arr[] = $row; - } - return $arr; - } + /** + * Returns all rows as array + * + * @return array Array of rows + * @access public + */ + public function as_array() + { + $arr = array(); + foreach ($this as $row) + { + $arr[] = $row; + } + return $arr; + } - /** - * Checks if the rows from the result set have - * been fetched at least once. If not fetches first row. - * - * @access public - */ - protected function check_fetched() - { - if (!$this->_fetched) - { - $this->_fetched = true; - $this->next(); - } - } + /** + * Checks if the rows from the result set have + * been fetched at least once. If not fetches first row. + * + * @access public + */ + protected function check_fetched() + { + if (!$this->_fetched) + { + $this->_fetched = true; + $this->next(); + } + } - /** - * Gets a column from the current row in the set - * - * @param string $column Column name - * @return mixed Column value - * @access public - */ - public function get($column) - { - if ($this->valid() && isset($this->_row->$column)) - { - return $this->_row->$column; - } - } + /** + * Gets a column from the current row in the set + * + * @param string $column Column name + * @return mixed Column value + * @access public + */ + public function get($column) + { + if ($this->valid() && isset($this->_row->$column)) + { + return $this->_row->$column; + } + } }
\ No newline at end of file diff --git a/modules/database/classes/db.php b/modules/database/classes/db.php index 4979450..ee6d779 100644 --- a/modules/database/classes/db.php +++ b/modules/database/classes/db.php @@ -9,140 +9,140 @@ abstract class DB { - /** - * An associative array of connections to databases - * @var array - * @access private - * @static - */ - private static $_instances = array(); + /** + * An associative array of connections to databases + * @var array + * @access private + * @static + */ + private static $_instances = array(); - /** - * Executes a prepared statement query - * - * @param string $query A prepared statement query - * @param array $params Parameters for the query - * @return Result_Database - * @access public - * @see Result_Database - */ - public abstract function execute($query, $params = array()); + /** + * Executes a prepared statement query + * + * @param string $query A prepared statement query + * @param array $params Parameters for the query + * @return Result_Database + * @access public + * @see Result_Database + */ + public abstract function execute($query, $params = array()); - /** - * Builds a new Query to the database - * - * @param string $type Query type. Available types: select, update, insert, delete, count - * @return Result_Database - * @access public - * @see Query_Database - */ - public abstract function build_query($type); + /** + * Builds a new Query to the database + * + * @param string $type Query type. Available types: select, update, insert, delete, count + * @return Result_Database + * @access public + * @see Query_Database + */ + public abstract function build_query($type); - /** - * Gets the id of the last inserted row. - * - * @return mixed The id of the last inserted row - * @access public - */ - public abstract function get_insert_id(); + /** + * Gets the id of the last inserted row. + * + * @return mixed The id of the last inserted row + * @access public + */ + public abstract function get_insert_id(); - /** - * Gets column names for the specified table - * - * @param string $table Name of the table to get columns from - * @return array Array of column names - * @access public - */ - public abstract function list_columns($table); + /** + * Gets column names for the specified table + * + * @param string $table Name of the table to get columns from + * @return array Array of column names + * @access public + */ + public abstract function list_columns($table); - /** - * Executes a named query where parameters are passed as an associative array - * Example: - * <code> - * $result=$db->namedQuery("SELECT * FROM fairies where name = :name",array('name'=>'Tinkerbell')); - * </code> - * - * @param string $query A named query - * @param array $params Associative array of parameters - * @return Result_Database Current drivers implementation of Result_Database - * @access public - */ - public function named_query($query, $params = array()) - { - $bind = array(); - preg_match_all('#:(\w+)#is', $query, $matches, PREG_SET_ORDER); - foreach ($matches as $match) - { - if (isset($params[$match[1]])) - { - $query = preg_replace("#{$match[0]}#", '?', $query, 1); - $bind[] = $params[$match[1]]; - } - } - return $this->execute($query, $bind); - } + /** + * Executes a named query where parameters are passed as an associative array + * Example: + * <code> + * $result=$db->namedQuery("SELECT * FROM fairies where name = :name",array('name'=>'Tinkerbell')); + * </code> + * + * @param string $query A named query + * @param array $params Associative array of parameters + * @return Result_Database Current drivers implementation of Result_Database + * @access public + */ + public function named_query($query, $params = array()) + { + $bind = array(); + preg_match_all('#:(\w+)#is', $query, $matches, PREG_SET_ORDER); + foreach ($matches as $match) + { + if (isset($params[$match[1]])) + { + $query = preg_replace("#{$match[0]}#", '?', $query, 1); + $bind[] = $params[$match[1]]; + } + } + return $this->execute($query, $bind); + } - /** - * Returns an Expression_Database representation of the value. - * Values wrapped inside Expression_Database are not escaped in queries - * - * @param mixed $value Value to be wrapped - * @return Expression_Database Raw value that will not be escaped during query building - * @access public - * @static - */ - public static function expr($value) - { - return new Expression_Database($value); - } + /** + * Returns an Expression_Database representation of the value. + * Values wrapped inside Expression_Database are not escaped in queries + * + * @param mixed $value Value to be wrapped + * @return Expression_Database Raw value that will not be escaped during query building + * @access public + * @static + */ + public static function expr($value) + { + return new Expression_Database($value); + } - /** - * Builds a query for specified connection. - * - * @param string $type Query type. Available types: select,update,insert,delete,count - * @param string $config Configuration name of the connection. - * Defaults to 'default'. - * @return Query_Database Driver implementation of the Query_Database class. - * @access public - * @static - */ - public static function query($type, $config = 'default') - { - return DB::instance($config)->build_query($type); - } + /** + * Builds a query for specified connection. + * + * @param string $type Query type. Available types: select,update,insert,delete,count + * @param string $config Configuration name of the connection. + * Defaults to 'default'. + * @return Query_Database Driver implementation of the Query_Database class. + * @access public + * @static + */ + public static function query($type, $config = 'default') + { + return DB::instance($config)->build_query($type); + } - /** - * Gets the id of the last inserted row - * - * @param string $config Configuration name of the connection. - * Defaults to 'default'. - * @return mixed Id of the last inserted row - * @access public - * @static - */ - public static function insert_id($config = 'default') - { - return DB::instance($config)->get_insert_id(); - } + /** + * Gets the id of the last inserted row + * + * @param string $config Configuration name of the connection. + * Defaults to 'default'. + * @return mixed Id of the last inserted row + * @access public + * @static + */ + public static function insert_id($config = 'default') + { + return DB::instance($config)->get_insert_id(); + } - /** - * Gets an instance of a connection to the database - * - * @param string $config Configuration name of the connection. - * Defaults to 'default'. - * @return DB Driver implementation of the DB class. - * @access public - * @static - */ - public static function instance($config = 'default') - { - if (!isset(DB::$_instances[$config])) - { - $driver = Config::get("database.{$config}.driver"); - $driver = "DB_{$driver}_Driver"; - DB::$_instances[$config] = new $driver($config); - } - return DB::$_instances[$config]; - } + /** + * Gets an instance of a connection to the database + * + * @param string $config Configuration name of the connection. + * Defaults to 'default'. + * @return DB Driver implementation of the DB class. + * @access public + * @static + */ + public static function instance($config = 'default') + { + if (!isset(DB::$_instances[$config])) + { + $driver = Config::get("database.{$config}.driver"); + $driver = "DB_{$driver}_Driver"; + DB::$_instances[$config] = new $driver($config); + } + return DB::$_instances[$config]; + } } diff --git a/modules/database/classes/driver/mysql/db.php b/modules/database/classes/driver/mysql/db.php index 88df9b8..1d49acf 100644 --- a/modules/database/classes/driver/mysql/db.php +++ b/modules/database/classes/driver/mysql/db.php @@ -7,120 +7,120 @@ class DB_Mysql_Driver extends DB
{
- /**
- * Mysqli database connection object
- * @var mysqli
- * @access public
- * @link http://php.net/manual/en/class.mysqli.php
- */
- public $conn;
+ /**
+ * Mysqli database connection object
+ * @var mysqli
+ * @access public
+ * @link http://php.net/manual/en/class.mysqli.php
+ */
+ public $conn;
- /**
- * Type of the database, mysql.
- * @var string
- * @access public
- */
- public $db_type = 'mysql';
+ /**
+ * Type of the database, mysql.
+ * @var string
+ * @access public
+ */
+ public $db_type = 'mysql';
- /**
- * Initializes database connection
- *
- * @param string $config Name of the connection to initialize
- * @return void
- * @access public
- */
- public function __construct($config)
- {
- $this->conn = mysqli_connect(
- Config::get("database.{$config}.host", 'localhost'), Config::get("database.{$config}.user", ''), Config::get("database.{$config}.password", ''), Config::get("database.{$config}.db")
- );
- $this->conn->set_charset("utf8");
- }
+ /**
+ * Initializes database connection
+ *
+ * @param string $config Name of the connection to initialize
+ * @return void
+ * @access public
+ */
+ public function __construct($config)
+ {
+ $this->conn = mysqli_connect(
+ Config::get("database.{$config}.host", 'localhost'), Config::get("database.{$config}.user", ''), Config::get("database.{$config}.password", ''), Config::get("database.{$config}.db")
+ );
+ $this->conn->set_charset("utf8");
+ }
- /**
- * Gets column names for the specified table
- *
- * @param string $table Name of the table to get columns from
- * @return array Array of column names
- * @throw Exception if table doesn't exist
- * @access public
- */
- public function list_columns($table)
- {
- $columns = array();
- $table_desc = $this->execute("DESCRIBE `$table`");
- Debug::log($table_desc);
- if (!$table_desc->valid())
- {
- throw new Exception("Table '{$table}' doesn't exist");
- }
- foreach ($table_desc as $column)
- {
- $columns[] = $column->Field;
- }
+ /**
+ * Gets column names for the specified table
+ *
+ * @param string $table Name of the table to get columns from
+ * @return array Array of column names
+ * @throw Exception if table doesn't exist
+ * @access public
+ */
+ public function list_columns($table)
+ {
+ $columns = array();
+ $table_desc = $this->execute("DESCRIBE `$table`");
+ Debug::log($table_desc);
+ if (!$table_desc->valid())
+ {
+ throw new Exception("Table '{$table}' doesn't exist");
+ }
+ foreach ($table_desc as $column)
+ {
+ $columns[] = $column->Field;
+ }
- return $columns;
- }
+ return $columns;
+ }
- /**
- * Builds a new Query implementation
- *
- * @param string $type Query type. Available types: select,update,insert,delete,count
- * @return Query_Mysql_Driver Returns a Mysqli implementation of a Query.
- * @access public
- * @see Query_Database
- */
- public function build_query($type)
- {
- return new Query_Mysql_Driver($this, $type);
- }
+ /**
+ * Builds a new Query implementation
+ *
+ * @param string $type Query type. Available types: select,update,insert,delete,count
+ * @return Query_Mysql_Driver Returns a Mysqli implementation of a Query.
+ * @access public
+ * @see Query_Database
+ */
+ public function build_query($type)
+ {
+ return new Query_Mysql_Driver($this, $type);
+ }
- /**
- * Gets the id of the last inserted row.
- *
- * @return mixed Row id
- * @access public
- */
- public function get_insert_id()
- {
- return $this->conn->insert_id;
- }
+ /**
+ * Gets the id of the last inserted row.
+ *
+ * @return mixed Row id
+ * @access public
+ */
+ public function get_insert_id()
+ {
+ return $this->conn->insert_id;
+ }
- /**
- * Executes a prepared statement query
- *
- * @param string $query A prepared statement query
- * @param array $params Parameters for the query
- * @return Result_Mysql_Driver Mysqli implementation of a database result
- * @access public
- * @throws Exception If the query resulted in an error
- * @see Database_Result
- */
- public function execute($query, $params = array())
- {
- $cursor = $this->conn->prepare($query);
- if (!$cursor)
- {
- throw new Exception("Database error: {$this->conn->error} \n in query:\n{$query}");
- }
- $types = '';
- $bind = array();
- $refs = array();
- if (!empty($params))
- {
- foreach ($params as $key => $param)
- {
- $refs[$key] = is_array($param) ? $param[0] : $param;
- $bind[] = &$refs[$key];
- $types .= is_array($param) ? $param[1] : 's';
- }
- array_unshift($bind, $types);
+ /**
+ * Executes a prepared statement query
+ *
+ * @param string $query A prepared statement query
+ * @param array $params Parameters for the query
+ * @return Result_Mysql_Driver Mysqli implementation of a database result
+ * @access public
+ * @throws Exception If the query resulted in an error
+ * @see Database_Result
+ */
+ public function execute($query, $params = array())
+ {
+ $cursor = $this->conn->prepare($query);
+ if (!$cursor)
+ {
+ throw new Exception("Database error: {$this->conn->error} \n in query:\n{$query}");
+ }
+ $types = '';
+ $bind = array();
+ $refs = array();
+ if (!empty($params))
+ {
+ foreach ($params as $key => $param)
+ {
+ $refs[$key] = is_array($param) ? $param[0] : $param;
+ $bind[] = &$refs[$key];
+ $types .= is_array($param) ? $param[1] : 's';
+ }
+ array_unshift($bind, $types);
- call_user_func_array(array($cursor, 'bind_param'), $bind);
- }
- $cursor->execute();
- $res = $cursor->get_result();
- return new Result_Mysql_Driver($res);
- }
+ call_user_func_array(array($cursor, 'bind_param'), $bind);
+ }
+ $cursor->execute();
+ $res = $cursor->get_result();
+ return new Result_Mysql_Driver($res);
+ }
}
\ No newline at end of file diff --git a/modules/database/classes/driver/mysql/query.php b/modules/database/classes/driver/mysql/query.php index 803f932..b880338 100644 --- a/modules/database/classes/driver/mysql/query.php +++ b/modules/database/classes/driver/mysql/query.php @@ -4,4 +4,7 @@ * Mysqli implementation of the database Query * @package Database */ -class Query_Mysql_Driver extends Query_PDO_Driver {}
\ No newline at end of file +class Query_Mysql_Driver extends Query_PDO_Driver +{ + +} diff --git a/modules/database/classes/driver/mysql/result.php b/modules/database/classes/driver/mysql/result.php index e9de4a5..2927192 100644 --- a/modules/database/classes/driver/mysql/result.php +++ b/modules/database/classes/driver/mysql/result.php @@ -7,52 +7,52 @@ class Result_Mysql_Driver extends Result_Database { - /** - * Initializes new result object - * - * @param mysqli_result $result Mysqli Result - * @return void - * @access public - * @link http://php.net/manual/en/class.mysqli-result.php - */ - public function __construct($result) - { - $this->_result = $result; - } + /** + * Initializes new result object + * + * @param mysqli_result $result Mysqli Result + * @return void + * @access public + * @link http://php.net/manual/en/class.mysqli-result.php + */ + public function __construct($result) + { + $this->_result = $result; + } - /** - * Throws exception if rewind is attempted. - * - * @return void - * @access public - * @throws Exception If rewind is attempted - */ - public function rewind() - { - if ($this->_position > 0) - { - throw new Exception('Mysqli result cannot be rewound for unbuffered queries.'); - } - } + /** + * Throws exception if rewind is attempted. + * + * @return void + * @access public + * @throws Exception If rewind is attempted + */ + public function rewind() + { + if ($this->_position > 0) + { + throw new Exception('Mysqli result cannot be rewound for unbuffered queries.'); + } + } - /** - * Iterates to the next row in the result set - * - * @return void - * @access public - */ - public function next() - { - $this->check_fetched(); - $this->_row = $this->_result->fetch_object(); - if ($this->_row) - { - $this->_position++; - } - else - { - $this->_result->free(); - } - } + /** + * Iterates to the next row in the result set + * + * @return void + * @access public + */ + public function next() + { + $this->check_fetched(); + $this->_row = $this->_result->fetch_object(); + if ($this->_row) + { + $this->_position++; + } + else + { + $this->_result->free(); + } + } }
\ No newline at end of file diff --git a/modules/database/classes/driver/pdo/db.php b/modules/database/classes/driver/pdo/db.php index 5b81d66..5a920ec 100644 --- a/modules/database/classes/driver/pdo/db.php +++ b/modules/database/classes/driver/pdo/db.php @@ -7,125 +7,125 @@ class DB_PDO_Driver extends DB { - /** - * Connection object - * @var PDO - * @access public - * @link http://php.net/manual/en/class.pdo.php - */ - public $conn; + /** + * Connection object + * @var PDO + * @access public + * @link http://php.net/manual/en/class.pdo.php + */ + public $conn; - /** - * Type of the database, e.g. mysql, pgsql etc. - * @var string - * @access public - */ - public $db_type; + /** + * Type of the database, e.g. mysql, pgsql etc. + * @var string + * @access public + */ + public $db_type; - /** - * Initializes database connection - * - * @param string $config Name of the connection to initialize - * @return void - * @access public - */ - public function __construct($config) - { - $this->conn = new PDO( - Config::get("database.{$config}.connection"), Config::get("database.{$config}.user", ''), Config::get("database.{$config}.password", '') - ); - $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->db_type = strtolower(str_replace('PDO_', '', $this->conn->getAttribute(PDO::ATTR_DRIVER_NAME))); - if ($this->db_type != 'sqlite') - { - $this->conn->exec("SET NAMES utf8"); - } - } + /** + * Initializes database connection + * + * @param string $config Name of the connection to initialize + * @return void + * @access public + */ + public function __construct($config) + { + $this->conn = new PDO( + Config::get("database.{$config}.connection"), Config::get("database.{$config}.user", ''), Config::get("database.{$config}.password", '') + ); + $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->db_type = strtolower(str_replace('PDO_', '', $this->conn->getAttribute(PDO::ATTR_DRIVER_NAME))); + if ($this->db_type != 'sqlite') + { + $this->conn->exec("SET NAMES utf8"); + } + } - /** - * Builds a new Query implementation - * - * @param string $type Query type. Available types: select,update,insert,delete,count - * @return Query_PDO_Driver Returns a PDO implementation of a Query. - * @access public - * @see Query_Database - */ - public function build_query($type) - { - return new Query_PDO_Driver($this, $type); - } + /** + * Builds a new Query implementation + * + * @param string $type Query type. Available types: select,update,insert,delete,count + * @return Query_PDO_Driver Returns a PDO implementation of a Query. + * @access public + * @see Query_Database + */ + public function build_query($type) + { + return new Query_PDO_Driver($this, $type); + } - /** - * Gets the id of the last inserted row. - * - * @return mixed Row id - * @access public - */ - public function get_insert_id() - { - if ($this->db_type == 'pgsql') - { - return $this->execute('SELECT lastval() as id')->current()->id; - } - return $this->conn->lastInsertId(); - } + /** + * Gets the id of the last inserted row. + * + * @return mixed Row id + * @access public + */ + public function get_insert_id() + { + if ($this->db_type == 'pgsql') + { + return $this->execute('SELECT lastval() as id')->current()->id; + } + return $this->conn->lastInsertId(); + } - /** - * Gets column names for the specified table - * - * @param string $table Name of the table to get columns from - * @return array Array of column names - * @access public - */ - public function list_columns($table) - { - $columns = array(); - if ($this->db_type == 'mysql') - { - $table_desc = $this->execute("DESCRIBE `$table`"); - foreach ($table_desc as $column) - { - $columns[] = $column->Field; - } - } - if ($this->db_type == 'pgsql') - { - $table_desc = $this->execute("select column_name from information_schema.columns where table_name = '{$table}' and table_catalog=current_database();"); - foreach ($table_desc as $column) - { - $columns[] = $column->column_name; - } - } - if ($this->db_type == 'sqlite') - { - $table_desc = $this->execute("PRAGMA table_info('$table')"); - foreach ($table_desc as $column) - { - $columns[] = $column->name; - } - } - return $columns; - } + /** + * Gets column names for the specified table + * + * @param string $table Name of the table to get columns from + * @return array Array of column names + * @access public + */ + public function list_columns($table) + { + $columns = array(); + if ($this->db_type == 'mysql') + { + $table_desc = $this->execute("DESCRIBE `$table`"); + foreach ($table_desc as $column) + { + $columns[] = $column->Field; + } + } + if ($this->db_type == 'pgsql') + { + $table_desc = $this->execute("select column_name from information_schema.columns where table_name = '{$table}' and table_catalog=current_database();"); + foreach ($table_desc as $column) + { + $columns[] = $column->column_name; + } + } + if ($this->db_type == 'sqlite') + { + $table_desc = $this->execute("PRAGMA table_info('$table')"); + foreach ($table_desc as $column) + { + $columns[] = $column->name; + } + } + return $columns; + } - /** - * Executes a prepared statement query - * - * @param string $query A prepared statement query - * @param array $params Parameters for the query - * @return Result_PDO_Driver PDO implementation of a database result - * @access public - * @throws Exception If the query resulted in an error - * @see Database_Result - */ - public function execute($query, $params = array()) - { - $cursor = $this->conn->prepare($query); - if (!$cursor->execute($params)) - { - $error = $cursor->errorInfo(); - throw new Exception("Database error:\n".$error[2]." \n in query:\n{$query}"); - } - return new Result_PDO_Driver($cursor); - } + /** + * Executes a prepared statement query + * + * @param string $query A prepared statement query + * @param array $params Parameters for the query + * @return Result_PDO_Driver PDO implementation of a database result + * @access public + * @throws Exception If the query resulted in an error + * @see Database_Result + */ + public function execute($query, $params = array()) + { + $cursor = $this->conn->prepare($query); + if (!$cursor->execute($params)) + { + $error = $cursor->errorInfo(); + throw new Exception("Database error:\n".$error[2]." \n in query:\n{$query}"); + } + return new Result_PDO_Driver($cursor); + } }
\ No newline at end of file diff --git a/modules/database/classes/driver/pdo/query.php b/modules/database/classes/driver/pdo/query.php index d78316d..5d82cbf 100644 --- a/modules/database/classes/driver/pdo/query.php +++ b/modules/database/classes/driver/pdo/query.php @@ -7,96 +7,96 @@ class Query_PDO_Driver extends Query_Database { - /** - * Type of the database, e.g. mysql, pgsql etc. - * @var string - * @access public - */ - protected $_db_type; + /** + * Type of the database, e.g. mysql, pgsql etc. + * @var string + * @access public + */ + protected $_db_type; - /** - * Character to use for quoting fields - * @var string - * @access public - */ - protected $_quote; + /** + * Character to use for quoting fields + * @var string + * @access public + */ + protected $_quote; - /** - * Creates a new query object, checks which driver we are using and set the character used for quoting - * - * @param DB $db Database connection - * @param string $type Query type. Available types: select, update, insert, delete, count - * @return void - * @access public - * @see Query_Database::__construct() - */ - public function __construct($db, $type) - { - parent::__construct($db, $type); - $this->_db_type = $this->_db->db_type; - $this->_quote = $this->_db_type == 'mysql' ? '`' : '"'; - } + /** + * Creates a new query object, checks which driver we are using and set the character used for quoting + * + * @param DB $db Database connection + * @param string $type Query type. Available types: select, update, insert, delete, count + * @return void + * @access public + * @see Query_Database::__construct() + */ + public function __construct($db, $type) + { + parent::__construct($db, $type); + $this->_db_type = $this->_db->db_type; + $this->_quote = $this->_db_type == 'mysql' ? '`' : '"'; + } - /** - * Puts quotes around a string - * - * @param string $str String to be enclosed in quotes - * @return string String surrounded with quotes - * @access protected - */ - protected function quote($str) - { - return $this->_quote.$str.$this->_quote; - } + /** + * Puts quotes around a string + * + * @param string $str String to be enclosed in quotes + * @return string String surrounded with quotes + * @access protected + */ + protected function quote($str) + { + return $this->_quote.$str.$this->_quote; + } - /** - * If a string is passed escapes a field by enclosing it in specified quotes. - * If you pass an Expression_Database object the value will be inserted into the query unescaped - * - * @param mixed $field Field to be escaped or an Expression_Database object - * if the field must not be escaped - * @return string Escaped field representation - * @access public - * @see Expression_Database - */ - public function escape_field($field) - { - if (is_object($field) && get_class($field) == 'Expression_Database') - { - return $field->value; - } - $field = explode('.', $field); - if (count($field) == 1) - { - array_unshift($field, $this->last_alias()); - } - $str = $this->quote($field[0]).'.'; - if (trim($field[1]) == '*') - { - return $str.'*'; - } - return $str.$this->quote($field[1]); - } + /** + * If a string is passed escapes a field by enclosing it in specified quotes. + * If you pass an Expression_Database object the value will be inserted into the query unescaped + * + * @param mixed $field Field to be escaped or an Expression_Database object + * if the field must not be escaped + * @return string Escaped field representation + * @access public + * @see Expression_Database + */ + public function escape_field($field) + { + if (is_object($field) && get_class($field) == 'Expression_Database') + { + return $field->value; + } + $field = explode('.', $field); + if (count($field) == 1) + { + array_unshift($field, $this->last_alias()); + } + $str = $this->quote($field[0]).'.'; + if (trim($field[1]) == '*') + { + return $str.'*'; + } + return $str.$this->quote($field[1]); + } - /** - * Replaces the value with ? and appends it to the parameters array - * If you pass an Expression_Database object the value will be inserted into the query unescaped - * @param mixed $val Value to be escaped or an Expression_Database object - * if the value must not be escaped - * @param array &$params Reference to parameters array - * @return string Escaped value representation - * @access public - */ - public function escape_value($val,&$params) - { + /** + * Replaces the value with ? and appends it to the parameters array + * If you pass an Expression_Database object the value will be inserted into the query unescaped + * @param mixed $val Value to be escaped or an Expression_Database object + * if the value must not be escaped + * @param array &$params Reference to parameters array + * @return string Escaped value representation + * @access public + */ + public function escape_value($val, &$params) + { if ($val instanceof Expression_Database) - { + { return $val->value; - } + } if ($val instanceof Query_Database) - { - return $this->subquery($val,$params); - } + { + return $this->subquery($val, $params); + } $params[] = $val; return '?'; } @@ -106,295 +106,296 @@ class Query_PDO_Driver extends Query_Database * * @param Query_Database $query Query builder for the subquery * @param array &$params Reference to parameters array - * @return string Subquery SQL - * @access public + * @return string Subquery SQL + * @access public */ - protected function subquery($query, &$params) { + protected function subquery($query, &$params) + { $query = $query->query(); $params = array_merge($params, $query[1]); return "({$query[0]}) "; } - + /** * Gets the SQL for a table to select from * * @param string|Expression_Database|Query_Database|array $table Table representation * @param array &$params Reference to parameters array * @param string &alias Alias for this table - * @return string Table SQL - * @access public + * @return string Table SQL + * @access public */ - public function escape_table($table, &$params) { - $alias=null; - if (is_array($table)) { + public function escape_table($table, &$params) + { + $alias = null; + if (is_array($table)) + { $alias = $table[1]; $table = $table[0]; } - - if (is_string($table)){ + + if (is_string($table)) + { $table = $this->quote($table); if ($alias != null) $table.= " AS {$alias}"; return $table; } - + if ($alias == null) $alias = $this->last_alias(); - - if($table instanceof Query_Database) - return "{$this->subquery($table,$params)} AS {$alias}"; - - if($table instanceof Expression_Database) + + if ($table instanceof Query_Database) + return "{$this->subquery($table, $params)} AS {$alias}"; + + if ($table instanceof Expression_Database) return "({$table->value}) AS {$alias}"; - + throw new Exception("Parameter type {get_class($table)} cannot be used as a table"); } - /** - * Builds a query and fills the $params array with parameter values - * - * @return array An array with a prepared query string and an array of parameters - * @access public - */ + /** + * Builds a query and fills the $params array with parameter values + * + * @return array An array with a prepared query string and an array of parameters + * @access public + */ public function query() - { - + { + $query = ''; $params = array(); - + if ($this->_type == 'insert') - { + { $query .= "INSERT INTO {$this->quote($this->_table)} "; if (empty($this->_data) && $this->_db_type == 'pgsql') - { + { $query.= "DEFAULT VALUES "; } - else - { + else + { $columns = ''; $values = ''; $first = true; - foreach($this->_data as $key => $val) - { + foreach ($this->_data as $key => $val) + { if (!$first) - { + { $values .= ', '; $columns .= ', '; } - else - { + else + { $first = false; } $columns .= $this->quote($key); - $values .= $this->escape_value($val,$params); + $values .= $this->escape_value($val, $params); } $query .= "({$columns}) VALUES({$values})"; } - } - else - { + } + else + { if ($this->_type == 'select') - { + { $query .= "SELECT "; - if ($this->_fields==null) - { + if ($this->_fields == null) + { $query .= "* "; } - else - { + else + { $first = true; foreach ($this->_fields as $f) - { + { if (!$first) - { + { $query .= ", "; } - else - { + else + { $first = false; } if (is_array($f)) - { + { $query .= "{$this->escape_field($f[0])} AS {$f[1]} "; } - else - { + else + { $query .= "{$this->escape_field($f)} "; } } } - $query .= "FROM {$this->escape_table($this->_table,$params)} "; + $query .= "FROM {$this->escape_table($this->_table, $params)} "; } if ($this->_type == 'count') - { - $query .= "SELECT COUNT(*) as {$this->quote('count')} FROM {$this->escape_table($this->_table,$params)} "; + { + $query .= "SELECT COUNT(*) as {$this->quote('count')} FROM {$this->escape_table($this->_table, $params)} "; } - + if ($this->_type == 'delete') - { - if ($this->_db_type!='sqlite') - { + { + if ($this->_db_type != 'sqlite') + { $query .= "DELETE {$this->last_alias()}.* FROM {$this->quote($this->_table)} "; } - else - { + else + { if (!empty($this->_joins)) - { + { throw new Exception("SQLite doesn't support deleting a table with JOIN in the query"); - } + } $query .= "DELETE FROM {$this->quote($this->_table)} "; } } - if ($this->_type=='update') - { + if ($this->_type == 'update') + { $query .= "UPDATE {$this->quote($this->_table)} SET "; $first = true; - foreach ($this->_data as $key=>$val){ + foreach ($this->_data as $key => $val) + { if (!$first) - { + { $query.=", "; } - else - { - $first=false; + else + { + $first = false; } - $query .= "{$this->quote($key)} = {$this->escape_value($val,$params)}"; + $query .= "{$this->quote($key)} = {$this->escape_value($val, $params)}"; } $query .= " "; } - + foreach ($this->_joins as $join) - { + { $table = $join[0]; - $table = $this->escape_table($table,$params); - $query .= strtoupper($join[1])." JOIN {$table} ON {$this->get_condition_query($join[2],$params,true,true)} "; + $table = $this->escape_table($table, $params); + $query .= strtoupper($join[1])." JOIN {$table} ON {$this->get_condition_query($join[2], $params, true, true)} "; } if (!empty($this->_conditions)) - { - $query .= "WHERE {$this->get_condition_query($this->_conditions,$params,true)} "; + { + $query .= "WHERE {$this->get_condition_query($this->_conditions, $params, true)} "; } - if (($this->_type == 'select' || $this->_type == 'count') && $this->_group_by!=null) - { + if (($this->_type == 'select' || $this->_type == 'count') && $this->_group_by != null) + { $query .= "GROUP BY {$this->escape_field($this->_group_by)} "; } - if (($this->_type == 'select' || $this->_type == 'count') && !empty($this->_having)) - { - $query .= "HAVING {$this->get_condition_query($this->_having,$params,true)} "; + if (($this->_type == 'select' || $this->_type == 'count') && !empty($this->_having)) + { + $query .= "HAVING {$this->get_condition_query($this->_having, $params, true)} "; } - + if ($this->_type == 'select' && !empty($this->_orderby)) - { + { $query .= "ORDER BY "; $first = true; foreach ($this->_orderby as $order) - { + { if (!$first) - { + { $query .= ','; } - else - { + else + { $first = false; } $query .= $this->escape_field($order[0])." "; if (isset($order[1])) - { + { $dir = strtoupper($order[1]); $query .= $dir." "; } } } - + if (count($this->_union) > 0 && ($this->_type == 'select')) - { + { $query = "({$query}) "; foreach ($this->_union as $union) - { - $query .= $union[1]?"UNION ALL ":"UNION "; + { + $query .= $union[1] ? "UNION ALL " : "UNION "; if (is_subclass_of($union[0], 'Query_Database')) - { - $query .= $this->subquery($union[0],$params); + { + $query .= $this->subquery($union[0], $params); } - elseif(is_subclass_of($union[0], 'Expression_Database')) - { + elseif (is_subclass_of($union[0], 'Expression_Database')) + { $query .= "({$union[0]->value}) "; } - else - { + else + { throw new Exception("You can only use query builder instances or DB::expr for unions"); } } } - + if ($this->_type != 'count') - { + { if ($this->_limit != null) - { + { $query .= "LIMIT {$this->_limit} "; - } + } if ($this->_offset != null) - { + { $query .= "OFFSET {$this->_offset} "; - } + } } - } - - return array($query,$params); - } + return array($query, $params); + } - /** - * Recursively parses conditions array into a query string - * - * @param array $p Element of the cobditions array - * @param array &$params Reference to parameters array - * @param boolean $skip_first_operator Flag to skip the first logical operator in a query - * to prevent AND or OR to be at the beginning of the query - * @param boolean $value_is_field Flag if the the value in the logical operations should - * be treated as a field. E.g. for joins where the fields are - * compared between themselves and not with actual values - * @return string String representation of the conditions - * @access public - * @throws Exception If condition cannot be parsed - */ - public function get_condition_query($p,&$params,$skip_first_operator,$value_is_field = false) - { + /** + * Recursively parses conditions array into a query string + * + * @param array $p Element of the cobditions array + * @param array &$params Reference to parameters array + * @param boolean $skip_first_operator Flag to skip the first logical operator in a query + * to prevent AND or OR to be at the beginning of the query + * @param boolean $value_is_field Flag if the the value in the logical operations should + * be treated as a field. E.g. for joins where the fields are + * compared between themselves and not with actual values + * @return string String representation of the conditions + * @access public + * @throws Exception If condition cannot be parsed + */ + public function get_condition_query($p, &$params, $skip_first_operator, $value_is_field = false) + { if (isset($p['field'])) - { + { if ($value_is_field) - { + { $param = $this->escape_field($p['value']); } - else - { + else + { $param = $this->escape_value($p['value'], $params); } return $this->escape_field($p['field']).' '.$p['operator'].' '.$param; } if (isset($p['logic'])) - { + { return ($skip_first_operator ? '' : strtoupper($p['logic']).' ') - .$this->get_condition_query($p['conditions'], $params, false, $value_is_field); + .$this->get_condition_query($p['conditions'], $params, false, $value_is_field); } - + $conds = ''; $skip = $skip_first_operator || (count($p) > 1); - foreach($p as $q) - { - $conds .= $this->get_condition_query($q,$params,$skip,$value_is_field).' '; + foreach ($p as $q) + { + $conds .= $this->get_condition_query($q, $params, $skip, $value_is_field).' '; $skip = false; } if (count($p) > 1 && !$skip_first_operator) - { + { return "( ".$conds.")"; - } + } return $conds; throw new Exception("Cannot parse condition:\n".var_export($p, true)); } - - }
\ No newline at end of file diff --git a/modules/database/classes/driver/pdo/result.php b/modules/database/classes/driver/pdo/result.php index d64c479..fda813c 100644 --- a/modules/database/classes/driver/pdo/result.php +++ b/modules/database/classes/driver/pdo/result.php @@ -7,52 +7,52 @@ class Result_PDO_Driver extends Result_Database { - /** - * Initializes new result object - * - * @param PDOStatement $stmt PDO Statement - * @return void - * @access public - * @link http://php.net/manual/en/class.pdostatement.php - */ - public function __construct($stmt) - { - $this->_result = $stmt; - } + /** + * Initializes new result object + * + * @param PDOStatement $stmt PDO Statement + * @return void + * @access public + * @link http://php.net/manual/en/class.pdostatement.php + */ + public function __construct($stmt) + { + $this->_result = $stmt; + } - /** - * Throws exception if rewind is attempted. - * - * @return void - * @access public - * @throws Exception If rewind is attempted - */ - public function rewind() - { - if ($this->_position > 0) - { - throw new Exception('PDO statement cannot be rewound for unbuffered queries'); - } - } + /** + * Throws exception if rewind is attempted. + * + * @return void + * @access public + * @throws Exception If rewind is attempted + */ + public function rewind() + { + if ($this->_position > 0) + { + throw new Exception('PDO statement cannot be rewound for unbuffered queries'); + } + } - /** - * Iterates to the next row in the result set - * - * @return void - * @access public - */ - public function next() - { - $this->check_fetched(); - $this->_row = $this->_result->fetchObject(); - if ($this->_row) - { - $this->_position++; - } - else - { - $this->_result->closeCursor(); - } - } + /** + * Iterates to the next row in the result set + * + * @return void + * @access public + */ + public function next() + { + $this->check_fetched(); + $this->_row = $this->_result->fetchObject(); + if ($this->_row) + { + $this->_position++; + } + else + { + $this->_result->closeCursor(); + } + } }
\ No newline at end of file diff --git a/modules/orm/classes/orm.php b/modules/orm/classes/orm.php index 96bfd1e..f975fc1 100644 --- a/modules/orm/classes/orm.php +++ b/modules/orm/classes/orm.php @@ -22,791 +22,792 @@ class ORM { - /** - * Specifies which table the model will use, can be overridden - * @var string - * @access public - */ - public $table = null; - - /** - * Specifies which connection the model will use, can be overridden - * but a model can have relationships only with models utilizing the same connection - * @var string - * @access public - */ - public $connection = 'default'; - - /** - * Specifies which column is treated as PRIMARY KEY - * @var string - * @access public - */ - public $id_field = 'id'; - - /** - * You can define 'Belongs to' relationships buy changing this array - * @var array - * @access protected - */ - protected $belongs_to = array(); - - /** - * You can define 'Has one' relationships buy changing this array - * @var array - * @access protected - */ - protected $has_one = array(); - - /** - * You can define 'Has many' relationships buy changing this array - * @var array - * @access protected - */ - protected $has_many = array(); - - /** - * Associated query builder - * @var Query_Database - * @access public - */ - public $query; - - /** - * The name of the model - * @var string - * @access public - */ - public $model_name; - - /** - * Cached properties - * @var array - * @access public - */ - public $cached = array(); - - /** - * An instance of the database connection - * @var DB - * @access protected - */ - protected $db; - - /** - * Current row returned by the database - * @var array - * @access protected - */ - protected $_row = array(); - - /** - * A flag whether the row was loaded from the database - * @var boolean - * @access protected - */ - protected $_loaded = false; - - /** - * Relationships to be preloaded - * @var array - * @access protected - */ - protected $_with = array(); - - /** - * Cached column names for tables - * @var array - * @access protected - */ - protected static $_column_cache = array(); - - /** - * Constructs the model. To use ORM it is enough to - * just create a model like this: - * <code> - * class Fairy_Model extends ORM { } - * </code> - * By default it will assume that the name of your table - * is the plural form of the models' name, the PRIMARY KEY is id, - * and will use the 'default' connection. This behaviour is easy to be - * changed by overriding $table, $id and $db properties. - * - * @return void - * @access public - * @ see $table - * @ see $id - * @ see $db - */ - public function __construct() - { - $this->query = DB::instance($this->connection)->build_query('select'); - $this->model_name = strtolower(get_class($this)); - if (substr($this->model_name, -6) == '_model') - { - $this->model_name = substr($this->model_name, 0, -6); - } - if ($this->table == null) - { - $this->table = ORM::plural($this->model_name); - } - $this->query->table($this->table); - - foreach (array('belongs_to', 'has_one', 'has_many') as $rels) - { - $normalized = array(); - foreach ($this->$rels as $key => $rel) - { - if (!is_array($rel)) - { - $key = $rel; - $rel = array(); - } - $normalized[$key] = $rel; - if (!isset($rel['model'])) - { - $rel['model'] = $normalized[$key]['model'] = $rels == 'has_many' ? ORM::singular($key) : $key; - } - - $normalized[$key]['type'] = $rels; - if (!isset($rel['key'])) - { - $normalized[$key]['key'] = $rels != 'belongs_to' ? ($this->model_name.'_id') : $rel['model'].'_id'; - } - - if ($rels == 'has_many' && isset($rel['through'])) - { - if (!isset($rel['foreign_key'])) - { - $normalized[$key]['foreign_key'] = $rel['model'].'_id'; - } - } - - $normalized[$key]['name'] = $key; - } - $this->$rels = $normalized; - } - } - - /** - * Magic method for call Query_Database methods - * - * @param string $method Method to call - * @param array $arguments Arguments passed to the method - * @return mixed Returns self if parameters were passed. If no parameters where passed returns - * current value for the associated parameter - * @throws Exception If method doesn't exist - * @access public - */ - public function __call($method, $arguments) - { - if (!in_array($method, array('limit', 'offset', 'order_by', 'where'))) - { - throw new Exception("Method '{$method}' doesn't exist on .".get_class($this)); - } - $res = call_user_func_array(array($this->query, $method), $arguments); - if (is_subclass_of($res, 'Query_Database')) - { - return $this; - } - return $res; - } - - /** - * Finds all rows that meet set criteria. - * - * @return Result_ORM Returns Result_ORM that you can use in a 'foreach' loop. - * @throw Exception If the relationship specified using with() does not exist or is not of the belongs_to or has_one type - * @access public - */ - public function find_all() - { - $paths = array(); - if (!empty($this->_with)) - { - $fields = array(); - $this_alias = $this->query->last_alias(); - foreach ($this->columns() as $column) - { - $fields[] = array("{$this_alias}.{$column}", "{$this_alias}__{$column}"); - } - foreach ($this->_with as $target) - { - $model = $this; - $model_alias = $this_alias; - $rels = explode('.', $target); - foreach ($rels as $key => $rel_name) - { - $path = implode('.', array_slice($rels, 0, $key + 1)); - if (isset($paths[$path])) - { - $model = $paths[$path]['model']; - $model_alias = $paths[$path]['alias']; - continue; - } - $alias = $this->query->add_alias(); - $model_rels = array_merge($model->has_one, $model->has_many, $model->belongs_to); - $rel = Misc::arr($model_rels, $rel_name, false); - - if (!$rel) - { - throw new Exception("Model '{$model->model_name}' doesn't have a '{$rel_name}' relation defined"); - } - if ($rel['type'] == 'has_many') - { - throw new Exception("Relationship '{$rel_name}' is of has_many type and cannot be preloaded view with()"); - } - $rel_model = ORM::factory($rel['model']); - - if ($rel['type'] == 'belongs_to') - { - $this->query->join(array($rel_model->table, $alias), array( - $model_alias.'.'.$rel['key'], - $alias.'.'.$rel_model->id_field, - ), 'left'); - } - else - { - $this->query->join(array($rel_model->table, $alias), array( - $model_alias.'.'.$model->id_field, - $alias.'.'.$rel['key'], - ), 'left'); - } - - foreach ($rel_model->columns() as $column) - { - $fields[] = array("{$alias}.{$column}", "{$alias}__{$column}"); - } - $model = $rel_model; - $model_alias = $alias; - $paths[$path] = array('alias' => $alias, 'model' => $model); - } - } - - call_user_func_array(array($this->query, 'fields'), $fields); - } - - return new Result_ORM(get_class($this), $res = $this->query->execute(), $paths); - } - - /** - * Searches for the first row that meets set criteria. If no rows match it still returns an ORM object - * but with its loaded() flag being False. calling save() on such an object will insert a new row. - * - * @return ORM Found item or new object of the current model but with loaded() flag being False - * @access public - */ - public function find() - { - $set_limit = $this->limit(); - $res = $this->limit(1)->find_all()->current(); - $this->limit($set_limit); - return $res; - } - - /** - * Counts all rows that meet set criteria. Ignores limit and offset. - * - * @return int Number of rows - * @access public - */ - public function count_all() - { - $query = clone $this->query; - $query->type('count'); - return $query->execute(); - } - - /** - * Checks if the item is considered to be loaded from the database - * - * @return boolean Returns True if the item was loaded - * @access public - */ - public function loaded() - { - return $this->_loaded; - } - - /** - * Returns the row associated with current ORM item as an associative array - * - * @return array Associative array representing the row - * @access public - */ - public function as_array() - { - return $this->_row; - } - - /** - * Returns a clone of query builder that is being used to set conditions. - * It is useful for example if you let ORM manage building a complex query using it's relationship - * system, then you get the clone of that query and alter it to your liking, - * so there is no need to writing relationship joins yourself. - * - * @return Query_Database A clone of the current query builder - * @access public - */ - public function query() - { - return clone $this->query; - } - - /** - * You can override this method to return additional properties that you would like to use - * in your model. One advantage for using this instead of just overriding __get() is that - * in this way the properties also get cached. - * - * @param string $property The name of the property to get - * @return void - * @access public - */ - public function get($property) - { - - } - - /** - * Magic method that allows accessing row columns as properties and also facilitates - * access to relationships and custom properties defined in get() method. - * If a relationship is being accessed, it will return an ORM model of the related table - * and automatically alter its query so that all your previously set conditions will remain - - * @param string $column Name of the column, property or relationship to get - * @return mixed - * @access public - * @throws Exception If neither property nor a relationship with such name is found - */ - public function __get($column) - { - if (array_key_exists($column, $this->_row)) - { - return $this->_row[$column]; - } - if (array_key_exists($column, $this->cached)) - { - return $this->cached[$column]; - } - if (($val = $this->get($column)) !== null) - { - $this->cached[$column] = $val; - return $val; - } - $relations = array_merge($this->has_one, $this->has_many, $this->belongs_to); - if ($target = Misc::arr($relations, $column, false)) - { - $model = ORM::factory($target['model']); - $model->query = clone $this->query; - if ($this->loaded()) - { - $model->query->where($this->id_field, $this->_row[$this->id_field]); - } - if ($target['type'] == 'has_many' && isset($target['through'])) - { - $last_alias = $model->query->last_alias(); - $through_alias = $model->query->add_alias(); - $new_alias = $model->query->add_alias(); - $model->query->join(array($target['through'], $through_alias), array( - $last_alias.'.'.$this->id_field, - $through_alias.'.'.$target['key'], - ), 'inner'); - $model->query->join(array($model->table, $new_alias), array( - $through_alias.'.'.$target['foreign_key'], - $new_alias.'.'.$model->id_field, - ), 'inner'); - } - else - { - $last_alias = $model->query->last_alias(); - $new_alias = $model->query->add_alias(); - if ($target['type'] == 'belongs_to') - { - $model->query->join(array($model->table, $new_alias), array( - $last_alias.'.'.$target['key'], - $new_alias.'.'.$model->id_field, - ), 'inner'); - } - else - { - $model->query->join(array($model->table, $new_alias), array( - $last_alias.'.'.$this->id_field, - $new_alias.'.'.$target['key'], - ), 'inner'); - } - } - $model->query->fields("$new_alias.*"); - if ($target['type'] != 'has_many' && $this->loaded()) - { - $model = $model->find(); - $this->cached[$column] = $model; - } - return $model; - } - - throw new Exception("Property {$column} not found on {$this->model_name} model."); - } - - /** - * Magic method to update record values when set as properties or to add an ORM item to - * a relation. By assigning an ORM object to a relationship a relationship is created between the - * current item and the passed one Using properties this way is a shortcut to the add() method. - * - * @param string $column Column or relationship name - * @param mixed $val Column value or an ORM item to be added to a relation - * @return void - * @access public - * @see add() - */ - public function __set($column, $val) - { - $relations = array_merge($this->has_one, $this->has_many, $this->belongs_to); - if (array_key_exists($column, $relations)) - { - $this->add($column, $val); - } - else - { - $this->_row[$column] = $val; - } - $this->cached = array(); - } - - /** - * Create a relationship between current item and an other one - * - * @param string $relation Name of the relationship - * @param ORM $model ORM item to create a relationship with - * @return void - * @access public - * @throws Exception Exception If relationship is not defined - * @throws Exception Exception If current item is not in the database yet (isn't considered loaded()) - * @throws Exception Exception If passed item is not in the database yet (isn't considered loaded()) - */ - public function add($relation, $model) - { - - $rels = array_merge($this->has_one, $this->has_many, $this->belongs_to); - $rel = Misc::arr($rels, $relation, false); - if (!$rel) - { - throw new Exception("Model doesn't have a '{$relation}' relation defined"); - } - - if ($rel['type'] == 'belongs_to') - { - - if (!$model->loaded()) - { - throw new Exception("Model must be loaded before added to a belongs_to relationship. Probably you haven't saved it."); - } - - $key = $rel['key']; - $this->$key = $model->_row[$this->id_field]; - if ($this->loaded()) - { - $this->save(); - } - } - elseif (isset($rel['through'])) - { - - if (!$this->loaded()) - { - throw new Exception("Model must be loaded before you try adding 'through' relationships to it. Probably you haven't saved it."); - } - if (!$model->loaded()) - { - throw new Exception("Model must be loaded before added to a 'through' relationship. Probably you haven't saved it."); - } - - $exists = DB::instance($this->connection)->build_query('count') - ->table($rel['through']) - ->where(array( - array($rel['key'], $this->_row[$this->id_field]), - array($rel['foreign_key'], $model->_row[$model->id_field]) - )) - ->execute(); - if (!$exists) - { - DB::instance($this->connection)->build_query('insert') - ->table($rel['through']) - ->data(array( - $rel['key'] => $this->_row[$this->id_field], - $rel['foreign_key'] => $model->_row[$model->id_field] - )) - ->execute(); - } - } - else - { - - if (!$this->loaded()) - { - throw new Exception("Model must be loaded before you try adding 'has_many' relationships to it. Probably you haven't saved it."); - } - - $key = $rel['key']; - $model->$key = $this->_row[$this->id_field]; - if ($model->loaded()) - { - $model->save(); - } - } - $this->cached = array(); - } - - /** - * Removes a relationship between current item and the passed one - * - * @param string $relation Name of the relationship - * @param ORM $model ORM item to remove relationship with. Can be omitted for 'belongs_to' relationships - * @return void - * @access public - * @throws Exception Exception If realtionship is not defined - * @throws Exception Exception If current item is not in the database yet (isn't considered loaded()) - * @throws Exception Exception If passed item is not in the database yet (isn't considered loaded()) - */ - public function remove($relation, $model = null) - { - - if (!$this->loaded()) - { - throw new Exception("Model must be loaded before you try removing relationships from it."); - } - - $rels = array_merge($this->has_one, $this->has_many, $this->belongs_to); - $rel = Misc::arr($rels, $relation, false); - if (!$rel) - { - throw new Exception("Model doesn't have a '{$relation}' relation defined"); - } - - if ($rel['type'] != 'belongs_to' && (!$model || !$model->loaded())) - { - throw new Exception("Model must be loaded before being removed from a has_one or has_many relationship."); - } - if ($rel['type'] == 'belongs_to') - { - $key = $rel['key']; - $this->$key = null; - $this->save(); - } - elseif (isset($rel['through'])) - { - DB::instance($this->connection)->build_query('delete') - ->table($rel['through']) - ->where(array( - array($rel['key'], $this->_row[$this->id_field]), - array($rel['foreign_key'], $model->_row[$model->id_field]) - )) - ->execute(); - } - else - { - $key = $rel['key']; - $model->$key = null; - $model->save(); - } - $this->cached = array(); - } - - /** - * Gets name column names of the table associated with the model. - * - * @return array Array of column names - * @access public - */ - public function columns() - { - if (!isset(ORM::$_column_cache[$this->table])) - { - ORM::$_column_cache[$this->table] = DB::instance($this->connection)->list_columns($this->table); - } - return ORM::$_column_cache[$this->table]; - } - - /** - * Defines which relationships should be preloaded. You can only preload - * belongs_to and has_one relationships. You can use the dot notation to - * preload deep relationsips, e.g. 'tree.protector' will preload the tree - * that a fairy lives in and also preload the protector of that tree. - * - * @param string $relationsip,... List of relationships to preload - * @return ORM Returns itself - * @access public - */ - public function with() - { - $this->_with = func_get_args(); - return $this; - } - - /** - * Deletes current item from the database - * - * @return void - * @access public - * @throws Exception If the item is not in the database, e.g. is not loaded() - */ - public function delete() - { - if (!$this->loaded()) - { - throw new Exception("Cannot delete an item that wasn't selected from database"); - } - DB::instance($this->connection)->build_query('delete') - ->table($this->table) - ->where($this->id_field, $this->_row[$this->id_field]) - ->execute(); - $this->cached = array(); - } - - /** - * Deletes all items that meet set conditions. Use in the same way - * as you would a find_all() method. - * - * @return ORM Returns self - * @access public - */ - public function delete_all() - { - $query = clone $this->query; - $query->type('delete'); - $query->execute(); - return $this; - } - - /** - * Saves the item back to the database. If item is loaded() it will result - * in an update, otherwise a new row will be inserted - * - * @return ORM Returns self - * @access public - */ - public function save() - { - if ($this->loaded()) - { - $query = DB::instance($this->connection)->build_query('update') - ->table($this->table) - ->where($this->id_field, $this->_row[$this->id_field]); - } - else - { - $query = DB::instance($this->connection)->build_query('insert') - ->table($this->table); - } - $query->data($this->_row); - $query->execute(); - - if ($this->loaded()) - { - $id = $this->_row[$this->id_field]; - } - else - { - $id = DB::instance($this->connection)->get_insert_id(); - } - $row = (array) DB::instance($this->connection)->build_query('select') - ->table($this->table) - ->where($this->id_field, $id)->execute()->current(); - $this->values($row, true); - return $this; - } - - /** - * Batch updates item columns using an associative array - * - * @param array $row Associative array of key => value pairs - * @param boolean $set_loaded Flag to consider the ORM item loaded. Useful if you selected - * the row from the database and want to wrap it in ORM - * @return ORM Returns self - * @access public - */ - public function values($row, $set_loaded = false) - { - $this->_row = array_merge($this->_row, $row); - if ($set_loaded) - { - $this->_loaded = true; - } - $this->cached = array(); - return $this; - } - - /** - * Initializes ORM model by name, and optionally fetches an item by id - * - * @param string $name Model name - * @param mixed $id If set ORM will try to load the item with this id from the database - * @return ORM ORM model, either empty or preloaded - * @access public - * @static - */ - public static function factory($name, $id = null) - { - $model = $name.'_Model'; - $model = new $model; - if ($id != null) - { - $model = $model->where($model->id_field, $id)->find(); - $model->values(array($model->id_field => $id)); - } - return $model; - } - - /** - * Gets plural form of a noun - * - * @param string $str Noun to get a plural form of - * @return string Plural form - * @access private - * @static - */ - private static function plural($str) - { - $regexes = array( - '/^(.*?[sxz])$/i' => '\\1es', - '/^(.*?[^aeioudgkprt]h)$/i' => '\\1es', - '/^(.*?[^aeiou])y$/i' => '\\1ies', - ); - foreach ($regexes as $key => $val) - { - $str = preg_replace($key, $val, $str, -1, $count); - if ($count) - { - return $str; - } - } - return $str.'s'; - } - - /** - * Gets singular form of a noun - * - * @param string $str Noun to get singular form of - * @return string Singular form of the noun - * @access private - * @static - */ - private static function singular($str) - { - $regexes = array( - '/^(.*?us)$/i' => '\\1', - '/^(.*?[sxz])es$/i' => '\\1', - '/^(.*?[^aeioudgkprt]h)es$/i' => '\\1', - '/^(.*?[^aeiou])ies$/i' => '\\1y', - '/^(.*?)s$/' => '\\1', - ); - foreach ($regexes as $key => $val) - { - $str = preg_replace($key, $val, $str, -1, $count); - if ($count) - { - return $str; - } - } - return $str; - } + /** + * Specifies which table the model will use, can be overridden + * @var string + * @access public + */ + public $table = null; + + /** + * Specifies which connection the model will use, can be overridden + * but a model can have relationships only with models utilizing the same connection + * @var string + * @access public + */ + public $connection = 'default'; + + /** + * Specifies which column is treated as PRIMARY KEY + * @var string + * @access public + */ + public $id_field = 'id'; + + /** + * You can define 'Belongs to' relationships buy changing this array + * @var array + * @access protected + */ + protected $belongs_to = array(); + + /** + * You can define 'Has one' relationships buy changing this array + * @var array + * @access protected + */ + protected $has_one = array(); + + /** + * You can define 'Has many' relationships buy changing this array + * @var array + * @access protected + */ + protected $has_many = array(); + + /** + * Associated query builder + * @var Query_Database + * @access public + */ + public $query; + + /** + * The name of the model + * @var string + * @access public + */ + public $model_name; + + /** + * Cached properties + * @var array + * @access public + */ + public $cached = array(); + + /** + * An instance of the database connection + * @var DB + * @access protected + */ + protected $db; + + /** + * Current row returned by the database + * @var array + * @access protected + */ + protected $_row = array(); + + /** + * A flag whether the row was loaded from the database + * @var boolean + * @access protected + */ + protected $_loaded = false; + + /** + * Relationships to be preloaded + * @var array + * @access protected + */ + protected $_with = array(); + + /** + * Cached column names for tables + * @var array + * @access protected + */ + protected static $_column_cache = array(); + + /** + * Constructs the model. To use ORM it is enough to + * just create a model like this: + * <code> + * class Fairy_Model extends ORM { } + * </code> + * By default it will assume that the name of your table + * is the plural form of the models' name, the PRIMARY KEY is id, + * and will use the 'default' connection. This behaviour is easy to be + * changed by overriding $table, $id and $db properties. + * + * @return void + * @access public + * @ see $table + * @ see $id + * @ see $db + */ + public function __construct() + { + $this->query = DB::instance($this->connection)->build_query('select'); + $this->model_name = strtolower(get_class($this)); + if (substr($this->model_name, -6) == '_model') + { + $this->model_name = substr($this->model_name, 0, -6); + } + if ($this->table == null) + { + $this->table = ORM::plural($this->model_name); + } + $this->query->table($this->table); + + foreach (array('belongs_to', 'has_one', 'has_many') as $rels) + { + $normalized = array(); + foreach ($this->$rels as $key => $rel) + { + if (!is_array($rel)) + { + $key = $rel; + $rel = array(); + } + $normalized[$key] = $rel; + if (!isset($rel['model'])) + { + $rel['model'] = $normalized[$key]['model'] = $rels == 'has_many' ? ORM::singular($key) : $key; + } + + $normalized[$key]['type'] = $rels; + if (!isset($rel['key'])) + { + $normalized[$key]['key'] = $rels != 'belongs_to' ? ($this->model_name.'_id') : $rel['model'].'_id'; + } + + if ($rels == 'has_many' && isset($rel['through'])) + { + if (!isset($rel['foreign_key'])) + { + $normalized[$key]['foreign_key'] = $rel['model'].'_id'; + } + } + + $normalized[$key]['name'] = $key; + } + $this->$rels = $normalized; + } + } + + /** + * Magic method for call Query_Database methods + * + * @param string $method Method to call + * @param array $arguments Arguments passed to the method + * @return mixed Returns self if parameters were passed. If no parameters where passed returns + * current value for the associated parameter + * @throws Exception If method doesn't exist + * @access public + */ + public function __call($method, $arguments) + { + if (!in_array($method, array('limit', 'offset', 'order_by', 'where'))) + { + throw new Exception("Method '{$method}' doesn't exist on .".get_class($this)); + } + $res = call_user_func_array(array($this->query, $method), $arguments); + if (is_subclass_of($res, 'Query_Database')) + { + return $this; + } + return $res; + } + + /** + * Finds all rows that meet set criteria. + * + * @return Result_ORM Returns Result_ORM that you can use in a 'foreach' loop. + * @throw Exception If the relationship specified using with() does not exist or is not of the belongs_to or has_one type + * @access public + */ + public function find_all() + { + $paths = array(); + if (!empty($this->_with)) + { + $fields = array(); + $this_alias = $this->query->last_alias(); + foreach ($this->columns() as $column) + { + $fields[] = array("{$this_alias}.{$column}", "{$this_alias}__{$column}"); + } + foreach ($this->_with as $target) + { + $model = $this; + $model_alias = $this_alias; + $rels = explode('.', $target); + foreach ($rels as $key => $rel_name) + { + $path = implode('.', array_slice($rels, 0, $key + 1)); + if (isset($paths[$path])) + { + $model = $paths[$path]['model']; + $model_alias = $paths[$path]['alias']; + continue; + } + $alias = $this->query->add_alias(); + $model_rels = array_merge($model->has_one, $model->has_many, $model->belongs_to); + $rel = Misc::arr($model_rels, $rel_name, false); + + if (!$rel) + { + throw new Exception("Model '{$model->model_name}' doesn't have a '{$rel_name}' relation defined"); + } + if ($rel['type'] == 'has_many') + { + throw new Exception("Relationship '{$rel_name}' is of has_many type and cannot be preloaded view with()"); + } + $rel_model = ORM::factory($rel['model']); + + if ($rel['type'] == 'belongs_to') + { + $this->query->join(array($rel_model->table, $alias), array( + $model_alias.'.'.$rel['key'], + $alias.'.'.$rel_model->id_field, + ), 'left'); + } + else + { + $this->query->join(array($rel_model->table, $alias), array( + $model_alias.'.'.$model->id_field, + $alias.'.'.$rel['key'], + ), 'left'); + } + + foreach ($rel_model->columns() as $column) + { + $fields[] = array("{$alias}.{$column}", "{$alias}__{$column}"); + } + $model = $rel_model; + $model_alias = $alias; + $paths[$path] = array('alias' => $alias, 'model' => $model); + } + } + + call_user_func_array(array($this->query, 'fields'), $fields); + } + + return new Result_ORM(get_class($this), $res = $this->query->execute(), $paths); + } + + /** + * Searches for the first row that meets set criteria. If no rows match it still returns an ORM object + * but with its loaded() flag being False. calling save() on such an object will insert a new row. + * + * @return ORM Found item or new object of the current model but with loaded() flag being False + * @access public + */ + public function find() + { + $set_limit = $this->limit(); + $res = $this->limit(1)->find_all()->current(); + $this->limit($set_limit); + return $res; + } + + /** + * Counts all rows that meet set criteria. Ignores limit and offset. + * + * @return int Number of rows + * @access public + */ + public function count_all() + { + $query = clone $this->query; + $query->type('count'); + return $query->execute(); + } + + /** + * Checks if the item is considered to be loaded from the database + * + * @return boolean Returns True if the item was loaded + * @access public + */ + public function loaded() + { + return $this->_loaded; + } + + /** + * Returns the row associated with current ORM item as an associative array + * + * @return array Associative array representing the row + * @access public + */ + public function as_array() + { + return $this->_row; + } + + /** + * Returns a clone of query builder that is being used to set conditions. + * It is useful for example if you let ORM manage building a complex query using it's relationship + * system, then you get the clone of that query and alter it to your liking, + * so there is no need to writing relationship joins yourself. + * + * @return Query_Database A clone of the current query builder + * @access public + */ + public function query() + { + return clone $this->query; + } + + /** + * You can override this method to return additional properties that you would like to use + * in your model. One advantage for using this instead of just overriding __get() is that + * in this way the properties also get cached. + * + * @param string $property The name of the property to get + * @return void + * @access public + */ + public function get($property) + { + + } + + /** + * Magic method that allows accessing row columns as properties and also facilitates + * access to relationships and custom properties defined in get() method. + * If a relationship is being accessed, it will return an ORM model of the related table + * and automatically alter its query so that all your previously set conditions will remain + + * @param string $column Name of the column, property or relationship to get + * @return mixed + * @access public + * @throws Exception If neither property nor a relationship with such name is found + */ + public function __get($column) + { + if (array_key_exists($column, $this->_row)) + { + return $this->_row[$column]; + } + if (array_key_exists($column, $this->cached)) + { + return $this->cached[$column]; + } + if (($val = $this->get($column)) !== null) + { + $this->cached[$column] = $val; + return $val; + } + $relations = array_merge($this->has_one, $this->has_many, $this->belongs_to); + if ($target = Misc::arr($relations, $column, false)) + { + $model = ORM::factory($target['model']); + $model->query = clone $this->query; + if ($this->loaded()) + { + $model->query->where($this->id_field, $this->_row[$this->id_field]); + } + if ($target['type'] == 'has_many' && isset($target['through'])) + { + $last_alias = $model->query->last_alias(); + $through_alias = $model->query->add_alias(); + $new_alias = $model->query->add_alias(); + $model->query->join(array($target['through'], $through_alias), array( + $last_alias.'.'.$this->id_field, + $through_alias.'.'.$target['key'], + ), 'inner'); + $model->query->join(array($model->table, $new_alias), array( + $through_alias.'.'.$target['foreign_key'], + $new_alias.'.'.$model->id_field, + ), 'inner'); + } + else + { + $last_alias = $model->query->last_alias(); + $new_alias = $model->query->add_alias(); + if ($target['type'] == 'belongs_to') + { + $model->query->join(array($model->table, $new_alias), array( + $last_alias.'.'.$target['key'], + $new_alias.'.'.$model->id_field, + ), 'inner'); + } + else + { + $model->query->join(array($model->table, $new_alias), array( + $last_alias.'.'.$this->id_field, + $new_alias.'.'.$target['key'], + ), 'inner'); + } + } + $model->query->fields("$new_alias.*"); + if ($target['type'] != 'has_many' && $this->loaded()) + { + $model = $model->find(); + $this->cached[$column] = $model; + } + return $model; + } + + throw new Exception("Property {$column} not found on {$this->model_name} model."); + } + + /** + * Magic method to update record values when set as properties or to add an ORM item to + * a relation. By assigning an ORM object to a relationship a relationship is created between the + * current item and the passed one Using properties this way is a shortcut to the add() method. + * + * @param string $column Column or relationship name + * @param mixed $val Column value or an ORM item to be added to a relation + * @return void + * @access public + * @see add() + */ + public function __set($column, $val) + { + $relations = array_merge($this->has_one, $this->has_many, $this->belongs_to); + if (array_key_exists($column, $relations)) + { + $this->add($column, $val); + } + else + { + $this->_row[$column] = $val; + } + $this->cached = array(); + } + + /** + * Create a relationship between current item and an other one + * + * @param string $relation Name of the relationship + * @param ORM $model ORM item to create a relationship with + * @return void + * @access public + * @throws Exception Exception If relationship is not defined + * @throws Exception Exception If current item is not in the database yet (isn't considered loaded()) + * @throws Exception Exception If passed item is not in the database yet (isn't considered loaded()) + */ + public function add($relation, $model) + { + + $rels = array_merge($this->has_one, $this->has_many, $this->belongs_to); + $rel = Misc::arr($rels, $relation, false); + if (!$rel) + { + throw new Exception("Model doesn't have a '{$relation}' relation defined"); + } + + if ($rel['type'] == 'belongs_to') + { + + if (!$model->loaded()) + { + throw new Exception("Model must be loaded before added to a belongs_to relationship. Probably you haven't saved it."); + } + + $key = $rel['key']; + $this->$key = $model->_row[$this->id_field]; + if ($this->loaded()) + { + $this->save(); + } + } + elseif (isset($rel['through'])) + { + + if (!$this->loaded()) + { + throw new Exception("Model must be loaded before you try adding 'through' relationships to it. Probably you haven't saved it."); + } + if (!$model->loaded()) + { + throw new Exception("Model must be loaded before added to a 'through' relationship. Probably you haven't saved it."); + } + + $exists = DB::instance($this->connection)->build_query('count') + ->table($rel['through']) + ->where(array( + array($rel['key'], $this->_row[$this->id_field]), + array($rel['foreign_key'], $model->_row[$model->id_field]) + )) + ->execute(); + if (!$exists) + { + DB::instance($this->connection)->build_query('insert') + ->table($rel['through']) + ->data(array( + $rel['key'] => $this->_row[$this->id_field], + $rel['foreign_key'] => $model->_row[$model->id_field] + )) + ->execute(); + } + } + else + { + + if (!$this->loaded()) + { + throw new Exception("Model must be loaded before you try adding 'has_many' relationships to it. Probably you haven't saved it."); + } + + $key = $rel['key']; + $model->$key = $this->_row[$this->id_field]; + if ($model->loaded()) + { + $model->save(); + } + } + $this->cached = array(); + } + + /** + * Removes a relationship between current item and the passed one + * + * @param string $relation Name of the relationship + * @param ORM $model ORM item to remove relationship with. Can be omitted for 'belongs_to' relationships + * @return void + * @access public + * @throws Exception Exception If realtionship is not defined + * @throws Exception Exception If current item is not in the database yet (isn't considered loaded()) + * @throws Exception Exception If passed item is not in the database yet (isn't considered loaded()) + */ + public function remove($relation, $model = null) + { + + if (!$this->loaded()) + { + throw new Exception("Model must be loaded before you try removing relationships from it."); + } + + $rels = array_merge($this->has_one, $this->has_many, $this->belongs_to); + $rel = Misc::arr($rels, $relation, false); + if (!$rel) + { + throw new Exception("Model doesn't have a '{$relation}' relation defined"); + } + + if ($rel['type'] != 'belongs_to' && (!$model || !$model->loaded())) + { + throw new Exception("Model must be loaded before being removed from a has_one or has_many relationship."); + } + if ($rel['type'] == 'belongs_to') + { + $key = $rel['key']; + $this->$key = null; + $this->save(); + } + elseif (isset($rel['through'])) + { + DB::instance($this->connection)->build_query('delete') + ->table($rel['through']) + ->where(array( + array($rel['key'], $this->_row[$this->id_field]), + array($rel['foreign_key'], $model->_row[$model->id_field]) + )) + ->execute(); + } + else + { + $key = $rel['key']; + $model->$key = null; + $model->save(); + } + $this->cached = array(); + } + + /** + * Gets name column names of the table associated with the model. + * + * @return array Array of column names + * @access public + */ + public function columns() + { + if (!isset(ORM::$_column_cache[$this->table])) + { + ORM::$_column_cache[$this->table] = DB::instance($this->connection)->list_columns($this->table); + } + return ORM::$_column_cache[$this->table]; + } + + /** + * Defines which relationships should be preloaded. You can only preload + * belongs_to and has_one relationships. You can use the dot notation to + * preload deep relationsips, e.g. 'tree.protector' will preload the tree + * that a fairy lives in and also preload the protector of that tree. + * + * @param string $relationsip,... List of relationships to preload + * @return ORM Returns itself + * @access public + */ + public function with() + { + $this->_with = func_get_args(); + return $this; + } + + /** + * Deletes current item from the database + * + * @return void + * @access public + * @throws Exception If the item is not in the database, e.g. is not loaded() + */ + public function delete() + { + if (!$this->loaded()) + { + throw new Exception("Cannot delete an item that wasn't selected from database"); + } + DB::instance($this->connection)->build_query('delete') + ->table($this->table) + ->where($this->id_field, $this->_row[$this->id_field]) + ->execute(); + $this->cached = array(); + } + + /** + * Deletes all items that meet set conditions. Use in the same way + * as you would a find_all() method. + * + * @return ORM Returns self + * @access public + */ + public function delete_all() + { + $query = clone $this->query; + $query->type('delete'); + $query->execute(); + return $this; + } + + /** + * Saves the item back to the database. If item is loaded() it will result + * in an update, otherwise a new row will be inserted + * + * @return ORM Returns self + * @access public + */ + public function save() + { + if ($this->loaded()) + { + $query = DB::instance($this->connection)->build_query('update') + ->table($this->table) + ->where($this->id_field, $this->_row[$this->id_field]); + } + else + { + $query = DB::instance($this->connection)->build_query('insert') + ->table($this->table); + } + $query->data($this->_row); + $query->execute(); + + if ($this->loaded()) + { + $id = $this->_row[$this->id_field]; + } + else + { + $id = DB::instance($this->connection)->get_insert_id(); + } + $row = (array) DB::instance($this->connection)->build_query('select') + ->table($this->table) + ->where($this->id_field, $id)->execute()->current(); + $this->values($row, true); + return $this; + } + + /** + * Batch updates item columns using an associative array + * + * @param array $row Associative array of key => value pairs + * @param boolean $set_loaded Flag to consider the ORM item loaded. Useful if you selected + * the row from the database and want to wrap it in ORM + * @return ORM Returns self + * @access public + */ + public function values($row, $set_loaded = false) + { + $this->_row = array_merge($this->_row, $row); + if ($set_loaded) + { + $this->_loaded = true; + } + $this->cached = array(); + return $this; + } + + /** + * Initializes ORM model by name, and optionally fetches an item by id + * + * @param string $name Model name + * @param mixed $id If set ORM will try to load the item with this id from the database + * @return ORM ORM model, either empty or preloaded + * @access public + * @static + */ + public static function factory($name, $id = null) + { + $model = $name.'_Model'; + $model = new $model; + if ($id != null) + { + $model = $model->where($model->id_field, $id)->find(); + $model->values(array($model->id_field => $id)); + } + return $model; + } + + /** + * Gets plural form of a noun + * + * @param string $str Noun to get a plural form of + * @return string Plural form + * @access private + * @static + */ + private static function plural($str) + { + $regexes = array( + '/^(.*?[sxz])$/i' => '\\1es', + '/^(.*?[^aeioudgkprt]h)$/i' => '\\1es', + '/^(.*?[^aeiou])y$/i' => '\\1ies', + ); + foreach ($regexes as $key => $val) + { + $str = preg_replace($key, $val, $str, -1, $count); + if ($count) + { + return $str; + } + } + return $str.'s'; + } + + /** + * Gets singular form of a noun + * + * @param string $str Noun to get singular form of + * @return string Singular form of the noun + * @access private + * @static + */ + private static function singular($str) + { + $regexes = array( + '/^(.*?us)$/i' => '\\1', + '/^(.*?[sxz])es$/i' => '\\1', + '/^(.*?[^aeioudgkprt]h)es$/i' => '\\1', + '/^(.*?[^aeiou])ies$/i' => '\\1y', + '/^(.*?)s$/' => '\\1', + ); + foreach ($regexes as $key => $val) + { + $str = preg_replace($key, $val, $str, -1, $count); + if ($count) + { + return $str; + } + } + return $str; + } + }
\ No newline at end of file diff --git a/modules/orm/classes/orm/result.php b/modules/orm/classes/orm/result.php index 47757cf..cb0754c 100644 --- a/modules/orm/classes/orm/result.php +++ b/modules/orm/classes/orm/result.php @@ -12,211 +12,211 @@ class Result_ORM implements Iterator { - /** - * Name of the model that the rows belong to - * @var string - * @access private - */ - private $_model; - - /** - * Database result - * @var Result_Database - * @access private - */ - private $_dbresult; - - /** - * Rules for preloaded relationships - * @var array - * @access private - */ - private $_with = array(); - - /** - * Initialized an Result_ORM with which model to use and which result to - * iterate over - * - * @param string $model Model name - * @param Result_Database $dbresult Database result - * @param array $with Array of rules for preloaded relationships - * @return void - * @access public - */ - public function __construct($model, $dbresult, $with = array()) - { - $this->_model = $model; - $this->_dbresult = $dbresult; - foreach ($with as $path => $rel) - { - $this->_with[] = array( - 'path' => explode('.', $path), - 'path_count' => count(explode('.', $path)), - 'model' => $rel['model'], - 'columns' => $rel['model']->columns(), - ); - } - } - - /** - * Rewinds database cursor to the first row - * - * @return void - * @access public - */ - function rewind() - { - $this->_dbresult->rewind(); - } - - /** - * Gets an ORM Model of the current row - * - * @return ORM Model of the current row of the result set - * @access public - */ - function current() - { - $model = new $this->_model; - - if (!$this->_dbresult->valid()) - { - return $model; - } - - if (empty($this->_with)) - { - return $model->values((array) $this->_dbresult->current(), true); - } - - $data = (array) $this->_dbresult->current(); - - $model_data = array(); - foreach ($model->columns() as $column) - { - $model_data[$column] = array_shift($data); - } - $model->values($model_data, true); - - foreach ($this->_with as $rel) - { - $rel_data = array(); - foreach ($rel['columns'] as $column) - { - $rel_data[$column] = array_shift($data); - } - $rel['model']->values($rel_data, true); - - $owner = $model; - foreach ($rel['path'] as $key => $child) - { - if ($key == $rel['path_count'] - 1) - { - $owner->cached[$child] = $rel['model']; - } - else - { - $owner = $owner->cached[$child]; - } - } - } - - return $model; - } - - /** - * Gets current rows' index number - * - * @return int Row number - * @access public - */ - function key() - { - return $this->_dbresult->key(); - } - - /** - * Iterates to the next row in the result - * - * @return void - * @access public - */ - function next() - { - $this->_dbresult->next(); - } - - /** - * Checks if current row is valid. - * - * @return bool returns false if we reach the end of the result set. - * @access public - */ - function valid() - { - return $this->_dbresult->valid(); - } - - /** - * Returns an array of all rows as ORM objects if $rows is False, - * or just an array of result rows with each row being a standard object, - * this can be useful for functions like json_encode. - * - * @param boolean $rows Whether to return just rows and not ORM objects - * @return array Array of ORM objects or standard objects representing rows - * @access public - */ - public function as_array($rows = false) - { - if (!$rows) - { - $arr = array(); - foreach ($this as $row) - $arr[] = $row; - return $arr; - } - - if (empty($this->_with)) - { - return $this->_dbresult->as_array(); - } - - $arr = array(); - $model = new $this->_model; - foreach ($this->_dbresult as $data) - { - $row = new stdClass; - $data = (array) $data; - foreach ($model->columns() as $column) - { - $row->$column = array_shift($data); - } - - foreach ($this->_with as $rel) - { - $rel_data = new StdClass; - foreach ($rel['columns'] as $column) - { - $rel_data->$column = array_shift($data); - } - - $owner = &$row; - foreach ($rel['path'] as $key => $child) - { - if ($key == $rel['path_count'] - 1) - { - $owner->$child = $rel_data; - } - else - { - $owner = &$owner->$child; - } - } - } - $arr[] = $row; - } - - return $arr; - } + /** + * Name of the model that the rows belong to + * @var string + * @access private + */ + private $_model; + + /** + * Database result + * @var Result_Database + * @access private + */ + private $_dbresult; + + /** + * Rules for preloaded relationships + * @var array + * @access private + */ + private $_with = array(); + + /** + * Initialized an Result_ORM with which model to use and which result to + * iterate over + * + * @param string $model Model name + * @param Result_Database $dbresult Database result + * @param array $with Array of rules for preloaded relationships + * @return void + * @access public + */ + public function __construct($model, $dbresult, $with = array()) + { + $this->_model = $model; + $this->_dbresult = $dbresult; + foreach ($with as $path => $rel) + { + $this->_with[] = array( + 'path' => explode('.', $path), + 'path_count' => count(explode('.', $path)), + 'model' => $rel['model'], + 'columns' => $rel['model']->columns(), + ); + } + } + + /** + * Rewinds database cursor to the first row + * + * @return void + * @access public + */ + function rewind() + { + $this->_dbresult->rewind(); + } + + /** + * Gets an ORM Model of the current row + * + * @return ORM Model of the current row of the result set + * @access public + */ + function current() + { + $model = new $this->_model; + + if (!$this->_dbresult->valid()) + { + return $model; + } + + if (empty($this->_with)) + { + return $model->values((array) $this->_dbresult->current(), true); + } + + $data = (array) $this->_dbresult->current(); + + $model_data = array(); + foreach ($model->columns() as $column) + { + $model_data[$column] = array_shift($data); + } + $model->values($model_data, true); + + foreach ($this->_with as $rel) + { + $rel_data = array(); + foreach ($rel['columns'] as $column) + { + $rel_data[$column] = array_shift($data); + } + $rel['model']->values($rel_data, true); + + $owner = $model; + foreach ($rel['path'] as $key => $child) + { + if ($key == $rel['path_count'] - 1) + { + $owner->cached[$child] = $rel['model']; + } + else + { + $owner = $owner->cached[$child]; + } + } + } + + return $model; + } + + /** + * Gets current rows' index number + * + * @return int Row number + * @access public + */ + function key() + { + return $this->_dbresult->key(); + } + + /** + * Iterates to the next row in the result + * + * @return void + * @access public + */ + function next() + { + $this->_dbresult->next(); + } + + /** + * Checks if current row is valid. + * + * @return bool returns false if we reach the end of the result set. + * @access public + */ + function valid() + { + return $this->_dbresult->valid(); + } + + /** + * Returns an array of all rows as ORM objects if $rows is False, + * or just an array of result rows with each row being a standard object, + * this can be useful for functions like json_encode. + * + * @param boolean $rows Whether to return just rows and not ORM objects + * @return array Array of ORM objects or standard objects representing rows + * @access public + */ + public function as_array($rows = false) + { + if (!$rows) + { + $arr = array(); + foreach ($this as $row) + $arr[] = $row; + return $arr; + } + + if (empty($this->_with)) + { + return $this->_dbresult->as_array(); + } + + $arr = array(); + $model = new $this->_model; + foreach ($this->_dbresult as $data) + { + $row = new stdClass; + $data = (array) $data; + foreach ($model->columns() as $column) + { + $row->$column = array_shift($data); + } + + foreach ($this->_with as $rel) + { + $rel_data = new StdClass; + foreach ($rel['columns'] as $column) + { + $rel_data->$column = array_shift($data); + } + + $owner = &$row; + foreach ($rel['path'] as $key => $child) + { + if ($key == $rel['path_count'] - 1) + { + $owner->$child = $rel_data; + } + else + { + $owner = &$owner->$child; + } + } + } + $arr[] = $row; + } + + return $arr; + } }
\ No newline at end of file |