Permalink
Browse files

Merge branch 'develop'

* develop:
  Update changelog
  Fix incorrect tests, see issue #12
  • Loading branch information...
2 parents 94c14f9 + ed75ae6 commit d8f5967b42cfe0540867e9a3a2e2d8bcbbc9b156 @j4mie committed Jan 30, 2011
Showing with 135 additions and 19 deletions.
  1. +4 −0 README.markdown
  2. +129 −17 test/idiorm.php
  3. +2 −2 test/test_queries.php
View
@@ -33,6 +33,10 @@ Changelog
* Add `is_dirty` method
+#### 1.1.1 - released 2011-01-30
+
+* Fix incorrect tests, see issue #12
+
Philosophy
----------
View
@@ -63,6 +63,7 @@ class ORM {
'driver_options' => null,
'identifier_quote_character' => null, // if this is null, will be autodetected
'logging' => false,
+ 'caching' => false,
);
// Database connection, instance of the PDO class
@@ -74,6 +75,9 @@ class ORM {
// Log of all queries run, only populated if logging is enabled
protected static $_query_log = array();
+ // Query cache, only used if query caching is enabled
+ protected static $_query_cache = array();
+
// --------------------------- //
// --- INSTANCE PROPERTIES --- //
// --------------------------- //
@@ -96,6 +100,9 @@ class ORM {
// Join sources
protected $_join_sources = array();
+ // Should the query include a DISTINCT keyword?
+ protected $_distinct = false;
+
// Is this a raw query?
protected $_is_raw_query = false;
@@ -117,6 +124,9 @@ class ORM {
// ORDER BY
protected $_order_by = array();
+ // GROUP BY
+ protected $_group_by = array();
+
// The data for a hydrated instance of the class
protected $_data = array();
@@ -325,6 +335,17 @@ public function use_id_column($id_column) {
}
/**
+ * Create an ORM instance from the given row (an associative
+ * array of data fetched from the database)
+ */
+ protected function _create_instance_from_row($row) {
+ $instance = self::for_table($this->_table_name);
+ $instance->use_id_column($this->_instance_id_column);
+ $instance->hydrate($row);
+ return $instance;
+ }
+
+ /**
* Tell the ORM that you are expecting a single result
* back from your query, and execute it. Will return
* a single instance of the ORM class, or false if no
@@ -334,13 +355,17 @@ public function use_id_column($id_column) {
* lookup on the table.
*/
public function find_one($id=null) {
- if(!is_null($id)) {
+ if (!is_null($id)) {
$this->where_id_is($id);
}
$this->limit(1);
- $statement = $this->_run();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
- return $result ? self::for_table($this->_table_name)->use_id_column($this->_instance_id_column)->hydrate($result) : $result;
+ $rows = $this->_run();
+
+ if (empty($rows)) {
+ return false;
+ }
+
+ return $this->_create_instance_from_row($rows[0]);
}
/**
@@ -350,12 +375,8 @@ public function find_one($id=null) {
* no rows were returned.
*/
public function find_many() {
- $statement = $this->_run();
- $instances = array();
- while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
- $instances[] = self::for_table($this->_table_name)->use_id_column($this->_instance_id_column)->hydrate($row);
- }
- return $instances;
+ $rows = $this->_run();
+ return array_map(array($this, '_create_instance_from_row'), $rows);
}
/**
@@ -365,9 +386,8 @@ public function find_many() {
*/
public function count() {
$this->select_expr('COUNT(*)', 'count');
- $statement = $this->_run();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
- return isset($result['count']) ? (int) $result['count'] : 0;
+ $result = $this->find_one();
+ return ($result !== false && isset($result->count)) ? (int) $result->count : 0;
}
/**
@@ -451,6 +471,14 @@ public function select_expr($expr, $alias=null) {
}
/**
+ * Add a DISTINCT keyword before the list of columns in the SELECT query
+ */
+ public function distinct() {
+ $this->_distinct = true;
+ return $this;
+ }
+
+ /**
* Internal method to add a JOIN source to the query.
*
* The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this
@@ -721,6 +749,15 @@ public function order_by_asc($column_name) {
}
/**
+ * Add a column to the list of columns to GROUP BY
+ */
+ public function group_by($column_name) {
+ $column_name = $this->_quote_identifier($column_name);
+ $this->_group_by[] = $column_name;
+ return $this;
+ }
+
+ /**
* Build a SELECT statement based on the clauses that have
* been passed to this instance by chaining method calls.
*/
@@ -738,6 +775,7 @@ protected function _build_select() {
$this->_build_select_start(),
$this->_build_join(),
$this->_build_where(),
+ $this->_build_group_by(),
$this->_build_order_by(),
$this->_build_limit(),
$this->_build_offset(),
@@ -749,7 +787,13 @@ protected function _build_select() {
*/
protected function _build_select_start() {
$result_columns = join(', ', $this->_result_columns);
+
+ if ($this->_distinct) {
+ $result_columns = 'DISTINCT ' . $result_columns;
+ }
+
$fragment = "SELECT {$result_columns} FROM " . $this->_quote_identifier($this->_table_name);
+
if (!is_null($this->_table_alias)) {
$fragment .= " " . $this->_quote_identifier($this->_table_alias);
}
@@ -786,6 +830,16 @@ protected function _build_where() {
}
/**
+ * Build GROUP BY
+ */
+ protected function _build_group_by() {
+ if (count($this->_group_by) === 0) {
+ return '';
+ }
+ return "GROUP BY " . join(", ", $this->_group_by);
+ }
+
+ /**
* Build ORDER BY
*/
protected function _build_order_by() {
@@ -845,24 +899,82 @@ protected function _quote_identifier($identifier) {
/**
* This method performs the actual quoting of a single
- * part of an identifier. Currently uses backticks, which
- * are compatible with (at least) MySQL and SQLite.
+ * part of an identifier, using the identifier quote
+ * character specified in the config (or autodetected).
*/
protected function _quote_identifier_part($part) {
+ if ($part === '*') {
+ return $part;
+ }
$quote_character = self::$_config['identifier_quote_character'];
return $quote_character . $part . $quote_character;
}
/**
+ * Create a cache key for the given query and parameters.
+ */
+ protected static function _create_cache_key($query, $parameters) {
+ $parameter_string = join(',', $parameters);
+ $key = $query . ':' . $parameter_string;
+ return sha1($key);
+ }
+
+ /**
+ * Check the query cache for the given cache key. If a value
+ * is cached for the key, return the value. Otherwise, return false.
+ */
+ protected static function _check_query_cache($cache_key) {
+ if (isset(self::$_query_cache[$cache_key])) {
+ return self::$_query_cache[$cache_key];
+ }
+ return false;
+ }
+
+ /**
+ * Clear the query cache
+ */
+ public static function clear_cache() {
+ self::$_query_cache = array();
+ }
+
+ /**
+ * Add the given value to the query cache.
+ */
+ protected static function _cache_query_result($cache_key, $value) {
+ self::$_query_cache[$cache_key] = $value;
+ }
+
+ /**
* Execute the SELECT query that has been built up by chaining methods
- * on this class. Return the executed PDOStatement object.
+ * on this class. Return an array of rows as associative arrays.
*/
protected function _run() {
$query = $this->_build_select();
+ $caching_enabled = self::$_config['caching'];
+
+ if ($caching_enabled) {
+ $cache_key = self::_create_cache_key($query, $this->_values);
+ $cached_result = self::_check_query_cache($cache_key);
+
+ if ($cached_result !== false) {
+ return $cached_result;
+ }
+ }
+
self::_log_query($query, $this->_values);
$statement = self::$_db->prepare($query);
$statement->execute($this->_values);
- return $statement;
+
+ $rows = array();
+ while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
+ $rows[] = $row;
+ }
+
+ if ($caching_enabled) {
+ self::_cache_query_result($cache_key, $rows);
+ }
+
+ return $rows;
}
/**
@@ -175,7 +175,7 @@ public function authors() {
$book = Model::factory('Book')->find_one(1);
$authors = $book->authors()->find_many();
- $expected = "SELECT `author`.`*` FROM `author` JOIN `author_book` ON `author`.`id` = `author_book`.`author_id` WHERE `author_book`.`book_id` = '1'";
+ $expected = "SELECT `author`.* FROM `author` JOIN `author_book` ON `author`.`id` = `author_book`.`author_id` WHERE `author_book`.`book_id` = '1'";
Tester::check_equal("has_many_through relation", $expected);
class AuthorTwo extends Model {
@@ -192,7 +192,7 @@ public function authors() {
$book2 = Model::factory('BookTwo')->find_one(1);
$authors2 = $book2->authors()->find_many();
- $expected = "SELECT `author_two`.`*` FROM `author_two` JOIN `wrote_the_book` ON `author_two`.`id` = `wrote_the_book`.`custom_author_id` WHERE `wrote_the_book`.`custom_book_id` = '1'";
+ $expected = "SELECT `author_two`.* FROM `author_two` JOIN `wrote_the_book` ON `author_two`.`id` = `wrote_the_book`.`custom_author_id` WHERE `wrote_the_book`.`custom_book_id` = '1'";
Tester::check_equal("has_many_through relation with custom intermediate model and key names", $expected);
Tester::report();

0 comments on commit d8f5967

Please sign in to comment.