From 2f24de81cb0259023a5bcfbb03b0f2597fcd9275 Mon Sep 17 00:00:00 2001 From: Ber Clausen Date: Wed, 27 Nov 2013 20:36:19 -0300 Subject: [PATCH 1/4] Model objects should be in CamelCase. Add a Model type hint. --- lib/Cake/Model/Datasource/DboSource.php | 130 ++++++++++++------------ 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index bd6a1054f..688979913 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -1794,11 +1794,11 @@ class DboSource extends DataSource { * Builds and generates an SQL statement from an array. Handles final clean-up before conversion. * * @param array $query An array defining an SQL query - * @param Model $model The model object which initiated the query + * @param Model $Model The model object which initiated the query * @return string An executable SQL statement * @see DboSource::renderStatement() */ - public function buildStatement($query, $model) { + public function buildStatement($query, Model $Model) { $query = array_merge($this->_queryDefaults, $query); if (!empty($query['joins'])) { @@ -1811,14 +1811,14 @@ class DboSource extends DataSource { } return $this->renderStatement('select', array( - 'conditions' => $this->conditions($query['conditions'], true, true, $model), + 'conditions' => $this->conditions($query['conditions'], true, true, $Model), 'fields' => implode(', ', $query['fields']), 'table' => $query['table'], 'alias' => $this->alias . $this->name($query['alias']), - 'order' => $this->order($query['order'], 'ASC', $model), + 'order' => $this->order($query['order'], 'ASC', $Model), 'limit' => $this->limit($query['limit'], $query['offset']), 'joins' => implode(' ', $query['joins']), - 'group' => $this->group($query['group'], $model) + 'group' => $this->group($query['group'], $Model) )); } @@ -1911,24 +1911,24 @@ class DboSource extends DataSource { * Generates and executes an SQL UPDATE statement for given model, fields, and values. * For databases that do not support aliases in UPDATE queries. * - * @param Model $model + * @param Model $Model * @param array $fields * @param array $values * @param mixed $conditions * @return boolean Success */ - public function update(Model $model, $fields = array(), $values = null, $conditions = null) { + public function update(Model $Model, $fields = array(), $values = null, $conditions = null) { if (!$values) { $combined = $fields; } else { $combined = array_combine($fields, $values); } - $fields = implode(', ', $this->_prepareUpdateFields($model, $combined, empty($conditions))); + $fields = implode(', ', $this->_prepareUpdateFields($Model, $combined, empty($conditions))); $alias = $joins = null; - $table = $this->fullTableName($model); - $conditions = $this->_matchRecords($model, $conditions); + $table = $this->fullTableName($Model); + $conditions = $this->_matchRecords($Model, $conditions); if ($conditions === false) { return false; @@ -1936,7 +1936,7 @@ class DboSource extends DataSource { $query = compact('table', 'alias', 'joins', 'fields', 'conditions'); if (!$this->execute($this->renderStatement('update', $query))) { - $model->onError(); + $Model->onError(); return false; } return true; @@ -1945,22 +1945,22 @@ class DboSource extends DataSource { /** * Quotes and prepares fields and values for an SQL UPDATE statement * - * @param Model $model + * @param Model $Model * @param array $fields * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets * @param boolean $alias Include the model alias in the field name * @return array Fields and values, quoted and prepared */ - protected function _prepareUpdateFields(Model $model, $fields, $quoteValues = true, $alias = false) { - $quotedAlias = $this->startQuote . $model->alias . $this->endQuote; + protected function _prepareUpdateFields(Model $Model, $fields, $quoteValues = true, $alias = false) { + $quotedAlias = $this->startQuote . $Model->alias . $this->endQuote; $updates = array(); foreach ($fields as $field => $value) { if ($alias && strpos($field, '.') === false) { - $quoted = $model->escapeField($field); + $quoted = $Model->escapeField($field); } elseif (!$alias && strpos($field, '.') !== false) { $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace( - $model->alias . '.', '', $field + $Model->alias . '.', '', $field ))); } else { $quoted = $this->name($field); @@ -1973,12 +1973,12 @@ class DboSource extends DataSource { $update = $quoted . ' = '; if ($quoteValues) { - $update .= $this->value($value, $model->getColumnType($field)); - } elseif ($model->getColumnType($field) === 'boolean' && (is_int($value) || is_bool($value))) { + $update .= $this->value($value, $Model->getColumnType($field)); + } elseif ($Model->getColumnType($field) === 'boolean' && (is_int($value) || is_bool($value))) { $update .= $this->boolean($value, true); } elseif (!$alias) { $update .= str_replace($quotedAlias . '.', '', str_replace( - $model->alias . '.', '', $value + $Model->alias . '.', '', $value )); } else { $update .= $value; @@ -1992,21 +1992,21 @@ class DboSource extends DataSource { * Generates and executes an SQL DELETE statement. * For databases that do not support aliases in UPDATE queries. * - * @param Model $model + * @param Model $Model * @param mixed $conditions * @return boolean Success */ - public function delete(Model $model, $conditions = null) { + public function delete(Model $Model, $conditions = null) { $alias = $joins = null; - $table = $this->fullTableName($model); - $conditions = $this->_matchRecords($model, $conditions); + $table = $this->fullTableName($Model); + $conditions = $this->_matchRecords($Model, $conditions); if ($conditions === false) { return false; } if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) { - $model->onError(); + $Model->onError(); return false; } return true; @@ -2016,15 +2016,15 @@ class DboSource extends DataSource { * Gets a list of record IDs for the given conditions. Used for multi-record updates and deletes * in databases that do not support aliases in UPDATE/DELETE queries. * - * @param Model $model + * @param Model $Model * @param mixed $conditions * @return array List of record IDs */ - protected function _matchRecords(Model $model, $conditions = null) { + protected function _matchRecords(Model $Model, $conditions = null) { if ($conditions === true) { $conditions = $this->conditions(true); } elseif ($conditions === null) { - $conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model); + $conditions = $this->conditions($this->defaultConditions($Model, $conditions, false), true, true, $Model); } else { $noJoin = true; foreach ($conditions as $field => $value) { @@ -2034,7 +2034,7 @@ class DboSource extends DataSource { $field = ltrim($field, $this->startQuote); $field = rtrim($field, $this->endQuote); } - if (!$model->hasField($field)) { + if (!$Model->hasField($field)) { $noJoin = false; break; } @@ -2046,8 +2046,8 @@ class DboSource extends DataSource { if ($noJoin === true) { return $this->conditions($conditions); } - $idList = $model->find('all', array( - 'fields' => "{$model->alias}.{$model->primaryKey}", + $idList = $Model->find('all', array( + 'fields' => "{$Model->alias}.{$Model->primaryKey}", 'conditions' => $conditions )); @@ -2055,7 +2055,7 @@ class DboSource extends DataSource { return false; } $conditions = $this->conditions(array( - $model->primaryKey => Hash::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}") + $Model->primaryKey => Hash::extract($idList, "{n}.{$Model->alias}.{$Model->primaryKey}") )); } return $conditions; @@ -2103,12 +2103,12 @@ class DboSource extends DataSource { /** * Returns an SQL calculation, i.e. COUNT() or MAX() * - * @param Model $model + * @param Model $Model * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' * @param array $params Function parameters (any values must be quoted manually) * @return string An SQL calculation function */ - public function calculate(Model $model, $func, $params = array()) { + public function calculate(Model $Model, $func, $params = array()) { $params = (array)$params; switch (strtolower($func)) { @@ -2119,8 +2119,8 @@ class DboSource extends DataSource { if (!isset($params[1])) { $params[1] = 'count'; } - if (is_object($model) && $model->isVirtualField($params[0])) { - $arg = $this->_quoteFields($model->getVirtualField($params[0])); + if (is_object($Model) && $Model->isVirtualField($params[0])) { + $arg = $this->_quoteFields($Model->getVirtualField($params[0])); } else { $arg = $this->name($params[0]); } @@ -2130,8 +2130,8 @@ class DboSource extends DataSource { if (!isset($params[1])) { $params[1] = $params[0]; } - if (is_object($model) && $model->isVirtualField($params[0])) { - $arg = $this->_quoteFields($model->getVirtualField($params[0])); + if (is_object($Model) && $Model->isVirtualField($params[0])) { + $arg = $this->_quoteFields($Model->getVirtualField($params[0])); } else { $arg = $this->name($params[0]); } @@ -2295,7 +2295,7 @@ class DboSource extends DataSource { * If conditions are supplied then they will be returned. If a model doesn't exist and no conditions * were provided either null or false will be returned based on what was input. * - * @param Model $model + * @param Model $Model * @param string|array|boolean $conditions Array of conditions, conditions string, null or false. If an array of conditions, * or string conditions those conditions will be returned. With other values the model's existence will be checked. * If the model doesn't exist a null or false will be returned depending on the input value. @@ -2304,35 +2304,35 @@ class DboSource extends DataSource { * @see DboSource::update() * @see DboSource::conditions() */ - public function defaultConditions(Model $model, $conditions, $useAlias = true) { + public function defaultConditions(Model $Model, $conditions, $useAlias = true) { if (!empty($conditions)) { return $conditions; } - $exists = $model->exists(); + $exists = $Model->exists(); if (!$exists && $conditions !== null) { return false; } elseif (!$exists) { return null; } - $alias = $model->alias; + $alias = $Model->alias; if (!$useAlias) { - $alias = $this->fullTableName($model, false); + $alias = $this->fullTableName($Model, false); } - return array("{$alias}.{$model->primaryKey}" => $model->getID()); + return array("{$alias}.{$Model->primaryKey}" => $Model->getID()); } /** * Returns a key formatted like a string Model.fieldname(i.e. Post.title, or Country.name) * - * @param Model $model + * @param Model $Model * @param string $key * @param string $assoc * @return string */ - public function resolveKey(Model $model, $key, $assoc = null) { + public function resolveKey(Model $Model, $key, $assoc = null) { if (strpos('.', $key) !== false) { - return $this->name($model->alias) . '.' . $this->name($key); + return $this->name($Model->alias) . '.' . $this->name($key); } return $key; } @@ -2355,16 +2355,16 @@ class DboSource extends DataSource { /** * Converts model virtual fields into sql expressions to be fetched later * - * @param Model $model + * @param Model $Model * @param string $alias Alias table name * @param array $fields virtual fields to be used on query * @return array */ - protected function _constructVirtualFields(Model $model, $alias, $fields) { + protected function _constructVirtualFields(Model $Model, $alias, $fields) { $virtual = array(); foreach ($fields as $field) { $virtualField = $this->name($alias . $this->virtualFieldSeparator . $field); - $expression = $this->_quoteFields($model->getVirtualField($field)); + $expression = $this->_quoteFields($Model->getVirtualField($field)); $virtual[] = '(' . $expression . ") {$this->alias} {$virtualField}"; } return $virtual; @@ -2373,27 +2373,27 @@ class DboSource extends DataSource { /** * Generates the fields list of an SQL query. * - * @param Model $model + * @param Model $Model * @param string $alias Alias table name * @param mixed $fields * @param boolean $quote If false, returns fields array unquoted * @return array */ - public function fields(Model $model, $alias = null, $fields = array(), $quote = true) { + public function fields(Model $Model, $alias = null, $fields = array(), $quote = true) { if (empty($alias)) { - $alias = $model->alias; + $alias = $Model->alias; } - $virtualFields = $model->getVirtualField(); + $virtualFields = $Model->getVirtualField(); $cacheKey = array( $alias, - get_class($model), - $model->alias, + get_class($Model), + $Model->alias, $virtualFields, $fields, $quote, ConnectionManager::getSourceName($this), - $model->schemaName, - $model->table + $Model->schemaName, + $Model->table ); $cacheKey = md5(serialize($cacheKey)); if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) { @@ -2401,23 +2401,23 @@ class DboSource extends DataSource { } $allFields = empty($fields); if ($allFields) { - $fields = array_keys($model->schema()); + $fields = array_keys($Model->schema()); } elseif (!is_array($fields)) { $fields = String::tokenize($fields); } $fields = array_values(array_filter($fields)); - $allFields = $allFields || in_array('*', $fields) || in_array($model->alias . '.*', $fields); + $allFields = $allFields || in_array('*', $fields) || in_array($Model->alias . '.*', $fields); $virtual = array(); if (!empty($virtualFields)) { $virtualKeys = array_keys($virtualFields); foreach ($virtualKeys as $field) { - $virtualKeys[] = $model->alias . '.' . $field; + $virtualKeys[] = $Model->alias . '.' . $field; } $virtual = ($allFields) ? $virtualKeys : array_intersect($virtualKeys, $fields); foreach ($virtual as $i => $field) { if (strpos($field, '.') !== false) { - $virtual[$i] = str_replace($model->alias . '.', '', $field); + $virtual[$i] = str_replace($Model->alias . '.', '', $field); } $fields = array_diff($fields, array($field)); } @@ -2425,7 +2425,7 @@ class DboSource extends DataSource { } if (!$quote) { if (!empty($virtual)) { - $fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual)); + $fields = array_merge($fields, $this->_constructVirtualFields($Model, $alias, $virtual)); } return $fields; } @@ -2481,7 +2481,7 @@ class DboSource extends DataSource { } } if (!empty($virtual)) { - $fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual)); + $fields = array_merge($fields, $this->_constructVirtualFields($Model, $alias, $virtual)); } return $this->cacheMethod(__FUNCTION__, $cacheKey, array_unique($fields)); } @@ -2516,6 +2516,7 @@ class DboSource extends DataSource { } return $clause . implode(' AND ', $out); } + if (is_bool($conditions)) { return $clause . (int)$conditions . ' = 1'; } @@ -2523,12 +2524,15 @@ class DboSource extends DataSource { if (empty($conditions) || trim($conditions) === '') { return $clause . '1 = 1'; } + $clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i'; if (preg_match($clauses, $conditions)) { $clause = ''; } + $conditions = $this->_quoteFields($conditions); + return $clause . $conditions; } From 5c595b90d299973099b050ac3a8e29ac8a3d530a Mon Sep 17 00:00:00 2001 From: Ber Clausen Date: Wed, 27 Nov 2013 20:38:50 -0300 Subject: [PATCH 2/4] Remove is_object() calls when the test subject is already type hinted as a Model. --- lib/Cake/Model/Datasource/DboSource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 688979913..0826518f9 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2119,7 +2119,7 @@ class DboSource extends DataSource { if (!isset($params[1])) { $params[1] = 'count'; } - if (is_object($Model) && $Model->isVirtualField($params[0])) { + if ($Model->isVirtualField($params[0])) { $arg = $this->_quoteFields($Model->getVirtualField($params[0])); } else { $arg = $this->name($params[0]); @@ -2130,7 +2130,7 @@ class DboSource extends DataSource { if (!isset($params[1])) { $params[1] = $params[0]; } - if (is_object($Model) && $Model->isVirtualField($params[0])) { + if ($Model->isVirtualField($params[0])) { $arg = $this->_quoteFields($Model->getVirtualField($params[0])); } else { $arg = $this->name($params[0]); From 63a192ea39416b7b86c78401cf1c5d5c27c4a24b Mon Sep 17 00:00:00 2001 From: Ber Clausen Date: Wed, 27 Nov 2013 21:00:52 -0300 Subject: [PATCH 3/4] Update DboSource::group(): * Update documentation. * Add Model type hint. * Bail early when fields are empty. --- lib/Cake/Model/Datasource/DboSource.php | 36 ++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 0826518f9..21521f250 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2870,26 +2870,32 @@ class DboSource extends DataSource { } /** - * Create a GROUP BY SQL clause + * Create a GROUP BY SQL clause. * - * @param string $group Group By Condition - * @param Model $model - * @return string string condition or null + * @param string|array $fields Group By fields + * @param Model $Model + * @return string Group By clause or null. */ - public function group($group, $model = null) { - if ($group) { - if (!is_array($group)) { - $group = array($group); - } - foreach ($group as $index => $key) { - if (is_object($model) && $model->isVirtualField($key)) { - $group[$index] = '(' . $model->getVirtualField($key) . ')'; + public function group($fields, Model $Model = null) { + if (empty($fields)) { + return null; + } + + if (!is_array($fields)) { + $fields = array($fields); + } + + if (!empty($Model)) { + foreach ($fields as $index => $key) { + if ($Model->isVirtualField($key)) { + $fields[$index] = '(' . $Model->getVirtualField($key) . ')'; } } - $group = implode(', ', $group); - return ' GROUP BY ' . $this->_quoteFields($group); } - return null; + + $fields = implode(', ', $fields); + + return ' GROUP BY ' . $this->_quoteFields($fields); } /** From 2d6760418276bbe492b34f9957b203a5f544ca7e Mon Sep 17 00:00:00 2001 From: Ber Clausen Date: Wed, 27 Nov 2013 21:55:31 -0300 Subject: [PATCH 4/4] Add more Model type hints, and ease the model testing replacing is_object() for '!== null'. --- lib/Cake/Model/Datasource/DboSource.php | 81 +++++++++++++++---------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 21521f250..f785b91ac 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -1713,14 +1713,14 @@ class DboSource extends DataSource { * Returns a conditions array for the constraint between two models * * @param string $type Association type - * @param Model $model Model object + * @param Model $Model Model object * @param string $linkModel * @param string $alias * @param array $assoc * @param string $alias2 - * @return array Conditions array defining the constraint between $model and $association + * @return array Conditions array defining the constraint between $Model and $association */ - public function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) { + public function getConstraint($type, Model $Model, $linkModel, $alias, $assoc, $alias2 = null) { $assoc += array('external' => false); if (empty($assoc['foreignKey'])) { @@ -1734,7 +1734,7 @@ class DboSource extends DataSource { ); case ($type === 'hasOne' && !$assoc['external']): return array( - "{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}") + "{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$Model->alias}.{$Model->primaryKey}") ); case ($type === 'belongsTo' && $assoc['external']): return array( @@ -1742,7 +1742,7 @@ class DboSource extends DataSource { ); case ($type === 'belongsTo' && !$assoc['external']): return array( - "{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}") + "{$Model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}") ); case ($type === 'hasMany'): return array( @@ -2498,10 +2498,10 @@ class DboSource extends DataSource { * @param mixed $conditions Array or string of conditions, or any value. * @param boolean $quoteValues If true, values should be quoted * @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 + * @param Model $Model A reference to the Model instance making the query * @return string SQL fragment */ - public function conditions($conditions, $quoteValues = true, $where = true, $model = null) { + public function conditions($conditions, $quoteValues = true, $where = true, Model $Model = null) { $clause = $out = ''; if ($where) { @@ -2509,7 +2509,7 @@ class DboSource extends DataSource { } if (is_array($conditions) && !empty($conditions)) { - $out = $this->conditionKeysToString($conditions, $quoteValues, $model); + $out = $this->conditionKeysToString($conditions, $quoteValues, $Model); if (empty($out)) { return $clause . ' 1 = 1'; @@ -2541,10 +2541,10 @@ class DboSource extends DataSource { * * @param array $conditions Array or string of conditions * @param boolean $quoteValues If true, values should be quoted - * @param Model $model A reference to the Model instance making the query + * @param Model $Model A reference to the Model instance making the query * @return string SQL fragment */ - public function conditionKeysToString($conditions, $quoteValues = true, $model = null) { + public function conditionKeysToString($conditions, $quoteValues = true, Model $Model = null) { $out = array(); $data = $columnType = null; $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); @@ -2570,7 +2570,7 @@ class DboSource extends DataSource { } else { $key = $join; } - $value = $this->conditionKeysToString($value, $quoteValues, $model); + $value = $this->conditionKeysToString($value, $quoteValues, $Model); if (strpos($join, 'NOT') !== false) { if (strtoupper(trim($key)) === 'NOT') { @@ -2610,17 +2610,17 @@ class DboSource extends DataSource { if ($count === 1 && !preg_match('/\s+(?:NOT|\!=)$/', $key)) { $data = $this->_quoteFields($key) . ' = ('; if ($quoteValues) { - if (is_object($model)) { - $columnType = $model->getColumnType($key); + if ($Model !== null) { + $columnType = $Model->getColumnType($key); } $data .= implode(', ', $this->value($value, $columnType)); } $data .= ')'; } else { - $data = $this->_parseKey($model, $key, $value); + $data = $this->_parseKey($key, $value, $Model); } } else { - $ret = $this->conditionKeysToString($value, $quoteValues, $model); + $ret = $this->conditionKeysToString($value, $quoteValues, $Model); if (count($ret) > 1) { $data = '(' . implode(') AND (', $ret) . ')'; } elseif (isset($ret[0])) { @@ -2630,7 +2630,7 @@ class DboSource extends DataSource { } elseif (is_numeric($key) && !empty($value)) { $data = $this->_quoteFields($value); } else { - $data = $this->_parseKey($model, trim($key), $value); + $data = $this->_parseKey(trim($key), $value, $Model); } if ($data) { @@ -2646,12 +2646,12 @@ class DboSource extends DataSource { * Extracts a Model.field identifier and an SQL condition operator from a string, formats * and inserts values, and composes them into an SQL snippet. * - * @param Model $model Model object initiating the query * @param string $key An SQL key snippet containing a field and optional SQL operator * @param mixed $value The value(s) to be inserted in the string + * @param Model $Model Model object initiating the query * @return string */ - protected function _parseKey($model, $key, $value) { + protected function _parseKey($key, $value, Model $Model = null) { $operatorMatch = '/^(((' . implode(')|(', $this->_sqlOps); $operatorMatch .= ')\\x20?)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is'; $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false)); @@ -2670,17 +2670,22 @@ class DboSource extends DataSource { } $virtual = false; - if (is_object($model) && $model->isVirtualField($key)) { - $key = $this->_quoteFields($model->getVirtualField($key)); - $virtual = true; + $type = null; + + if ($Model !== null) { + if ($Model->isVirtualField($key)) { + $key = $this->_quoteFields($Model->getVirtualField($key)); + $virtual = true; + } + + $type = $Model->getColumnType($key); } - $type = is_object($model) ? $model->getColumnType($key) : null; $null = $value === null || (is_array($value) && empty($value)); if (strtolower($operator) === 'not') { $data = $this->conditionKeysToString( - array($operator => array($key => $value)), true, $model + array($operator => array($key => $value)), true, $Model ); return $data[0]; } @@ -2802,14 +2807,16 @@ class DboSource extends DataSource { * * @param array|string $keys Field reference, as a key (i.e. Post.title) * @param string $direction Direction (ASC or DESC) - * @param Model $model model reference (used to look for virtual field) + * @param Model $Model Model reference (used to look for virtual field) * @return string ORDER BY clause */ - public function order($keys, $direction = 'ASC', $model = null) { + public function order($keys, $direction = 'ASC', Model $Model = null) { if (!is_array($keys)) { $keys = array($keys); } + $keys = array_filter($keys); + $result = array(); while (!empty($keys)) { list($key, $dir) = each($keys); @@ -2823,6 +2830,7 @@ class DboSource extends DataSource { if (is_string($key) && strpos($key, ',') !== false && !preg_match('/\(.+\,.+\)/', $key)) { $key = array_map('trim', explode(',', $key)); } + if (is_array($key)) { //Flatten the array $key = array_reverse($key, true); @@ -2846,26 +2854,35 @@ class DboSource extends DataSource { $key = trim($key); - if (is_object($model) && $model->isVirtualField($key)) { - $key = '(' . $this->_quoteFields($model->getVirtualField($key)) . ')'; - } - list($alias, $field) = pluginSplit($key); - if (is_object($model) && $alias !== $model->alias && is_object($model->{$alias}) && $model->{$alias}->isVirtualField($key)) { - $key = '(' . $this->_quoteFields($model->{$alias}->getVirtualField($key)) . ')'; + if ($Model !== null) { + if ($Model->isVirtualField($key)) { + $key = '(' . $this->_quoteFields($Model->getVirtualField($key)) . ')'; + } + + list($alias, ) = pluginSplit($key); + + if ($alias !== $Model->alias && is_object($Model->{$alias}) && $Model->{$alias}->isVirtualField($key)) { + $key = '(' . $this->_quoteFields($Model->{$alias}->getVirtualField($key)) . ')'; + } } if (strpos($key, '.')) { $key = preg_replace_callback('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', array(&$this, '_quoteMatchedField'), $key); } + if (!preg_match('/\s/', $key) && strpos($key, '.') === false) { $key = $this->name($key); } + $key .= ' ' . trim($dir); + $result[] = $key; } + if (!empty($result)) { return ' ORDER BY ' . implode(', ', $result); } + return ''; } @@ -2885,7 +2902,7 @@ class DboSource extends DataSource { $fields = array($fields); } - if (!empty($Model)) { + if ($Model !== null) { foreach ($fields as $index => $key) { if ($Model->isVirtualField($key)) { $fields[$index] = '(' . $Model->getVirtualField($key) . ')';