diff --git a/README.md b/README.md index 9f3089b..67b235b 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Create a connection array or object similar to this: The parameters required depend on the database. ### Running Queries -Query uses the same interface as CodeIgniter's [Active Record class](http://codeigniter.com/user_guide/database/active_record.html). However, it does not implement the `having`, `or_having`, `insert_batch` or `update_batch` methods. +Query uses the same interface as CodeIgniter's [Active Record class](http://codeigniter.com/user_guide/database/active_record.html). However, it does not implement the `insert_batch` or `update_batch` methods. #### Retrieving Results diff --git a/classes/query_builder.php b/classes/query_builder.php index 39c21c5..b17a420 100644 --- a/classes/query_builder.php +++ b/classes/query_builder.php @@ -52,6 +52,9 @@ class Query_Builder { // 'string' => 'k=?' // ) private $query_map; + + // Map for having clause + private $having_map; // Convenience property for connection management public $conn_name = ""; @@ -493,7 +496,28 @@ class Query_Builder { */ public function having($key, $val=array()) { - // @todo Implement having + $where = $this->_where($key, $val); + + // Create key/value placeholders + foreach($where as $f => $val) + { + // Split each key by spaces, in case there + // is an operator such as >, <, !=, etc. + $f_array = explode(' ', trim($f)); + + $item = $this->quote_ident($f_array[0]); + + // Simple key value, or an operator + $item .= (count($f_array) === 1) ? '= ?' : " {$f_array[1]} ?"; + + // Put in the query map for select statements + $this->having_map[] = array( + 'conjunction' => ( ! empty($this->having_map)) ? ' AND ' : ' HAVING ', + 'string' => $item + ); + } + + return $this; } // -------------------------------------------------------------------------- @@ -507,7 +531,28 @@ class Query_Builder { */ public function or_having($key, $val=array()) { - // @todo Implement or_having + $where = $this->_where($key, $val); + + // Create key/value placeholders + foreach($where as $f => $val) + { + // Split each key by spaces, in case there + // is an operator such as >, <, !=, etc. + $f_array = explode(' ', trim($f)); + + $item = $this->quote_ident($f_array[0]); + + // Simple key value, or an operator + $item .= (count($f_array) === 1) ? '= ?' : " {$f_array[1]} ?"; + + // Put in the query map for select statements + $this->having_map[] = array( + 'conjunction' => ( ! empty($this->having_map)) ? ' OR ' : ' HAVING ', + 'string' => $item + ); + } + + return $this; } // -------------------------------------------------------------------------- @@ -515,7 +560,7 @@ class Query_Builder { // -------------------------------------------------------------------------- /** - * Do all the repeditive stuff for where type methods + * Do all the repeditive stuff for where/having type methods * * @param mixed $key * @param mixed $val @@ -569,7 +614,7 @@ class Query_Builder { $item = $this->quote_ident($f_array[0]); // Simple key value, or an operator - $item .= (count($f_array === 1)) ? '= ?' : " {$f_array[1]} ?"; + $item .= (count($f_array) === 1) ? '= ?' : " {$f_array[1]} ?"; // Put in the query map for select statements $this->query_map[] = array( @@ -997,7 +1042,7 @@ class Query_Builder { $sql = $this->_compile(); // Do prepared statements for anything involving a "where" clause - if ( ! empty($this->query_map)) + if ( ! empty($this->query_map) || ! empty($this->having_map)) { $result = $this->prepare_execute($sql, $this->values); } @@ -1218,7 +1263,7 @@ class Query_Builder { $this->$name = NULL; } - // Set values as an empty array + // Set empty arrays $this->values = array(); // Set select string as an empty string, for proper handling @@ -1266,6 +1311,15 @@ class Query_Builder { { $sql .= $this->group_string; } + + // Set the having string + if ( ! empty($this->having_map)) + { + foreach($this->having_map as $h) + { + $sql .= $h['conjunction'] . $h['string']; + } + } // Set the order_by string if ( ! empty($this->order_string)) diff --git a/tests/core/db_qb_test.php b/tests/core/db_qb_test.php index 8bb51db..b253534 100644 --- a/tests/core/db_qb_test.php +++ b/tests/core/db_qb_test.php @@ -55,6 +55,34 @@ abstract class QBTest extends UnitTestCase { $this->assertIsA($query, 'PDOStatement'); } + function TestHaving() + { + if (empty($this->db)) return; + + $query = $this->db->select('id') + ->from('create_test') + ->group_by('id') + ->having(array('id >' => 1)) + ->having('id !=', 3) + ->get(); + + $this->assertIsA($query, 'PDOStatement'); + } + + function TestOrHaving() + { + if (empty($this->db)) return; + + $query = $this->db->select('id') + ->from('create_test') + ->group_by('id') + ->having(array('id >' => 1)) + ->or_having('id !=', 3) + ->get(); + + $this->assertIsA($query, 'PDOStatement'); + } + function TestGetViews() { if (empty($this->db)) return; diff --git a/tests/databases/firebird/firebird-qb.php b/tests/databases/firebird/firebird-qb.php index ee3bb86..96ec990 100644 --- a/tests/databases/firebird/firebird-qb.php +++ b/tests/databases/firebird/firebird-qb.php @@ -56,6 +56,34 @@ class FirebirdQBTest extends QBTest { $this->assertIsA($query, 'Firebird_Result'); } + function TestHaving() + { + if (empty($this->db)) return; + + $query = $this->db->select('id') + ->from('create_test') + ->group_by('id') + ->having(array('id >' => 1)) + ->having('id !=', 3) + ->get(); + + $this->assertIsA($query, 'Firebird_Result'); + } + + function TestOrHaving() + { + if (empty($this->db)) return; + + $query = $this->db->select('id') + ->from('create_test') + ->group_by('id') + ->having(array('id >' => 1)) + ->or_having('id !=', 3) + ->get(); + + $this->assertIsA($query, 'Firebird_Result'); + } + function TestSelectWhereGet() { $query = $this->db->select('id, key as k, val') diff --git a/tests/db_files/FB_TEST_DB.FDB b/tests/db_files/FB_TEST_DB.FDB index 9d4bfa6..ace4c71 100644 Binary files a/tests/db_files/FB_TEST_DB.FDB and b/tests/db_files/FB_TEST_DB.FDB differ