Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch '1.3-misc' into mergers

Conflicts:
	cake/libs/model/datasources/dbo_source.php
	cake/libs/view/helpers/js.php
  • Loading branch information...
commit 7a620b62b00f88eae878a9f88428e1aea6eadce9 2 parents 6b2154d + 173b4e7
@markstory markstory authored
Showing with 1,228 additions and 297 deletions.
  1. +0 −1  app/config/core.php
  2. +14 −1 cake/basics.php
  3. +9 −5 cake/console/templates/default/classes/controller.ctp
  4. +6 −4 cake/libs/cache.php
  5. +2 −1  cake/libs/controller/components/auth.php
  6. +12 −2 cake/libs/controller/components/request_handler.php
  7. +14 −3 cake/libs/controller/components/security.php
  8. +75 −47 cake/libs/controller/controller.php
  9. +29 −2 cake/libs/error.php
  10. +52 −19 cake/libs/model/datasources/datasource.php
  11. +266 −111 cake/libs/model/datasources/dbo_source.php
  12. +61 −10 cake/libs/model/model.php
  13. +42 −7 cake/libs/validation.php
  14. +54 −0 cake/libs/view/elements/sql_dump.ctp
  15. +7 −8 cake/libs/view/{elements/dump.ctp → errors/error500.ctp}
  16. +11 −11 cake/libs/view/helpers/ajax.php
  17. +8 −3 cake/libs/view/helpers/js.php
  18. +19 −4 cake/libs/view/helpers/number.php
  19. +1 −1  cake/libs/view/layouts/default.ctp
  20. +0 −10 cake/libs/view/view.php
  21. +12 −11 cake/tests/cases/console/libs/api.test.php
  22. +5 −0 cake/tests/cases/console/libs/bake.test.php
  23. +7 −1 cake/tests/cases/console/libs/tasks/controller.test.php
  24. +3 −8 cake/tests/cases/console/libs/tasks/extract.test.php
  25. +55 −0 cake/tests/cases/libs/controller/components/auth.test.php
  26. +15 −0 cake/tests/cases/libs/controller/components/cookie.test.php
  27. +8 −2 cake/tests/cases/libs/controller/components/request_handler.test.php
  28. +27 −2 cake/tests/cases/libs/controller/components/security.test.php
  29. +65 −1 cake/tests/cases/libs/controller/controller.test.php
  30. +7 −7 cake/tests/cases/libs/controller/scaffold.test.php
  31. +26 −1 cake/tests/cases/libs/error.test.php
  32. +153 −8 cake/tests/cases/libs/model/datasources/dbo_source.test.php
  33. +82 −2 cake/tests/cases/libs/model/model_read.test.php
  34. +40 −0 cake/tests/cases/libs/validation.test.php
  35. +8 −0 cake/tests/cases/libs/view/helpers/ajax.test.php
  36. +7 −3 cake/tests/cases/libs/view/helpers/js.test.php
  37. +12 −0 cake/tests/cases/libs/view/helpers/number.test.php
  38. +8 −0 cake/tests/cases/libs/view/view.test.php
  39. +6 −0 cake/tests/groups/lib.group.php
  40. +0 −1  cake/tests/test_app/views/layouts/default.ctp
  41. 0  vendors/css/empty
  42. 0  vendors/js/empty
