From bf162323f03aa819a71649aa5758a8255bd3534e Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 17 Apr 2012 14:22:51 -0400 Subject: [PATCH] Implemented 'having' and 'or_having' methods --- README.md | 2 +- classes/query_builder.php | 66 ++++++++++++++++++++--- tests/core/db_qb_test.php | 28 ++++++++++ tests/databases/firebird/firebird-qb.php | 28 ++++++++++ tests/db_files/FB_TEST_DB.FDB | Bin 802816 -> 802816 bytes 5 files changed, 117 insertions(+), 7 deletions(-) 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 9d4bfa6de69421a6bf748d63ec1d12bcd85aa29b..ace4c71eb2fd017aadf808fd27903cbe85ffe3ff 100644 GIT binary patch delta 2533 zcmbVOO>7fK6n?w*`fmuCBo4o-v57+>6-c%iK~KBM?NrR0LF|RaBWOs-o6Hj^n^KE#l|KWkuhc*%z-#j6%oSpKoUN&G)_c zW}d56w_0^Sv*6?L!+B^oDXOs$(bLZX{DT*OZQc9};PkQD$i-b|f&?3XXEdB*EQ)AwX1F0FrOoqXnY^2SNIb9Qzl^HIth&)gdq z@mn(w62>;)C#?avpBRRCdKf0sqwrmNKgd)b?x*wcEV&;hQa$iZdI)|=4#D@yQTP_0 zC$aA!_MJ`+!#KWEyOrpHzY_=8tbYE4cL#{MX*S3ie6w_yt=~{KxTPg@08NhTF&h}` zxl`wVW*iH@WlWaH{GxUszMwVBdL4j6{YU1&nZ#ik0QKp%mrP20-5$_~?aPHlb^V1X z4y1{gX^x(m3Dl!yk+l}pk2tO_u8I-s4|RQ26lT~etxHBRhIGTrJlzs8BW1`-_c7MW zu!pfx-o{3$1+{l68-@Oev``sJZmXg`7iy%eutS7CKswlrxYC>vS0iy_>iC-2k^Hn$ zo}NDPyK1h8+>8}yJ?0y&^1}Jb+bW4=R3&4r5^XWoDlry}`;1qdg{~?;qyY9JakpeB zgesvl_qNIfty1k-71^B^$~5ZWh0nl&vs2UM6R=<+m~LVOe^Z}46|Q=)D*C^m<%vG0 z6Jry!vr~-C3>z3{=?iqnd>8uaQB!1h>wLo*5x$svF3fAb2@@4{?U^WA^Bbb@D@CE8 zPBp~`U(L~0p|yxBZw1E8oiQgIW5fMQ`QheV)!r4cCyPFcu*7#act|V<`uuQNW2PtY z)!n8j9xR_&sc0E{geNEydhQgEq!>ClyOdi+9&Xuhr~w<&V!&59WK+$2YIA2NS-6JzZrT#2!E zD;@!@4!t61y|>=hR`uE~a&dRPt;o6agw7pz6Mf8=^)Y6BjP(vgeT@!%+kyt^D~$C8 z@E##;s6l^AA&oV3t`B0acZ+IcL+Us64&6j6b7!rLSu5i~+McMj9%}6+3?iuiAFcJR zYIW*H>$dJ%>ydNlF>P@7>x~)9tj&&tbrm{8Q4x=8FGN)8LkW5Us*$FlU#R|tHBl`; z82;yFw^?`CylE^Lv5mV;wh5@1m+m4$q4bo4t*DjsBqXNVgQ2g@wk%~S zS&hCRi>*CPK|FZr$%9sCJ&54JlVZhVpGYW74e*lsC>?XjQFWs1UV}u77i13=DK;gYJ z`b}l-YW#u8jLJ-unSspYGLz5Dj?4^ZrjVH@u9;%asEgP0<>J-B>%UACrwf^cKb|=NtlA&$;JQ!$rFEr1Yb4W-K*6t3fRpLT7nvxgLNA~l(I)}I!hS5il(B-@g($6vhEQjb3@UlmrO!uM3;J?vBciE#) zYBGb+BietV$C0d4ueeyJ`9(L<>x56z2}hGo@7p8yP^YBk#)JhZ$eC$UlbD#|>JrC} zxq8@bg-r9as^yu^FA8ohL#`TMhH0-16}0T>zjb*WW|m|IGvyhcEf#Towq?)%p{v8a zln~L~=tc5e%YMCqx}CRg-Pe_cxBI=OyOg+)szB!woLQ1sM3$Jm2Km`$NENX*nJTU? z%{z{gy47)R#&(WFt?JnAlvH_pVNI6@Nw57)*6W<}Rb|0tgBx!koFh*vbsLC)luDFX zd?qoGObK3!d!U4{IibTXs+sT&$lER`j$m&u-VR% z*^csN6^bPIgpb5jkGPz~#0TRv2qFCkI+rodALu=46etm)Kb!G0C3~j z*wkYYE=J}$?aWnK=oym5Vnc@H1T2xm1tn&)^~jLf5RIG$4vK0t_IHR}xZC&9s)aV| zkj-*fX0%5?cWgUWvWOf(l7+|Z{%($Mk3AP-`8dlN*^6ts8XlHAWq=%Te0c8rUxz23 TST1>V8ofBO-6?zJfqv~D-Z5Hv