- Timestamp:
- 09/22/09 00:56:11 (10 months ago)
- Files:
-
- fActiveRecord.php (modified) (4 diffs)
- fORMDatabase.php (modified) (2 diffs)
- fRecordSet.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
fActiveRecord.php
r701 r707 Hide Line Numbers 16 16 * @link http://flourishlib.com/fActiveRecord 17 17 * 18 * @version 1.0.0b44 18 * @version 1.0.0b45 19 * @changes 1.0.0b45 Added support for `!~`, `&~`, `><` and OR comparisons to ::checkConditions(), made object handling in ::checkConditions() more robust [wb, 2009-09-21] 19 20 * @changes 1.0.0b44 Updated code for new fValidationException API [wb, 2009-09-18] 20 21 * @changes 1.0.0b43 Updated code for new fRecordSet API [wb, 2009-09-16] … … 177 178 178 179 /** 180 * Checks to see if a record matches a condition 181 * 182 * @internal 183 * 184 * @param string $operator The record to check 185 * @param mixed $value The value to compare to 186 * @param mixed $result The result of the method call(s) 187 * @return boolean If the comparison was successful 188 */ 189 static private function checkCondition($operator, $value, $result) 190 { 191 $was_array = is_array($value); 192 if (!$was_array) { $value = array($value); } 193 foreach ($value as $i => $_value) { 194 if (is_object($_value)) { 195 if ($_value instanceof fActiveRecord) { 196 continue; 197 } 198 if (method_exists($_value, '__toString')) { 199 $value[$i] = $_value->__toString(); 200 } 201 } 202 } 203 if (!$was_array) { $value = $value[0]; } 204 205 $was_array = is_array($result); 206 if (!$was_array) { $result = array($result); } 207 foreach ($result as $i => $_result) { 208 if (is_object($_result)) { 209 if ($_result instanceof fActiveRecord) { 210 continue; 211 } 212 if (method_exists($_result, '__toString')) { 213 $result[$i] = $_result->__toString(); 214 } 215 } 216 } 217 if (!$was_array) { $result = $result[0]; } 218 219 $match_all = $operator == '&~'; 220 $negate_like = $operator == '!~'; 221 222 switch ($operator) { 223 case '&~': 224 case '!~': 225 case '~': 226 if (!$match_all && !$negate_like && !is_array($value) && is_array($result)) { 227 $value = fORMDatabase::parseSearchTerms($value, TRUE); 228 } 229 230 settype($value, 'array'); 231 settype($result, 'array'); 232 233 if (count($result) > 1) { 234 foreach ($value as $_value) { 235 $found = FALSE; 236 foreach ($result as $_result) { 237 if (fUTF8::ipos($_result, $_value) !== FALSE) { 238 $found = TRUE; 239 } 240 } 241 if (!$found) { 242 return FALSE; 243 } 244 } 245 } else { 246 $found = FALSE; 247 foreach ($value as $_value) { 248 if (fUTF8::ipos($result[0], $_value) !== FALSE) { 249 $found = TRUE; 250 } elseif ($match_all) { 251 return FALSE; 252 } 253 } 254 if ((!$negate_like && !$found) || ($negate_like && $found)) { 255 return FALSE; 256 } 257 } 258 break; 259 260 case '=': 261 if ($value instanceof fActiveRecord && $result instanceof fActiveRecord) { 262 if (get_class($value) != get_class($result) || !$value->exists() || !$result->exists() || self::hash($value) != self::hash($result)) { 263 return FALSE; 264 } 265 266 } elseif (is_array($value) && !in_array($result, $value)) { 267 return FALSE; 268 269 } elseif (!is_array($value) && $result != $value) { 270 return FALSE; 271 } 272 break; 273 274 case '!': 275 if ($value instanceof fActiveRecord && $result instanceof fActiveRecord) { 276 if (get_class($value) == get_class($result) && $value->exists() && $result->exists() && self::hash($value) == self::hash($result)) { 277 return FALSE; 278 } 279 280 } elseif (is_array($value) && in_array($result, $value)) { 281 return FALSE; 282 283 } elseif (!is_array($value) && $result == $value) { 284 return FALSE; 285 } 286 break; 287 288 case '<': 289 if ($result >= $value) { 290 return FALSE; 291 } 292 break; 293 294 case '<=': 295 if ($result > $value) { 296 return FALSE; 297 } 298 break; 299 300 case '>': 301 if ($result <= $value) { 302 return FALSE; 303 } 304 break; 305 306 case '>=': 307 if ($result < $value) { 308 return FALSE; 309 } 310 break; 311 } 312 313 return TRUE; 314 } 315 316 317 /** 179 318 * Checks to see if a record matches all of the conditions 180 319 * … … 190 329 191 330 // Split the operator off of the end of the method name 192 if (in_array(substr($method, -2), array('<=', '>=', '!=', '<>' ))) {331 if (in_array(substr($method, -2), array('<=', '>=', '!=', '<>', '!~', '&~', '><'))) { 193 332 $operator = strtr( 194 333 substr($method, -2), … … 204 343 } 205 344 206 $multi_method = FALSE; 207 208 if ($operator == '~' && strpos($method, '|')) { 209 $multi_method = TRUE; 210 $result = array(); 211 foreach(explode('|', $method) as $_method) { 212 $result[] = $record->$_method(); 213 } 345 if (preg_match('#(?<!\|)\|(?!\|)#', $method)) { 346 347 $methods = explode('|', $method); 348 $values = $value; 349 $operators = array(); 350 351 foreach ($methods as &$_method) { 352 if (in_array(substr($_method, -2), array('<=', '>=', '!=', '<>', '!~', '&~', '><'))) { 353 $operators[] = strtr( 354 substr($_method, -2), 355 array( 356 '<>' => '!', 357 '!=' => '!' 358 ) 359 ); 360 $_method = substr($_method, 0, -2); 361 } elseif (!ctype_alnum(substr($_method, -1))) { 362 $operators[] = substr($_method, -1); 363 $_method = substr($_method, 0, -1); 364 } 365 } 366 $operators[] = $operator; 367 368 369 if (sizeof($operators) == 1) { 370 371 // Handle fuzzy searches 372 if ($operator == '~') { 214 373 374 $results = array(); 375 foreach ($methods as $method) { 376 $results[] = $record->$method(); 377 } 378 if (!self::checkCondition($operator, $value, $results)) { 379 return FALSE; 380 } 381 382 // Handle intersection 383 } elseif ($operator == '><') { 384 385 if (sizeof($methods) != 2 || sizeof($values) != 2) { 386 throw new fProgrammerException( 387 'The intersection operator, %s, requires exactly two methods and two values', 388 $operator 389 ); 390 } 391 392 $results = array(); 393 $results[0] = $record->{$methods[0]}(); 394 $results[1] = $record->{$methods[1]}(); 395 396 if ($results[1] === NULL && $values[1] === NULL) { 397 if (!self::checkCondition('=', $values[0], $results[0])) { 398 return FALSE; 399 } 400 401 402 } else { 403 404 $starts_between_values = FALSE; 405 $overlaps_value_1 = FALSE; 406 407 if ($values[1] !== NULL) { 408 $start_lt_value_1 = self::checkCondition('<', $values[0], $results[0]); 409 $start_gt_value_2 = self::checkCondition('>', $values[1], $results[0]); 410 $starts_between_values = !$start_lt_value_1 && !$start_gt_value_2; 411 } 412 if ($results[1] !== NULL) { 413 $start_gt_value_1 = self::checkCondition('>', $values[0], $results[0]); 414 $end_lt_value_1 = self::checkCondition('<', $values[0], $results[1]); 415 $overlaps_value_1 = !$start_gt_value_1 && !$end_lt_value_1; 416 } 417 418 if (!$starts_between_values && !$overlaps_value_1) { 419 return FALSE; 420 } 421 } 422 423 } else { 424 throw new fProgrammerException( 425 'An invalid comparison operator, %s, was specified for multiple columns', 426 $operator 427 ); 428 } 429 430 // Handle OR conditions 431 } else { 432 433 if (sizeof($methods) != sizeof($values)) { 434 throw new fProgrammerException( 435 'When performing an %1$s comparison there must be an equal number of methods and values, however there are not', 436 'OR', 437 sizeof($methods), 438 sizeof($values) 439 ); 440 } 441 442 if (sizeof($methods) != sizeof($operators)) { 443 throw new fProgrammerException( 444 'When performing an %s comparison there must be a comparison operator for each column, however one or more is missing', 445 'OR' 446 ); 447 } 448 449 $results = array(); 450 $iterations = sizeof($methods); 451 for ($i=0; $i<$iterations; $i++) { 452 $results[] = self::checkCondition($operators[$i], $values[$i], $record->{$methods[$i]}()); 453 } 454 455 if (!array_filter($results)) { 456 return FALSE; 457 } 458 459 } 460 461 // Single method comparisons 215 462 } else { 216 463 $result = $record->$method(); 217 } 218 219 switch ($operator) { 220 case '~': 221 // This is a fuzzy search type operation since it is using the output of multiple methods 222 if ($multi_method) { 223 if (!is_array($value)) { 224 $value = fORMDatabase::parseSearchTerms($value, TRUE); 225 } 226 227 foreach ($value as $_value) { 228 $found = FALSE; 229 foreach ($result as $_result) { 230 if (fUTF8::ipos($_result, $_value) !== FALSE) { 231 $found = TRUE; 232 } 233 } 234 if (!$found) { 235 return FALSE; 236 } 237 } 238 239 // This is a simple LIKE match against one or more values 240 } else { 241 // Ensure the method output is present in at least one value of the array 242 if (is_array($value)) { 243 244 $found = FALSE; 245 foreach ($value as $_value) { 246 if (fUTF8::ipos($result, $_value) !== FALSE) { 247 $found = TRUE; 248 } 249 } 250 if (!$found) { 251 return FALSE; 252 } 253 254 // Ensure the method is present in the value 255 } elseif (!is_array($value) && fUTF8::ipos($result, $value) === FALSE) { 256 return FALSE; 257 } 258 } 259 break; 260 261 case '=': 262 if ($value instanceof fActiveRecord && $result instanceof fActiveRecord) { 263 if (get_class($value) != get_class($result) || !$value->exists() || !$result->exists() || self::hash($value) != self::hash($result)) { 264 return FALSE; 265 } 266 267 } elseif (is_array($value) && !in_array($result, $value)) { 268 return FALSE; 269 270 } elseif (!is_array($value) && $result != $value) { 271 return FALSE; 272 } 273 break; 274 275 case '!': 276 if ($value instanceof fActiveRecord && $result instanceof fActiveRecord) { 277 if (get_class($value) == get_class($result) && $value->exists() && $result->exists() && self::hash($value) == self::hash($result)) { 278 return FALSE; 279 } 280 281 } elseif (is_array($value) && in_array($result, $value)) { 282 return FALSE; 283 284 } elseif (!is_array($value) && $result == $value) { 285 return FALSE; 286 } 287 break; 288 289 case '<': 290 if ($result >= $value) { 291 return FALSE; 292 } 293 break; 294 295 case '<=': 296 if ($result > $value) { 297 return FALSE; 298 } 299 break; 300 301 case '>': 302 if ($result <= $value) { 303 return FALSE; 304 } 305 break; 306 307 case '>=': 308 if ($result < $value) { 309 return FALSE; 310 } 311 break; 464 if (!self::checkCondition($operator, $value, $result)) { 465 return FALSE; 466 } 312 467 } 313 468 } fORMDatabase.php
r646 r707 Hide Line Numbers 11 11 * @link http://flourishlib.com/fORMDatabase 12 12 * 13 * @version 1.0.0b14 13 * @version 1.0.0b15 14 * @changes 1.0.0b15 Streamlined intersection operator SQL and added support for the second value being NULL [wb, 2009-09-21] 14 15 * @changes 1.0.0b14 Added support for the intersection operator `><` to ::createWhereClause() [wb, 2009-07-13] 15 16 * @changes 1.0.0b13 Added support for the `AND LIKE` operator `&~` to ::createWhereClause() [wb, 2009-07-09] … … 651 652 } 652 653 653 $part_1 = '(' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[1] . ' >= ' . self::escapeBySchema($table, $columns[1], $values[0]) . ')'; 654 $part_2 = '(' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[1]) . ' AND ' . $columns[1] . ' >= ' . self::escapeBySchema($table, $columns[1], $values[1]) . ')'; 655 $part_3 = '(' . $columns[0] . ' >= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[1] . ' <= ' . self::escapeBySchema($table, $columns[1], $values[1]) . ')'; 656 $part_4 = '(' . $columns[1] . ' IS NULL AND ' . $columns[0] . ' >= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[1]) . ')'; 657 658 $sql[] = ' (' . $part_1 . ' OR ' . $part_2 . ' OR ' . $part_3 . ' OR ' . $part_4 . ') '; 654 if ($values[1] === NULL) { 655 $part_1 = '(' . $columns[1] . ' IS NULL AND ' . $columns[0] . ' = ' . self::escapeBySchema($table, $columns[0], $values[0]) . ')'; 656 $part_2 = '(' . $columns[1] . ' IS NOT NULL AND ' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[1] . ' >= ' . self::escapeBySchema($table, $columns[1], $values[0]) . ')'; 657 } else { 658 $part_1 = '(' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[1] . ' >= ' . self::escapeBySchema($table, $columns[1], $values[0]) . ')'; 659 $part_2 = '(' . $columns[0] . ' >= ' . self::escapeBySchema($table, $columns[0], $values[0]) . ' AND ' . $columns[0] . ' <= ' . self::escapeBySchema($table, $columns[0], $values[1]) . ')'; 660 } 661 662 $sql[] = ' (' . $part_1 . ' OR ' . $part_2 . ') '; 659 663 660 664 } else { fRecordSet.php
r705 r707 Hide Line Numbers 10 10 * @link http://flourishlib.com/fRecordSet 11 11 * 12 * @version 1.0.0b25 12 * @version 1.0.0b26 13 * @changes 1.0.0b26 Updated the documentation for ::build() and ::filter() to reflect new functionality [wb, 2009-09-21] 13 14 * @changes 1.0.0b25 Fixed ::map() to work with string-style static method callbacks in PHP 5.1 [wb, 2009-09-18] 14 15 * @changes 1.0.0b24 Backwards Compatibility Break - renamed ::buildFromRecords() to ::buildFromArray(). Added ::buildFromCall(), ::buildFromMap() and `::build{RelatedRecords}()` [wb, 2009-09-16] … … 70 71 * 'column!~' => array(VALUE, VALUE2, ... ) // (column NOT LIKE '%VALUE%' AND column NOT LIKE '%VALUE2%' AND column ... ) 71 72 * 'column!|column2<|column3=' => array(VALUE, VALUE2, VALUE3) // (column <> '%VALUE%' OR column2 < '%VALUE2%' OR column3 = '%VALUE3%') 72 * 'column|column2><' => array(VALUE, VALUE2) // ((column <= VALUE AND column2 >= VALUE) OR (column <= VALUE2 AND column2 >= VALUE2) OR (column >= VALUE AND column2 <= VALUE2) OR (column2 IS NULL AND column >= VALUE AND column <= VALUE2)) 73 * 'column|column2><' => array(VALUE, VALUE2) // WHEN VALUE === NULL: ((column2 IS NULL AND column = VALUE) OR (column2 IS NOT NULL AND column <= VALUE AND column2 >= VALUE)) 74 * // WHEN VALUE !== NULL: ((column <= VALUE AND column2 >= VALUE) OR (column >= VALUE AND column <= VALUE2)) 73 75 * 'column|column2|column3~' => VALUE // (column LIKE '%VALUE%' OR column2 LIKE '%VALUE%' OR column3 LIKE '%VALUE%') 74 76 * 'column|column2|column3~' => array(VALUE, VALUE2, ... ) // ((column LIKE '%VALUE%' OR column2 LIKE '%VALUE%' OR column3 LIKE '%VALUE%') AND (column LIKE '%VALUE2%' OR column2 LIKE '%VALUE2%' OR column3 LIKE '%VALUE2%') AND ... ) … … 818 820 * {{{ 819 821 * // The following forms work for any $value that is not an array 820 * 'methodName=' => $value // If the output is equal to $value 821 * 'methodName!' => $value // If the output is not equal to $value 822 * 'methodName!=' => $value // If the output is not equal to $value 823 * 'methodName<>' => $value // If the output is not equal to $value 824 * 'methodName<' => $value // If the output is less than $value 825 * 'methodName<=' => $value // If the output is less than or equal to $value 826 * 'methodName>' => $value // If the output is greater than $value 827 * 'methodName>=' => $value // If the output is greater than or equal to $value 828 * 'methodName~' => $value // If the output contains the $value (case insensitive) 829 * 'methodName|methodName2|methodName3~' => $value // Parses $value as a search string and make sure each term is present in at least one output (case insensitive) 822 * 'methodName=' => $value // If the output is equal to $value 823 * 'methodName!' => $value // If the output is not equal to $value 824 * 'methodName!=' => $value // If the output is not equal to $value 825 * 'methodName<>' => $value // If the output is not equal to $value 826 * 'methodName<' => $value // If the output is less than $value 827 * 'methodName<=' => $value // If the output is less than or equal to $value 828 * 'methodName>' => $value // If the output is greater than $value 829 * 'methodName>=' => $value // If the output is greater than or equal to $value 830 * 'methodName~' => $value // If the output contains the $value (case insensitive) 831 * 'methodName!~' => $value // If the output does not contain the $value (case insensitive) 832 * 'methodName|methodName2|methodName3~' => $value // Parses $value as a search string and make sure each term is present in at least one output (case insensitive) 830 833 * 831 834 * // The following forms work for any $array that is an array 832 * 'methodName=' => $array // If the output is equal to at least one value in $array 833 * 'methodName!' => $array // If the output is not equal to any value in $array 834 * 'methodName!=' => $array // If the output is not equal to any value in $array 835 * 'methodName<>' => $array // If the output is not equal to any value in $array 836 * 'methodName~' => $array // If the output contains one of the strings in $array (case insensitive) 837 * 'methodName|methodName2|methodName3~' => $array // If each value in the array is present in the output of at least one method (case insensitive) 835 * 'methodName=' => $array // If the output is equal to at least one value in $array 836 * 'methodName!' => $array // If the output is not equal to any value in $array 837 * 'methodName!=' => $array // If the output is not equal to any value in $array 838 * 'methodName<>' => $array // If the output is not equal to any value in $array 839 * 'methodName~' => $array // If the output contains one of the strings in $array (case insensitive) 840 * 'methodName!~' => $array // If the output contains none of the strings in $array (case insensitive) 841 * 'methodName&~' => $array // If the output contains all of the strings in $array (case insensitive) 842 * 'methodName|methodName2|methodName3~' => $array // If each value in the array is present in the output of at least one method (case insensitive) 843 * 844 * // The following works for an equal number of methods and values in the array 845 * 'methodName!|methodName2<|methodName3=' => array($value, $value2, $value3) // An OR statement - one of the method to value comparisons must be TRUE 846 * 847 * // The following accepts exactly two methods and two values, although the second value may be NULL 848 * 'methodName|methodName2><' => array($value, $value2) // If the range of values from the methods intersects the range of $value and $value2 - should be dates, times, timestamps or numbers 838 849 * }}} 839 850 *