View
1  app/config/core.php
@@ -29,7 +29,6 @@
* Development Mode:
* 1: Errors and warnings shown, model caches refreshed, flash messages halted.
* 2: As in 1, but also with full debug messages and SQL output.
- * 3: As in 2, but also with full controller dump.
*
* In production mode, flash messages redirect after a time interval.
* In development mode, you need to click the flash message to continue.
View
15 cake/basics.php
@@ -81,7 +81,7 @@ function config() {
* `uses('flay', 'time');`
*
* @param string $name Filename without the .php part
- * @deprecated
+ * @deprecated Will be removed in 2.0
*/
function uses() {
$args = func_get_args();
@@ -169,6 +169,7 @@ function sortByKey(&$array, $sortby, $order = 'asc', $type = SORT_NUMERIC) {
* @param array $a1 Array to use for keys
* @param array $a2 Array to use for values
* @return mixed Outputs either combined array or false.
+ * @deprecated Will be removed in 2.0
*/
function array_combine($a1, $a2) {
$a1 = array_values($a1);
@@ -247,6 +248,7 @@ function pluginSplit($name, $dotAppend = false, $plugin = null) {
*
* @return array Array of given parameters
* @link http://book.cakephp.org/view/694/a
+ * @deprecated Will be removed in 2.0
*/
function a() {
$args = func_get_args();
@@ -266,6 +268,7 @@ function a() {
*
* @return array Associative array
* @link http://book.cakephp.org/view/695/aa
+ * @deprecated Will be removed in 2.0
*/
function aa() {
$args = func_get_args();
@@ -286,6 +289,7 @@ function aa() {
*
* @param string $text String to echo
* @link http://book.cakephp.org/view/700/e
+ * @deprecated Will be removed in 2.0
*/
function e($text) {
echo $text;
@@ -297,6 +301,7 @@ function e($text) {
* @param string $str String to lowercase
* @return string Lowercased string
* @link http://book.cakephp.org/view/705/low
+ * @deprecated Will be removed in 2.0
*/
function low($str) {
return strtolower($str);
@@ -308,6 +313,7 @@ function low($str) {
* @param string $str String to uppercase
* @return string Uppercased string
* @link http://book.cakephp.org/view/710/up
+ * @deprecated Will be removed in 2.0
*/
function up($str) {
return strtoupper($str);
@@ -321,6 +327,7 @@ function up($str) {
* @param string $subject String to search
* @return string Replaced string
* @link http://book.cakephp.org/view/708/r
+ * @deprecated Will be removed in 2.0
*/
function r($search, $replace, $subject) {
return str_replace($search, $replace, $subject);
@@ -348,6 +355,7 @@ function pr($var) {
*
* @param mixed $p Parameter as string or array
* @return string
+ * @deprecated Will be removed in 2.0
*/
function params($p) {
if (!is_array($p) || count($p) == 0) {
@@ -466,6 +474,7 @@ function env($key) {
* @param string $fileName File name.
* @param mixed $data String or array.
* @return boolean Success
+ * @deprecated Will be removed in 2.0
*/
function file_put_contents($fileName, $data) {
if (is_array($data)) {
@@ -836,6 +845,7 @@ function __c($msg, $category, $return = false) {
* @param array First array
* @param array Second array
* @return array Array with different keys
+ * @deprecated Will be removed in 2.0
*/
if (!function_exists('array_diff_key')) {
function array_diff_key() {
@@ -871,6 +881,7 @@ function array_diff_key() {
* @param array First array
* @param array Second array
* @return array Array with interesected keys
+ * @deprecated Will be removed in 2.0
*/
if (!function_exists('array_intersect_key')) {
function array_intersect_key($arr1, $arr2) {
@@ -942,6 +953,7 @@ function convertSlash($string) {
* @param string $baseKey Base key
* @return string URL encoded query string
* @see http://php.net/http_build_query
+ * @deprecated Will be removed in 2.0
*/
if (!function_exists('http_build_query')) {
function http_build_query($data, $prefix = null, $argSep = null, $baseKey = null) {
@@ -985,6 +997,7 @@ function http_build_query($data, $prefix = null, $argSep = null, $baseKey = null
* @param mixed $val2 Value to return if condition doesn't match
* @return mixed $val1 or $val2, depending on whether $condition evaluates to a non-empty expression.
* @link http://book.cakephp.org/view/704/ife
+ * @deprecated Will be removed in 2.0
*/
function ife($condition, $val1 = null, $val2 = null) {
if (!empty($condition)) {
View
14 cake/console/templates/default/classes/controller.ctp
@@ -29,13 +29,17 @@ class <?php echo $controllerName; ?>Controller extends <?php echo $plugin; ?>App
var $scaffold;
<?php else: ?>
<?php
-echo "\tvar \$helpers = array('Html', 'Form'";
if (count($helpers)):
- foreach ($helpers as $help):
- echo ", '" . Inflector::camelize($help) . "'";
- endforeach;
+ echo "\tvar \$helpers = array(";
+ for ($i = 0, $len = count($helpers); $i < $len; $i++):
+ if ($i != $len - 1):
+ echo "'" . Inflector::camelize($helpers[$i]) . "', ";
+ else:
+ echo "'" . Inflector::camelize($helpers[$i]) . "'";
+ endif;
+ endfor;
+ echo ");\n";
endif;
-echo ");\n";
if (count($components)):
echo "\tvar \$components = array(";
View
10 cake/libs/cache.php
@@ -196,11 +196,13 @@ function __loadEngine($name, $plugin = null) {
if ($plugin) {
return App::import('Lib', $plugin . '.cache' . DS . $name, false);
} else {
- $app = App::import('Lib', 'cache' . DS . $name, false);
- if (!$app) {
- require LIBS . 'cache' . DS . strtolower($name) . '.php';
+ $core = App::core();
+ $path = $core['libs'][0] . 'cache' . DS . strtolower($name) . '.php';
+ if (file_exists($path)) {
+ require $path;
+ return true;
}
- return true;
+ return App::import('Lib', 'cache' . DS . $name, false);
}
}
View
3  cake/libs/controller/components/auth.php
@@ -253,7 +253,7 @@ class AuthComponent extends Object {
* @return void
* @access public
*/
- function initialize(&$controller) {
+ function initialize(&$controller, $settings = array()) {
$this->params = $controller->params;
$crud = array('create', 'read', 'update', 'delete');
$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
@@ -275,6 +275,7 @@ function initialize(&$controller) {
));
}
}
+ $this->_set($settings);
if (Configure::read() > 0) {
App::import('Debugger');
Debugger::checkSessionKey();
View
14 cake/libs/controller/components/request_handler.php
@@ -165,14 +165,16 @@ function __construct() {
* as the first item.
*
* @param object $controller A reference to the controller
+ * @param array $settings Array of settings to _set().
* @return void
* @see Router::parseExtensions()
* @access public
*/
- function initialize(&$controller) {
+ function initialize(&$controller, $settings = array()) {
if (isset($controller->params['url']['ext'])) {
$this->ext = $controller->params['url']['ext'];
}
+ $this->_set($settings);
}
/**
@@ -408,7 +410,7 @@ function setContent($name, $type = null) {
* @return string Server address
* @access public
*/
- function getReferrer() {
+ function getReferer() {
if (env('HTTP_HOST') != null) {
$sessHost = env('HTTP_HOST');
}
@@ -420,6 +422,14 @@ function getReferrer() {
}
/**
+ * @deprecated use getReferer()
+ */
+ function getReferrer() {
+ trigger_error('Deprecated method, use RequestHandlerComponent::getReferer instead', E_USER_WARNING);
+ return $this->getReferer();
+ }
+
+/**
* Gets remote client IP
*
* @return string Client IP address
View
17 cake/libs/controller/components/security.php
@@ -169,9 +169,22 @@ class SecurityComponent extends Object {
var $_action = null;
/**
+ * Initialize the SecurityComponent
+ *
+ * @param object $controller Controller instance for the request
+ * @param array $settings Settings to set to the component
+ * @return void
+ * @access public
+ */
+ function initialize(&$controller, $settings = array()) {
+ $this->_set($settings);
+ }
+
+/**
* Component startup. All security checking happens here.
*
* @param object $controller Instantiating controller
+ * @return void
* @access public
*/
function startup(&$controller) {
@@ -342,7 +355,7 @@ function loginRequest($options = array()) {
if (strtolower($options['type']) == 'digest') {
$out[] = 'qop="auth"';
$out[] = 'nonce="' . uniqid("") . '"';
- $out[] = 'opaque="' . md5($options['realm']).'"';
+ $out[] = 'opaque="' . md5($options['realm']) . '"';
}
return $auth . ' ' . implode(',', $out);
@@ -719,10 +732,8 @@ function _callback(&$controller, $method, $params = array()) {
if (is_callable(array($controller, $method))) {
return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
} else {
- // Debug::warning('Callback method ' . $method . ' in controller ' . get_class($controller)
return null;
}
}
}
-
?>
View
122 cake/libs/controller/controller.php
@@ -74,7 +74,7 @@ class Controller extends Object {
*
* Example: var $uses = array('Product', 'Post', 'Comment');
*
- * Can be set to array() to use no models. Can be set to false to
+ * Can be set to array() to use no models. Can be set to false to
* use no models and prevent the merging of $uses with AppController
*
* @var mixed A single name as a string or a list of names as an array.
@@ -350,6 +350,16 @@ class Controller extends Object {
var $validationErrors = null;
/**
+ * Contains a list of the HTTP codes that CakePHP recognizes. These may be
+ * queried and/or modified through Controller::httpCodes(), which is also
+ * tasked with their lazy-loading.
+ *
+ * @var array Associative array of HTTP codes and their associated messages.
+ * @access private
+ */
+ var $__httpCodes = null;
+
+/**
* Constructor.
*
*/
@@ -505,6 +515,62 @@ function constructClasses() {
}
/**
+ * Queries & sets valid HTTP response codes & messages.
+ *
+ * @param mixed $code If $code is an integer, then the corresponding code/message is
+ * returned if it exists, null if it does not exist. If $code is an array,
+ * then the 'code' and 'message' keys of each nested array are added to the default
+ * HTTP codes. Example:
+ *
+ * httpCodes(404); // returns array(404 => 'Not Found')
+ *
+ * httpCodes(array(
+ * 701 => 'Unicorn Moved',
+ * 800 => 'Unexpected Minotaur'
+ * )); // sets these new values, and returns true
+ *
+ * @return mixed Associative array of the HTTP codes as keys, and the message
+ * strings as values, or null of the given $code does not exist.
+ */
+ function httpCodes($code = null) {
+ if (empty($this->__httpCodes)) {
+ $this->__httpCodes = array(
+ 100 => 'Continue', 101 => 'Switching Protocols',
+ 200 => 'OK', 201 => 'Created', 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information', 204 => 'No Content',
+ 205 => 'Reset Content', 206 => 'Partial Content',
+ 300 => 'Multiple Choices', 301 => 'Moved Permanently',
+ 302 => 'Found', 303 => 'See Other',
+ 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
+ 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required',
+ 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required',
+ 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone',
+ 411 => 'Length Required', 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large',
+ 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable',
+ 417 => 'Expectation Failed', 500 => 'Internal Server Error',
+ 501 => 'Not Implemented', 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable', 504 => 'Gateway Time-out'
+ );
+ }
+
+ if (empty($code)) {
+ return $this->__httpCodes;
+ }
+
+ if (is_array($code)) {
+ $this->__httpCodes = $code + $this->__httpCodes;
+ return true;
+ }
+
+ if (!isset($this->__httpCodes[$code])) {
+ return null;
+ }
+ return array($code => $this->__httpCodes[$code]);
+ }
+
+/**
* Loads and instantiates models required by this controller.
* If Controller::persistModel; is true, controller will cache model instances on first request,
* additional request will used cached models.
@@ -603,47 +669,8 @@ function redirect($url, $status = null, $exit = true) {
}
if (!empty($status)) {
- $codes = array(
- 100 => 'Continue',
- 101 => 'Switching Protocols',
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authoritative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 303 => 'See Other',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 307 => 'Temporary Redirect',
- 400 => 'Bad Request',
- 401 => 'Unauthorized',
- 402 => 'Payment Required',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Time-out',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition Failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Large',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested range not satisfiable',
- 417 => 'Expectation Failed',
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Time-out'
- );
+ $codes = $this->httpCodes();
+
if (is_string($status)) {
$codes = array_flip($codes);
}
@@ -657,14 +684,13 @@ function redirect($url, $status = null, $exit = true) {
$msg = $status;
}
$status = "HTTP/1.1 {$code} {$msg}";
+
} else {
$status = null;
}
- }
-
- if (!empty($status)) {
$this->header($status);
}
+
if ($url !== null) {
$this->header('Location: ' . Router::url($url, true));
}
@@ -1076,9 +1102,11 @@ function paginate($object = null, $scope = array(), $whitelist = array()) {
$value = $options['order'][$key];
unset($options['order'][$key]);
- if (isset($object->{$alias}) && $object->{$alias}->hasField($field)) {
+ if ($object->hasField($field)) {
$options['order'][$alias . '.' . $field] = $value;
- } elseif ($object->hasField($field)) {
+ } elseif ($object->hasField($field, true)) {
+ $options['order'][$field] = $value;
+ } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field)) {
$options['order'][$alias . '.' . $field] = $value;
}
}
View
31 cake/libs/error.php
@@ -92,7 +92,6 @@ function __construct($method, $messages) {
$this->controller =& new Controller();
$this->controller->viewPath = 'errors';
}
-
$options = array('escape' => false);
$messages = Sanitize::clean($messages, $options);
@@ -156,7 +155,7 @@ function error404($params) {
$url = $this->controller->here;
}
$url = Router::normalize($url);
- header("HTTP/1.0 404 Not Found");
+ $this->controller->header("HTTP/1.0 404 Not Found");
$this->controller->set(array(
'code' => '404',
'name' => __('Not Found', true),
@@ -167,6 +166,28 @@ function error404($params) {
}
/**
+ * Convenience method to display a 500 page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+ function error500($params) {
+ extract($params, EXTR_OVERWRITE);
+
+ if (!isset($url)) {
+ $url = $this->controller->here;
+ }
+ $url = Router::normalize($url);
+ $this->controller->header("HTTP/1.0 500 Internal Server Error");
+ $this->controller->set(array(
+ 'code' => '500',
+ 'name' => __('An Internal Error Has Occurred', true),
+ 'message' => h($url),
+ 'base' => $this->controller->base
+ ));
+ $this->_outputMessage('error500');
+ }
+/**
* Renders the Missing Controller web page.
*
* @param array $params Parameters for controller
@@ -229,7 +250,9 @@ function privateAction($params) {
function missingTable($params) {
extract($params, EXTR_OVERWRITE);
+ $this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
+ 'code' => '500',
'model' => $className,
'table' => $table,
'title' => __('Missing Database Table', true)
@@ -244,7 +267,9 @@ function missingTable($params) {
* @access public
*/
function missingDatabase($params = array()) {
+ $this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
+ 'code' => '500',
'title' => __('Scaffold Missing Database Connection', true)
));
$this->_outputMessage('missingScaffolddb');
@@ -294,7 +319,9 @@ function missingLayout($params) {
function missingConnection($params) {
extract($params, EXTR_OVERWRITE);
+ $this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
+ 'code' => '500',
'model' => $className,
'title' => __('Missing Database Connection', true)
));
View
71 cake/libs/model/datasources/datasource.php
@@ -78,6 +78,7 @@ class DataSource extends Object {
* The starting character that this DataSource uses for quoted identifiers.
*
* @var string
+ * @access public
*/
var $startQuote = null;
@@ -85,6 +86,7 @@ class DataSource extends Object {
* The ending character that this DataSource uses for quoted identifiers.
*
* @var string
+ * @access public
*/
var $endQuote = null;
@@ -199,11 +201,15 @@ class DataSource extends Object {
* should be cached
*
* @var boolean
+ * @access public
*/
var $cacheSources = true;
/**
* Constructor.
+ *
+ * @param array $config Array of configuration information for the datasource.
+ * @return void.
*/
function __construct($config = array()) {
parent::__construct();
@@ -213,7 +219,9 @@ function __construct($config = array()) {
/**
* Caches/returns cached results for child instances
*
- * @return array
+ * @param mixed $data
+ * @return array Array of sources available in this datasource.
+ * @access public
*/
function listSources($data = null) {
if ($this->cacheSources === false) {
@@ -240,7 +248,9 @@ function listSources($data = null) {
/**
* Convenience method for DboSource::listSources(). Returns source names in lowercase.
*
- * @return array
+ * @param boolean $reset Whether or not the source list should be reset.
+ * @return array Array of sources available in this datasource
+ * @access public
*/
function sources($reset = false) {
if ($reset === true) {
@@ -253,9 +263,10 @@ function sources($reset = false) {
* Returns a Model description (metadata) or null if none found.
*
* @param Model $model
- * @return mixed
+ * @return array Array of Metadata for the $model
+ * @access public
*/
- function describe($model) {
+ function describe(&$model) {
if ($this->cacheSources === false) {
return null;
}
@@ -276,6 +287,7 @@ function describe($model) {
* Begin a transaction
*
* @return boolean Returns true if a transaction is not in progress
+ * @access public
*/
function begin(&$model) {
return !$this->_transactionStarted;
@@ -285,6 +297,7 @@ function begin(&$model) {
* Commit a transaction
*
* @return boolean Returns true if a transaction is in progress
+ * @access public
*/
function commit(&$model) {
return $this->_transactionStarted;
@@ -294,6 +307,7 @@ function commit(&$model) {
* Rollback a transaction
*
* @return boolean Returns true if a transaction is in progress
+ * @access public
*/
function rollback(&$model) {
return $this->_transactionStarted;
@@ -304,6 +318,7 @@ function rollback(&$model) {
*
* @param string $real Real column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
+ * @access public
*/
function column($real) {
return false;
@@ -318,6 +333,7 @@ function column($real) {
* @param array $fields An Array of fields to be saved.
* @param array $values An Array of values to save.
* @return boolean success
+ * @access public
*/
function create(&$model, $fields = null, $values = null) {
return false;
@@ -331,6 +347,7 @@ function create(&$model, $fields = null, $values = null) {
* @param Model $model The model being read.
* @param array $queryData An array of query data used to find the data you want
* @return mixed
+ * @access public
*/
function read(&$model, $queryData = array()) {
return false;
@@ -345,6 +362,7 @@ function read(&$model, $queryData = array()) {
* @param array $fields Array of fields to be updated
* @param array $values Array of values to be update $fields to.
* @return boolean Success
+ * @access public
*/
function update(&$model, $fields = null, $values = null) {
return false;
@@ -357,6 +375,7 @@ function update(&$model, $fields = null, $values = null) {
*
* @param Model $model The model class having record(s) deleted
* @param mixed $id Primary key of the model
+ * @access public
*/
function delete(&$model, $id = null) {
if ($id == null) {
@@ -368,7 +387,8 @@ function delete(&$model, $id = null) {
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
- * @return in
+ * @return mixed Last ID key generated in previous INSERT
+ * @access public
*/
function lastInsertId($source = null) {
return false;
@@ -378,7 +398,8 @@ function lastInsertId($source = null) {
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
- * @return in
+ * @return integer Number of rows returned by last operation
+ * @access public
*/
function lastNumRows($source = null) {
return false;
@@ -388,7 +409,8 @@ function lastNumRows($source = null) {
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
- * @return in
+ * @return integer Number of rows affected by last query.
+ * @access public
*/
function lastAffected($source = null) {
return false;
@@ -400,6 +422,7 @@ function lastAffected($source = null) {
* before establishing a connection.
*
* @return boolean Whether or not the Datasources conditions for use are met.
+ * @access public
*/
function enabled() {
return true;
@@ -409,6 +432,7 @@ function enabled() {
*
* @param string $interface The name of the interface (method)
* @return boolean True on success
+ * @access public
*/
function isInterfaceSupported($interface) {
$methods = get_class_methods(get_class($this));
@@ -419,10 +443,12 @@ function isInterfaceSupported($interface) {
}
/**
- * Sets the configuration for the DataSource
+ * Sets the configuration for the DataSource.
+ * Merges the $config information with the _baseConfig and the existing $config property.
*
* @param array $config The configuration array
* @return void
+ * @access public
*/
function setConfig($config = array()) {
$this->config = array_merge($this->_baseConfig, $this->config, $config);
@@ -433,6 +459,8 @@ function setConfig($config = array()) {
*
* @param string $object The name of the object (model) to cache
* @param mixed $data The description of the model, usually a string or array
+ * @return mixed
+ * @access private
*/
function __cacheDescription($object, $data = null) {
if ($this->cacheSources === false) {
@@ -455,16 +483,18 @@ function __cacheDescription($object, $data = null) {
}
/**
- * Enter description here...
+ * Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data.
*
- * @param unknown_type $query
- * @param unknown_type $data
- * @param unknown_type $association
+ * @param string $query Query string needing replacements done.
+ * @param array $data Array of data with values that will be inserted in placeholders.
+ * @param string $association Name of association model being replaced
* @param unknown_type $assocData
- * @param Model $model
- * @param Model $linkModel
+ * @param Model $model Instance of the model to replace $__cakeID__$
+ * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
* @param array $stack
- * @return unknown
+ * @return string String of query data with placeholders replaced.
+ * @access public
+ * @todo Remove and refactor $assocData, ensure uses of the method have the param removed too.
*/
function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
$keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
@@ -538,17 +568,20 @@ function insertQueryData($query, $data, $association, $assocData, &$model, &$lin
/**
* To-be-overridden in subclasses.
*
- * @param unknown_type $model
- * @param unknown_type $key
- * @return unknown
+ * @param Model $model Model instance
+ * @param string $key Key name to make
+ * @return string Key name for model.
+ * @access public
*/
- function resolveKey($model, $key) {
+ function resolveKey(&$model, $key) {
return $model->alias . $key;
}
/**
* Closes the current datasource.
*
+ * @return void
+ * @access public
*/
function __destruct() {
if ($this->_transactionStarted) {
View
377 cake/libs/model/datasources/dbo_source.php
@@ -32,7 +32,8 @@ class DboSource extends DataSource {
/**
* Description string for this Database Data Source.
*
- * @var unknown_type
+ * @var string
+ * @access public
*/
var $description = "Database Data Source";
@@ -47,6 +48,7 @@ class DboSource extends DataSource {
* Database keyword used to assign aliases to identifiers.
*
* @var string
+ * @access public
*/
var $alias = 'AS ';
@@ -54,6 +56,7 @@ class DboSource extends DataSource {
* Caches fields quoted in DboSource::name()
*
* @var array
+ * @access public
*/
var $fieldCache = array();
@@ -61,6 +64,7 @@ class DboSource extends DataSource {
* Bypass automatic adding of joined fields/associations.
*
* @var boolean
+ * @access private
*/
var $__bypass = false;
@@ -68,6 +72,7 @@ class DboSource extends DataSource {
* The set of valid SQL operations usable in a WHERE statement
*
* @var array
+ * @access private
*/
var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
@@ -87,7 +92,7 @@ class DboSource extends DataSource {
* List of table engine specific parameters used on table creating
*
* @var array
- * @access protected
+ * @access public
*/
var $tableParameters = array();
@@ -95,12 +100,16 @@ class DboSource extends DataSource {
* List of engine specific additional field parameters used on table creating
*
* @var array
- * @access protected
+ * @access public
*/
var $fieldParameters = array();
/**
* Constructor
+ *
+ * @param array $config Array of configuration information for the Datasource.
+ * @param boolean $autoConnect Whether or not the datasource should automatically connect.
+ * @access public
*/
function __construct($config = null, $autoConnect = true) {
if (!isset($config['prefix'])) {
@@ -123,6 +132,7 @@ function __construct($config = null, $autoConnect = true) {
*
* @param array $config An array defining the new configuration settings
* @return boolean True on success, false on failure
+ * @access public
*/
function reconnect($config = null) {
$this->disconnect();
@@ -139,6 +149,7 @@ function reconnect($config = null) {
* @param string $column The column into which this data will be inserted
* @param boolean $read Value to be used in READ or WRITE context
* @return mixed Prepared value or array of values.
+ * @access public
*/
function value($data, $column = null, $read = true) {
if (is_array($data) && !empty($data)) {
@@ -164,6 +175,7 @@ function value($data, $column = null, $read = true) {
*
* @param string $identifier
* @return object An object representing a database identifier to be used in a query
+ * @access public
*/
function identifier($identifier) {
$obj = new stdClass();
@@ -177,6 +189,7 @@ function identifier($identifier) {
*
* @param string $expression
* @return object An object representing a database expression to be used in a query
+ * @access public
*/
function expression($expression) {
$obj = new stdClass();
@@ -189,7 +202,8 @@ function expression($expression) {
* Executes given SQL statement.
*
* @param string $sql SQL statement
- * @return unknown
+ * @return boolean
+ * @access public
*/
function rawQuery($sql) {
$this->took = $this->error = $this->numRows = false;
@@ -199,11 +213,18 @@ function rawQuery($sql) {
/**
* Queries the database with given SQL statement, and obtains some metadata about the result
* (rows affected, timing, any errors, number of rows in resultset). The query is also logged.
- * If DEBUG is set, the log is shown all the time, else it is only shown on errors.
+ * If Configure::read('debug') is set, the log is shown all the time, else it is only shown on errors.
+ *
+ * ### Options
+ *
+ * - stats - Collect meta data stats for this query. Stats include time take, rows affected,
+ * any errors, and number of rows returned. Defaults to `true`.
+ * - log - Whether or not the query should be logged to the memory log.
*
* @param string $sql
* @param array $options
* @return mixed Resource or object representing the result set, or false on failure
+ * @access public
*/
function execute($sql, $options = array()) {
$defaults = array('stats' => true, 'log' => $this->fullDebug);
@@ -232,7 +253,8 @@ function execute($sql, $options = array()) {
/**
* DataSource Query abstraction
*
- * @return resource Result resource identifier
+ * @return resource Result resource identifier.
+ * @access public
*/
function query() {
$args = func_get_args();
@@ -329,6 +351,7 @@ function query() {
* Returns a row from current resultset as an array
*
* @return array The fetched row as an array
+ * @access public
*/
function fetchRow($sql = null) {
if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
@@ -340,6 +363,9 @@ function fetchRow($sql = null) {
if ($this->hasResult()) {
$this->resultSet($this->_result);
$resultRow = $this->fetchResult();
+ if (!empty($resultRow)) {
+ $this->fetchVirtualField($resultRow);
+ }
return $resultRow;
} else {
return null;
@@ -353,6 +379,7 @@ function fetchRow($sql = null) {
* @param string $sql SQL statement
* @param boolean $cache Enables returning/storing cached query results
* @return array Array of resultset rows, or false if no rows matched
+ * @access public
*/
function fetchAll($sql, $cache = true, $modelName = null) {
if ($cache && isset($this->_queryCache[$sql])) {
@@ -369,6 +396,7 @@ function fetchAll($sql, $cache = true, $modelName = null) {
$out[] = $first;
}
while ($this->hasResult() && $item = $this->fetchResult()) {
+ $this->fetchVirtualField($item);
$out[] = $item;
}
@@ -387,15 +415,44 @@ function fetchAll($sql, $cache = true, $modelName = null) {
}
/**
+ * Modifies $result array to place virtual fields in model entry where they belongs to
+ *
+ * @param array $resut REference to the fetched row
+ * @return void
+ */
+ function fetchVirtualField(&$result) {
+ if (isset($result[0]) && is_array($result[0])) {
+ foreach ($result[0] as $field => $value) {
+ if (strpos($field, '__') === false) {
+ continue;
+ }
+ list($alias, $virtual) = explode('__', $field);
+
+ if (!ClassRegistry::isKeySet($alias)) {
+ return;
+ }
+ $model = ClassRegistry::getObject($alias);
+ if ($model->isVirtualField($virtual)) {
+ $result[$alias][$virtual] = $value;
+ unset($result[0][$field]);
+ }
+ }
+ if (empty($result[0])) {
+ unset($result[0]);
+ }
+ }
+ }
+
+/**
* Returns a single field of the first of query results for a given SQL query, or false if empty.
*
* @param string $name Name of the field
* @param string $sql SQL query
- * @return unknown
+ * @return mixed Value of field read.
+ * @access public
*/
function field($name, $sql) {
$data = $this->fetchRow($sql);
-
if (!isset($data[$name]) || empty($data[$name])) {
return false;
} else {
@@ -409,6 +466,7 @@ function field($name, $sql) {
*
* @param string $data
* @return string SQL field
+ * @access public
*/
function name($data) {
if ($data == '*') {
@@ -471,9 +529,10 @@ function name($data) {
}
/**
- * Checks if it's connected to the database
+ * Checks if the source is connected to the database.
*
* @return boolean True if the database is connected, else false
+ * @access public
*/
function isConnected() {
return $this->connected;
@@ -483,6 +542,7 @@ function isConnected() {
* Checks if the result is valid
*
* @return boolean True if the result is valid else false
+ * @access public
*/
function hasResult() {
return is_resource($this->_result);
@@ -493,40 +553,40 @@ function hasResult() {
*
* @param boolean $sorted Get the queries sorted by time taken, defaults to false.
* @return array Array of queries run as an array
+ * @access public
*/
- function getLog($sorted = false) {
+ function getLog($sorted = false, $clear = true) {
if ($sorted) {
$log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
} else {
$log = $this->_queriesLog;
}
- return $log;
+ if ($clear) {
+ $this->_queriesLog = array();
+ }
+ return array('log' => $log, 'count' => $this->_queriesCnt, 'time' => $this->_queriesTime);
}
/**
- * Outputs the contents of the queries log.
+ * Outputs the contents of the queries log. If in a non-CLI environment the sql_log element
+ * will be rendered and output. If in a CLI environment, a plain text log is generated.
*
- * @param boolean $sorted Get the queries sorted by time taken, defaults to false
+ * @param boolean $sorted Get the queries sorted by time taken, defaults to false.
+ * @return void
*/
function showLog($sorted = false) {
- $log = $this->getLog($sorted);
-
- if ($this->_queriesCnt > 1) {
- $text = 'queries';
- } else {
- $text = 'query';
+ $log = $this->getLog($sorted, false);
+ if (empty($log['log'])) {
+ return;
}
-
if (PHP_SAPI != 'cli') {
- print ("<table class=\"cake-sql-log\" id=\"cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true)) . "\" summary=\"Cake SQL Log\" cellspacing=\"0\" border = \"0\">\n<caption>({$this->configKeyName}) {$this->_queriesCnt} {$text} took {$this->_queriesTime} ms</caption>\n");
- print ("<thead>\n<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>\n</thead>\n<tbody>\n");
-
- foreach ($log as $k => $i) {
- print ("<tr><td>" . ($k + 1) . "</td><td>" . h($i['query']) . "</td><td>{$i['error']}</td><td style = \"text-align: right\">{$i['affected']}</td><td style = \"text-align: right\">{$i['numRows']}</td><td style = \"text-align: right\">{$i['took']}</td></tr>\n");
- }
- print ("</tbody></table>\n");
+ App::import('Core', 'View');
+ $controller = null;
+ $View =& new View($controller, false);
+ $View->set('logs', array($this->configKeyName => $log));
+ echo $View->element('sql_dump');
} else {
- foreach ($log as $k => $i) {
+ foreach ($log['log'] as $k => $i) {
print (($k + 1) . ". {$i['query']} {$i['error']}\n");
}
}
@@ -537,6 +597,7 @@ function showLog($sorted = false) {
*
* @param string $sql SQL statement
* @todo: Add hook to log errors instead of returning false
+ * @access public
*/
function logQuery($sql) {
$this->_queriesCnt++;
@@ -561,6 +622,7 @@ function logQuery($sql) {
* and execution time in microseconds. If the query fails, an error is output instead.
*
* @param string $sql Query to show information on.
+ * @access public
*/
function showQuery($sql) {
$error = $this->error;
@@ -581,9 +643,10 @@ function showQuery($sql) {
/**
* Gets full table name including prefix
*
- * @param mixed $model
- * @param boolean $quote
+ * @param mixed $model Either a Model object or a string table name.
+ * @param boolean $quote Whether you want the table name quoted.
* @return string Full quoted table name
+ * @access public
*/
function fullTableName($model, $quote = true) {
if (is_object($model)) {
@@ -602,10 +665,15 @@ function fullTableName($model, $quote = true) {
/**
* The "C" in CRUD
*
- * @param Model $model
- * @param array $fields
- * @param array $values
+ * Creates new records in the database.
+ *
+ * @param Model $model Model object that the record is for.
+ * @param array $fields An array of field names to insert. If null, $model->data will be
+ * used to generate field names.
+ * @param array $values An array of values with keys matching the fields. If null, $model->data will
+ * be used to generate values.
* @return boolean Success
+ * @access public
*/
function create(&$model, $fields = null, $values = null) {
$id = null;
@@ -648,10 +716,12 @@ function create(&$model, $fields = null, $values = null) {
/**
* The "R" in CRUD
*
- * @param Model $model
- * @param array $queryData
+ * Reads record(s) from the database.
+ *
+ * @param Model $model A Model object that the query is for.
+ * @param array $queryData An array of queryData information containing keys similar to Model::find()
* @param integer $recursive Number of levels of association
- * @return unknown
+ * @return mixed boolean false on error/failure. An array of results on success.
*/
function read(&$model, $queryData = array(), $recursive = null) {
$queryData = $this->__scrubQueryData($queryData);
@@ -742,12 +812,13 @@ function read(&$model, $queryData = array(), $recursive = null) {
}
/**
- * Private method. Passes association results thru afterFind filters of corresponding model
+ * Passes association results thru afterFind filters of corresponding model
*
* @param array $results Reference of resultset to be filtered
* @param object $model Instance of model to operate against
* @param array $filtered List of classes already filtered, to be skipped
- * @return return
+ * @return array Array of results that have been filtered through $model->afterFind
+ * @access private
*/
function __filterResults(&$results, &$model, $filtered = array()) {
$filtering = array();
@@ -779,16 +850,16 @@ function __filterResults(&$results, &$model, $filtered = array()) {
}
/**
- * Enter description here...
+ * Queries associations. Used to fetch results on recursive models.
*
- * @param Model $model
- * @param unknown_type $linkModel
- * @param string $type Association type
+ * @param Model $model Primary Model object
+ * @param Model $linkModel Linked model that
+ * @param string $type Association type, one of the model association types ie. hasMany
* @param unknown_type $association
* @param unknown_type $assocData
- * @param unknown_type $queryData
- * @param unknown_type $external
- * @param unknown_type $resultSet
+ * @param array $queryData
+ * @param boolean $external Whether or not the association query is on an external datasource.
+ * @param array $resultSet Existing results
* @param integer $recursive Number of levels of association
* @param array $stack
*/
@@ -941,10 +1012,11 @@ function queryAssociation(&$model, &$linkModel, $type, $association, $assocData,
/**
* A more efficient way to fetch associations. Woohoo!
*
- * @param model $model Primary model object
- * @param string $query Association query
- * @param array $ids Array of IDs of associated records
+ * @param model $model Primary model object
+ * @param string $query Association query
+ * @param array $ids Array of IDs of associated records
* @return array Association results
+ * @access public
*/
function fetchAssociated($model, $query, $ids) {
$query = str_replace('{$__cakeID__$}', implode(', ', $ids), $query);
@@ -1003,6 +1075,7 @@ function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel)
* @param unknown_type $association
* @param unknown_type $type
* @param boolean $selfJoin
+ * @access private
*/
function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = false) {
if (isset($merge[0]) && !isset($merge[0][$association])) {
@@ -1086,6 +1159,7 @@ function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = fal
* @param boolean $external
* @param array $resultSet
* @return mixed
+ * @access public
*/
function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) {
$queryData = $this->__scrubQueryData($queryData);
@@ -1251,6 +1325,7 @@ function generateAssociationQuery(&$model, &$linkModel, $type, $association = nu
* @param object $model Model object
* @param array $association Association array
* @return array Conditions array defining the constraint between $model and $association
+ * @access public
*/
function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) {
$assoc = array_merge(array('external' => false, 'self' => false), $assoc);
@@ -1290,6 +1365,7 @@ function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null
*
* @param array $join An array defining a JOIN statement in a query
* @return string An SQL JOIN statement to be used in a query
+ * @access public
* @see DboSource::renderJoinStatement()
* @see DboSource::buildStatement()
*/
@@ -1316,9 +1392,10 @@ function buildJoinStatement($join) {
* @param array $query An array defining an SQL query
* @param object $model The model object which initiated the query
* @return string An executable SQL statement
+ * @access public
* @see DboSource::renderStatement()
*/
- function buildStatement($query, $model) {
+ function buildStatement($query, &$model) {
$query = array_merge(array('offset' => null, 'joins' => array()), $query);
if (!empty($query['joins'])) {
$count = count($query['joins']);
@@ -1333,7 +1410,7 @@ function buildStatement($query, $model) {
'fields' => implode(', ', $query['fields']),
'table' => $query['table'],
'alias' => $this->alias . $this->name($query['alias']),
- 'order' => $this->order($query['order']),
+ 'order' => $this->order($query['order'], 'ASC', $model),
'limit' => $this->limit($query['limit'], $query['offset']),
'joins' => implode(' ', $query['joins']),
'group' => $this->group($query['group'])
@@ -1345,6 +1422,7 @@ function buildStatement($query, $model) {
*
* @param array $data
* @return string
+ * @access public
*/
function renderJoinStatement($data) {
extract($data);
@@ -1354,9 +1432,10 @@ function renderJoinStatement($data) {
/**
* Renders a final SQL statement by putting together the component parts in the correct order
*
- * @param string $type
- * @param array $data
- * @return string
+ * @param string $type type of query being run. e.g select, create, update, delete, schema, alter.
+ * @param array $data Array of data to insert into the query.
+ * @return string Rendered SQL expression to be run.
+ * @access public
*/
function renderStatement($type, $data) {
extract($data);
@@ -1403,6 +1482,7 @@ function renderStatement($type, $data) {
* Merges a mixed set of string/array conditions
*
* @return array
+ * @access private
*/
function __mergeConditions($query, $assoc) {
if (empty($assoc)) {
@@ -1435,6 +1515,7 @@ function __mergeConditions($query, $assoc) {
* @param array $values
* @param mixed $conditions
* @return boolean Success
+ * @access public
*/
function update(&$model, $fields = array(), $values = null, $conditions = null) {
if ($values == null) {
@@ -1512,6 +1593,7 @@ function _prepareUpdateFields(&$model, $fields, $quoteValues = true, $alias = fa
* @param Model $model
* @param mixed $conditions
* @return boolean Success
+ * @access public
*/
function delete(&$model, $conditions = null) {
$alias = $joins = null;
@@ -1564,6 +1646,7 @@ function _matchRecords(&$model, $conditions = null) {
*
* @param object $model
* @return array
+ * @access protected
*/
function _getJoins($model) {
$join = array();
@@ -1606,13 +1689,23 @@ function calculate(&$model, $func, $params = array()) {
if (!isset($params[1])) {
$params[1] = 'count';
}
- return 'COUNT(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
+ if (is_object($model) && $model->isVirtualField($params[0])){
+ $arg = $this->__quoteFields($model->getVirtualField($params[0]));
+ } else {
+ $arg = $this->name($params[0]);
+ }
+ return 'COUNT(' . $arg . ') AS ' . $this->name($params[1]);
case 'max':
case 'min':
if (!isset($params[1])) {
$params[1] = $params[0];
}
- return strtoupper($func) . '(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]);
+ if (is_object($model) && $model->isVirtualField($params[0])) {
+ $arg = $this->__quoteFields($model->getVirtualField($params[0]));
+ } else {
+ $arg = $this->name($params[0]);
+ }
+ return strtoupper($func) . '(' . $arg . ') AS ' . $this->name($params[1]);
break;
}
}
@@ -1636,6 +1729,7 @@ function truncate($table) {
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
+ * @access public
*/
function begin(&$model) {
if (parent::begin($model) && $this->execute($this->_commands['begin'])) {
@@ -1652,6 +1746,7 @@ function begin(&$model) {
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
+ * @access public
*/
function commit(&$model) {
if (parent::commit($model) && $this->execute($this->_commands['commit'])) {
@@ -1668,6 +1763,7 @@ function commit(&$model) {
* @return boolean True on success, false on fail
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
+ * @access public
*/
function rollback(&$model) {
if (parent::rollback($model) && $this->execute($this->_commands['rollback'])) {
@@ -1684,6 +1780,7 @@ function rollback(&$model) {
* @param mixed $conditions
* @param boolean $useAlias Use model aliases rather than table names when generating conditions
* @return mixed
+ * @access public
*/
function defaultConditions(&$model, $conditions, $useAlias = true) {
if (!empty($conditions)) {
@@ -1707,6 +1804,7 @@ function defaultConditions(&$model, $conditions, $useAlias = true) {
* @param unknown_type $key
* @param unknown_type $assoc
* @return string
+ * @access public
*/
function resolveKey($model, $key, $assoc = null) {
if (empty($assoc)) {
@@ -1723,6 +1821,7 @@ function resolveKey($model, $key, $assoc = null) {
*
* @param array $data
* @return array
+ * @access public
*/
function __scrubQueryData($data) {
foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) {
@@ -1734,6 +1833,24 @@ function __scrubQueryData($data) {
}
/**
+ * Converts model virtual fields into sql expressions to be fetched later
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields virtual fields to be used on query
+ * @return array
+ */
+ function _constructVirtualFields(&$model,$alias,$fields) {
+ $virtual = array();
+ foreach ($fields as $field) {
+ $virtualField = $this->name("{$alias}__{$field}");
+ $expression = $this->__quoteFields($model->getVirtualField($field));
+ $virtual[] = '(' .$expression . ") {$this->alias} {$virtualField}";
+ }
+ return $virtual;
+ }
+
+/**
* Generates the fields list of an SQL query.
*
* @param Model $model
@@ -1741,12 +1858,14 @@ function __scrubQueryData($data) {
* @param mixed $fields
* @param boolean $quote If false, returns fields array unquoted
* @return array
+ * @access public
*/
function fields(&$model, $alias = null, $fields = array(), $quote = true) {
if (empty($alias)) {
$alias = $model->alias;
}
- if (empty($fields)) {
+ $allFields = empty($fields);
+ if ($allFields) {
$fields = array_keys($model->schema());
} elseif (!is_array($fields)) {
$fields = String::tokenize($fields);
@@ -1756,10 +1875,20 @@ function fields(&$model, $alias = null, $fields = array(), $quote = true) {
if (!$quote) {
return $fields;
}
+ $virtual = array();
+ if ($model->getVirtualField()) {
+ $keys = array_keys($model->getVirtualField());
+ $virtual = ($allFields) ? $keys : array_intersect($keys, $fields);
+ }
$count = count($fields);
if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) {
for ($i = 0; $i < $count; $i++) {
+ if (in_array($fields[$i], $virtual)) {
+ unset($fields[$i]);
+ continue;
+ }
+ if (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){
if (is_object($fields[$i]) && isset($fields[$i]->type) && $fields[$i]->type === 'expression') {
$fields[$i] = $fields[$i]->value;
} elseif (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){
@@ -1815,6 +1944,9 @@ function fields(&$model, $alias = null, $fields = array(), $quote = true) {
}
}
}
+ if (!empty($virtual)) {
+ $fields = array_merge($fields,$this->_constructVirtualFields($model, $alias, $virtual));
+ }
return array_unique($fields);
}
@@ -1826,6 +1958,7 @@ function fields(&$model, $alias = null, $fields = array(), $quote = true) {
* @param boolean $where If true, "WHERE " will be prepended to the return value
* @param Model $model A reference to the Model instance making the query
* @return string SQL fragment
+ * @access public
*/
function conditions($conditions, $quoteValues = true, $where = true, $model = null) {
$clause = $out = '';
@@ -1866,6 +1999,7 @@ function conditions($conditions, $quoteValues = true, $where = true, $model = nu
* @param boolean $quoteValues If true, values should be quoted
* @param Model $model A reference to the Model instance making the query
* @return string SQL fragment
+ * @access public
*/
function conditionKeysToString($conditions, $quoteValues = true, $model = null) {
$c = 0;
@@ -1995,6 +2129,11 @@ function __parseKey($model, $key, $value) {
}
}
+ $virtual = false;
+ if (is_object($model) && $model->isVirtualField($key)) {
+ $key = $this->__quoteFields($model->getVirtualField($key));
+ $virtual = true;
+ }
$type = (is_object($model) ? $model->getColumnType($key) : null);
@@ -2009,7 +2148,7 @@ function __parseKey($model, $key, $value) {
$value = $this->value($value, $type);
- if ($key !== '?') {
+ if (!$virtual && $key !== '?') {
$isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false);
$key = $isKey ? $this->__quoteFields($key) : $this->name($key);
}
@@ -2047,7 +2186,9 @@ function __parseKey($model, $key, $value) {
break;
}
}
-
+ if ($virtual) {
+ return "({$key}) {$operator} {$value}";
+ }
return "{$key} {$operator} {$value}";
}
@@ -2090,6 +2231,7 @@ function __quoteFields($conditions) {
* @param integer $limit Limit of results returned
* @param integer $offset Offset from which to start results
* @return string SQL limit/offset statement
+ * @access public
*/
function limit($limit, $offset = null) {
if ($limit) {
@@ -2113,68 +2255,71 @@ function limit($limit, $offset = null) {
*
* @param string $key Field reference, as a key (i.e. Post.title)
* @param string $direction Direction (ASC or DESC)
+ * @param object $model model reference (used to look for virtual field)
* @return string ORDER BY clause
+ * @access public
*/
- function order($keys, $direction = 'ASC') {
- if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) {
- $keys = array_map('trim', explode(',', $keys));
- }
-
- if (is_array($keys)) {
- $keys = array_filter($keys);
- }
-
- if (empty($keys) || (is_array($keys) && isset($keys[0]) && empty($keys[0]))) {
- return '';
+ function order($keys, $direction = 'ASC', &$model = null) {
+ if (!is_array($keys)) {
+ $keys = array($keys);
}
+ $keys = array_filter($keys);
+ $result = array();
+ while (!empty($keys)) {
+ list($key, $dir) = each($keys);
+ array_shift($keys);
- if (is_array($keys)) {
- $keys = (Set::countDim($keys) > 1) ? array_map(array(&$this, 'order'), $keys) : $keys;
-
- foreach ($keys as $key => $value) {
- if (is_numeric($key)) {
- $key = $value = ltrim(str_replace('ORDER BY ', '', $this->order($value)));
- $value = (!preg_match('/\\x20ASC|\\x20DESC/i', $key) ? ' ' . $direction : '');
- } else {
- $value = ' ' . $value;
- }
+ if (is_numeric($key)) {
+ $key = $dir;
+ $dir = $direction;
+ }
- if (!preg_match('/^.+\\(.*\\)/', $key) && !strpos($key, ',')) {
- if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $dir)) {
- $dir = $dir[0];
- $key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
+ if (is_string($key) && strpos($key, ',') && !preg_match('/\(.+\,.+\)/', $key)) {
+ $key = array_map('trim', explode(',', $key));
+ }
+ if (is_array($key)) {
+ //Flatten the array
+ $key = array_reverse($key, true);
+ foreach ($key as $k => $v) {
+ if (is_numeric($k)) {
+ array_unshift($keys, $v);
} else {
- $dir = '';
- }
- $key = trim($key);
- if (!preg_match('/\s/', $key)) {
- $key = $this->name($key);
+ $keys = array($k => $v) + $keys;
}
- $key .= ' ' . trim($dir);
}
- $order[] = $this->order($key . $value);
+ continue;
}
- return ' ORDER BY ' . trim(str_replace('ORDER BY', '', implode(',', $order)));
- }
- $keys = preg_replace('/ORDER\\x20BY/i', '', $keys);
- if (strpos($keys, '.')) {
- preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $keys, $result, PREG_PATTERN_ORDER);
- $pregCount = count($result[0]);
+ if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $_dir)) {
+ $dir = $_dir[0];
+ $key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
+ }
- for ($i = 0; $i < $pregCount; $i++) {
- if (!is_numeric($result[0][$i])) {
- $keys = preg_replace('/' . $result[0][$i] . '/', $this->name($result[0][$i]), $keys);
+ if (strpos($key, '.')) {
+ preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $key, $matches, PREG_PATTERN_ORDER);
+ $pregCount = count($matches[0]);
+ for ($i = 0; $i < $pregCount; $i++) {
+ if (!is_numeric($matches[0][$i])) {
+ $key = preg_replace('/' . $matches[0][$i] . '/', $this->name($matches[0][$i]), $key);
+ }
}
}
- $result = ' ORDER BY ' . $keys;
- return $result . (!preg_match('/\\x20ASC|\\x20DESC/i', $keys) ? ' ' . $direction : '');
- } elseif (preg_match('/(\\x20ASC|\\x20DESC)/i', $keys, $match)) {
- $direction = $match[1];
- return ' ORDER BY ' . preg_replace('/' . $match[1] . '/', '', $keys) . $direction;
+ $key = trim($key);
+ if (!preg_match('/\s/', $key) && !strpos($key,'.')) {
+ if (is_object($model) && $model->isVirtualField($key)) {
+ $key = '('.$this->__quoteFields($model->getVirtualField($key)).')';
+ } else {
+ $key = $this->name($key);
+ }
+ }
+ $key .= ' ' . trim($dir);
+ $result[] = $key;
+ }
+ if (!empty($result)) {
+ return ' ORDER BY ' . implode(', ', $result);
}
- return ' ORDER BY ' . $keys . ' ' . $direction;
+ return '';
}
/**
@@ -2182,6 +2327,7 @@ function order($keys, $direction = 'ASC') {
*
* @param string $group Group By Condition
* @return mixed string condition or null
+ * @access public
*/
function group($group) {
if ($group) {
@@ -2194,14 +2340,12 @@ function group($group) {
}
/**
- * Disconnects database, kills the connection and says the connection is closed,
- * and if DEBUG is turned on, the log for this object is shown.
+ * Disconnects database, kills the connection and says the connection is closed.
*
+ * @return void
+ * @access public
*/
function close() {
- if (Configure::read() > 1) {
- $this->showLog();
- }
$this->disconnect();
}
@@ -2211,6 +2355,7 @@ function close() {
* @param Model $model Model to search
* @param string $sql SQL WHERE clause (condition only, not the "WHERE" part)
* @return boolean True if the table has a matching record, else false
+ * @access public
*/
function hasAny(&$Model, $sql) {
$sql = $this->conditions($sql);
@@ -2232,6 +2377,7 @@ function hasAny(&$Model, $sql) {
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return mixed An integer or string representing the length of the column
+ * @access public
*/
function length($real) {
if (!preg_match_all('/([\w\s]+)(?:\((\d+)(?:,(\d+))?\))?(\sunsigned)?(\szerofill)?/', $real, $result)) {
@@ -2292,6 +2438,7 @@ function length($real) {
*
* @param mixed $data Value to be translated
* @return mixed Converted boolean value
+ * @access public
*/
function boolean($data) {
if ($data === true || $data === false) {
@@ -2328,6 +2475,7 @@ function insertMulti($table, $fields, $values) {
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
+ * @access public
*/
function index($model) {
return false;
@@ -2340,6 +2488,7 @@ function index($model) {
* @param string $tableName Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
+ * @access public
*/
function createSchema($schema, $tableName = null) {
if (!is_a($schema, 'CakeSchema')) {
@@ -2388,7 +2537,7 @@ function createSchema($schema, $tableName = null) {
* Generate a alter syntax from CakeSchema::compare()
*
* @param unknown_type $schema
- * @return unknown
+ * @return boolean
*/
function alterSchema($compare, $table = null) {
return false;
@@ -2401,6 +2550,7 @@ function alterSchema($compare, $table = null) {
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
+ * @access public
*/
function dropSchema($schema, $table = null) {
if (!is_a($schema, 'CakeSchema')) {
@@ -2423,6 +2573,7 @@ function dropSchema($schema, $table = null) {
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
+ * @access public
*/
function buildColumn($column) {
$name = $type = null;
@@ -2483,6 +2634,7 @@ function buildColumn($column) {
* @param array $columnData The array of column data.
* @param string $position The position type to use. 'beforeDefault' or 'afterDefault' are common
* @return string a built column with the field parameters added.
+ * @access public
*/
function _buildFieldParameters($columnString, $columnData, $position) {
foreach ($this->fieldParameters as $paramName => $value) {
@@ -2506,6 +2658,7 @@ function _buildFieldParameters($columnString, $columnData, $position) {
* @param array $indexes
* @param string $table
* @return array
+ * @access public
*/
function buildIndex($indexes, $table = null) {
$join = array();
@@ -2536,6 +2689,7 @@ function buildIndex($indexes, $table = null) {
* @param array $parameters
* @param string $table
* @return array
+ * @access public
*/
function readTableParameters($name) {
$parameters = array();
@@ -2556,6 +2710,7 @@ function readTableParameters($name) {
* @param array $parameters
* @param string $table
* @return array
+ * @access public
*/
function buildTableParameters($parameters, $table = null) {
$result = array();
View
71 cake/libs/model/model.php
@@ -299,6 +299,20 @@ class Model extends Overloadable {