@@ -425,7 +425,8 @@ public function read(Model $model, $queryData = array(), $recursive = null) {
425425
426426 // Check if we are doing a 'count' .. this is kinda ugly but i couldn't find a better way to do this, yet
427427 if (is_string ($ queryData ['fields ' ]) && $ queryData ['fields ' ] === 'COUNT(*) AS ' . $ this ->name ('count ' )) {
428- $ queryData ['fields ' ] = array ();
428+ // limit a count request to a minimum field set
429+ $ queryData ['fields ' ] = array ($ model ->primaryKey );
429430 }
430431
431432 // Prepare query data ------------------------
@@ -459,6 +460,11 @@ public function read(Model $model, $queryData = array(), $recursive = null) {
459460 $ resultSet = ldap_get_entries ($ this ->database , $ res );
460461 $ resultSet = $ this ->_ldapFormat ($ model , $ resultSet );
461462
463+ // If an offset was specified, apply it now
464+ if ($ queryData ['offset ' ]) {
465+ $ resultSet = array_slice ($ resultSet , $ queryData ['offset ' ]);
466+ $ this ->count = count ($ resultSet );
467+ }
462468 // Query on linked models ----------------------
463469 if ($ model ->recursive > 0 ) {
464470 foreach ($ model ->_associations as $ type ) {
@@ -487,7 +493,12 @@ public function read(Model $model, $queryData = array(), $recursive = null) {
487493 }
488494
489495 // Add the count field to the resultSet (needed by find() to work out how many entries we got back .. used when $model->exists() is called)
490- $ resultSet [0 ][0 ]['count ' ] = $ this ->lastNumRows ();
496+ if ($ queryData ['offset ' ]) {
497+ // if an offset was applied, net count instead of the actual count
498+ $ resultSet [0 ][0 ]['count ' ] = $ this ->count ;
499+ } else {
500+ $ resultSet [0 ][0 ]['count ' ] = $ this ->lastNumRows ();
501+ }
491502 return $ resultSet ;
492503 }
493504
@@ -1022,29 +1033,29 @@ protected function _conditions($conditions, $model) {
10221033 if (is_array ($ conditions ) && count ($ conditions ) === 1 ) {
10231034 $ sqlHack = "$ name. $ key " ;
10241035 $ conditions = str_ireplace ($ sqlHack , $ key , $ conditions );
1025- foreach ($ conditions as $ k => $ v ) {
1036+ $ k = array_pop (array_keys ($ conditions ));
1037+ $ v = $ conditions [$ k ];
1038+ if (is_string ($ v )) {
10261039 if ($ k === $ name . '.dn ' ) {
10271040 $ res = substr ($ v , 0 , strpos ($ v , ', ' ));
10281041 } elseif (($ k === $ sqlHack ) && (empty ($ v ) || $ v === '* ' )) {
1029- $ res = ' objectclass =* ' ;
1042+ $ res = $ model -> primaryKey . ' =* ' ;
10301043 } elseif ($ k === $ sqlHack ) {
10311044 $ res = "$ key= $ v " ;
10321045 } else {
10331046 $ res = "$ k= $ v " ;
10341047 }
1048+ $ conditions = $ res ;
10351049 }
1036- $ conditions = $ res ;
10371050 }
10381051
10391052 if (is_array ($ conditions )) {
10401053 // Conditions expressed as an array
1041- if (empty ($ conditions )) {
1042- $ res = 'objectclass=* ' ;
1043- }
1054+ $ conditions = $ this ->_conditionsArrayToString ($ conditions );
10441055 }
10451056
10461057 if (empty ($ conditions )) {
1047- $ res = ' objectclass =* ' ;
1058+ $ res = $ model -> primaryKey . ' =* ' ;
10481059 } else {
10491060 $ res = $ conditions ;
10501061 }
@@ -1055,60 +1066,69 @@ protected function _conditions($conditions, $model) {
10551066 * Convert an array into a ldap condition string
10561067 *
10571068 * @param array $conditions condition
1069+ * @param string $join The type of opeation to use to join the conditions (&, |, !)
10581070 * @return string
10591071 */
1060- protected function _conditionsArrayToString ($ conditions ) {
1061- $ opsRec = array ('and ' => array ('prefix ' => '& ' ), 'or ' => array ('prefix ' => '| ' ));
1062- $ opsNeg = array ('and not ' => array (), 'or not ' => array (), 'not equals ' => array ());
1063- $ opsTer = array ('equals ' => array ('null ' => '* ' ));
1064-
1065- $ ops = array_merge ($ opsRec , $ opsNeg , $ opsTer );
1066-
1072+ protected function _conditionsArrayToString ($ conditions , $ join = '& ' ) {
10671073 if (is_array ($ conditions )) {
1068- $ operand = array_keys ($ conditions );
1069- $ operand = $ operand [0 ];
1070-
1071- if (!in_array ($ operand , array_keys ($ ops ))) {
1072- $ this ->log ("No operators defined in LDAP search conditions. " , 'ldap.error ' );
1073- return null ;
1074- }
1075-
1076- $ children = $ conditions [$ operand ];
1077-
1078- if (in_array ($ operand , array_keys ($ opsRec ))) {
1079- if (!is_array ($ children )) {
1080- return null ;
1081- }
1082- $ tmp = '( ' . $ opsRec [$ operand ]['prefix ' ];
1083- foreach ($ children as $ key => $ value ) {
1084- $ child = array ($ key => $ value );
1085- $ tmp .= $ this ->_conditionsArrayToString ($ child );
1086- }
1087- return $ tmp . ') ' ;
1088- }
1089-
1090- if (in_array ($ operand , array_keys ($ opsNeg ))) {
1091- if (!is_array ($ children )) {
1092- return null ;
1093- }
1094- $ nextOperand = trim (str_replace ('not ' , '' , $ operand ));
1095-
1096- return '(! ' . $ this ->_conditionsArrayToString (array ($ nextOperand => $ children )) . ') ' ;
1097- }
1098-
1099- if (in_array ($ operand , array_keys ($ opsTer ))) {
1100- $ tmp = '' ;
1101- foreach ($ children as $ key => $ value ) {
1102- if (!is_array ($ value )) {
1103- $ tmp .= '( ' . $ key . '= ' . ($ value === null ? $ opsTer ['equals ' ]['null ' ] : $ value ) . ') ' ;
1104- } else {
1105- foreach ($ value as $ subvalue ) {
1106- $ tmp .= $ this ->_conditionsArrayToString (array ('equals ' => array ($ key => $ subvalue )));
1074+ // Process array of conditions
1075+ $ ret_parts = array ();
1076+ foreach ($ conditions as $ k => $ v ) {
1077+ // Check key for each component part
1078+ switch (strtoupper ($ k )) {
1079+ case 'OR ' :
1080+ if (is_array ($ v )) {
1081+ // the children of this condition will be processed with an OR join
1082+ $ ret_parts [] = $ this ->_conditionsArrayToString ($ v , '| ' );
1083+ } else {
1084+ // Single OR condition? This is probably an error.
1085+ $ ret_parts [] = $ this ->_conditionsArrayToString ($ v );
1086+ }
1087+ break ;
1088+ case 'NOT ' :
1089+ // the childern of this condition will be processed with a NOT join
1090+ $ ret_parts [] = $ this ->_conditionsArrayToString ($ v , '! ' );
1091+ break ;
1092+ case 'AND ' :
1093+ // the children of this condition will be processed with an AND join (default)
1094+ $ ret_parts [] = $ this ->_conditionsArrayToString ($ v );
1095+ break ;
1096+ default :
1097+ // This is an array, but not an explicitly named boolean operation
1098+ if (is_numeric ($ k )) {
1099+ // numeric keys indicate a default AND
1100+ $ ret_parts [] = $ this ->_conditionsArrayToString ($ v );
1101+ } else if (strpos ($ k , '!= ' )) {
1102+ // A not equals must be converted to a NOT condition
1103+ $ ret_parts [] = '(!( ' .rtrim (substr ($ k , 0 , strpos ($ k , '!= ' ))).'= ' .$ v .')) ' ;
1104+ } else if (preg_match ('/([<>~]=)$/ ' , $ k , $ op )) {
1105+ // A lexical greater-than/less-than/approximately can be passed directly
1106+ $ ret_parts [] = '( ' .$ k .$ v .') ' ;
1107+ } else if (is_array ($ v )) {
1108+ // An array of values is an IN statement. This must be converted to an OR join
1109+ $ r = '(| ' ;
1110+ foreach ($ v as $ i ) {
1111+ $ r .= '( ' .$ k .'= ' .$ i .') ' ;
1112+ }
1113+ $ ret_parts [] = $ r .') ' ;
1114+ } else if ($ v === NULL ) {
1115+ // A check for NULL must be converted to a NOT any match
1116+ $ ret_parts [] = '(!( ' .$ k .'=*)) ' ;
1117+ } else {
1118+ // A single key-value pair is an equality check
1119+ $ ret_parts [] = '( ' .$ k .'= ' .$ v .') ' ;
11071120 }
1108- }
11091121 }
1110- return $ tmp ;
11111122 }
1123+ // If there is only one part to this condition, return immediately
1124+ if (count ($ conditions ) == 1 ) {
1125+ return $ ret_parts [0 ];
1126+ }
1127+ // Otherwise paste the parts together with the join
1128+ return '( ' .$ join .implode ('' , $ ret_parts ).') ' ;
1129+ } else {
1130+ // Ensure string condition has leading and trailing parenthesis
1131+ return strpos ($ conditions , '( ' ) === 0 ? (string ) $ conditions : '( ' .$ conditions .') ' ;
11121132 }
11131133 }
11141134
@@ -1164,7 +1184,8 @@ protected function _executeQuery($queryData = array(), $cache = true) {
11641184 if ($ queryData ['fields ' ] == 1 ) {
11651185 $ queryData ['fields ' ] = array ();
11661186 }
1167- $ res = @ldap_search ($ this ->database , $ queryData ['targetDn ' ], $ queryData ['conditions ' ], $ queryData ['fields ' ], 0 , $ queryData ['limit ' ]);
1187+ // if an offset was requested, grab $offset + $limit results. We'll select just the desired results later (in read())
1188+ $ res = @ldap_search ($ this ->database , $ queryData ['targetDn ' ], $ queryData ['conditions ' ], $ queryData ['fields ' ], 0 , (isset ($ queryData ['offset ' ]) ? $ queryData ['offset ' ] : 0 ) + $ queryData ['limit ' ]);
11681189 }
11691190
11701191 if (!$ res ) {
@@ -1387,19 +1408,10 @@ public function boolean() {
13871408 * @return integer Entry count
13881409 */
13891410 public function calculate (Model $ model , $ func , $ params = array ()) {
1390- $ params = (array )$ params ;
1391-
13921411 switch (strtolower ($ func )) {
13931412 case 'count ' :
1394- if (empty ($ params ) && $ model ->id ) {
1395- // quick search to make sure it exsits
1396- $ queryData ['targetDn ' ] = $ model ->id ;
1397- $ queryData ['conditions ' ] = 'objectClass=* ' ;
1398- $ queryData ['scope ' ] = 'base ' ;
1399- $ query = $ this ->read ($ model , $ queryData );
1400- }
1401- return $ this ->count ;
1402-
1413+ // read() expects this magic string as a count indicator
1414+ return 'COUNT(*) AS ' . $ this ->name ('count ' );
14031415 case 'max ' :
14041416 case 'min ' :
14051417 break ;
0 commit comments