Merge branch 'virtual-fields' into 1.3-misc

Conflicts:
	cake/tests/cases/libs/controller/controller.test.php
This commit is contained in:
Mark Story 2009-12-19 19:40:12 -05:00
commit 4ac0a55aff
6 changed files with 436 additions and 72 deletions
cake
libs
controller
model
tests/cases/libs

View file

@ -1102,9 +1102,11 @@ class Controller extends Object {
$value = $options['order'][$key]; $value = $options['order'][$key];
unset($options['order'][$key]); unset($options['order'][$key]);
if (isset($object->{$alias}) && $object->{$alias}->hasField($field)) { if ($object->hasField($field)) {
$options['order'][$alias . '.' . $field] = $value; $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; $options['order'][$alias . '.' . $field] = $value;
} }
} }

View file

@ -363,6 +363,9 @@ class DboSource extends DataSource {
if ($this->hasResult()) { if ($this->hasResult()) {
$this->resultSet($this->_result); $this->resultSet($this->_result);
$resultRow = $this->fetchResult(); $resultRow = $this->fetchResult();
if (!empty($resultRow)) {
$this->fetchVirtualField($resultRow);
}
return $resultRow; return $resultRow;
} else { } else {
return null; return null;
@ -393,6 +396,7 @@ class DboSource extends DataSource {
$out[] = $first; $out[] = $first;
} }
while ($this->hasResult() && $item = $this->fetchResult()) { while ($this->hasResult() && $item = $this->fetchResult()) {
$this->fetchVirtualField($item);
$out[] = $item; $out[] = $item;
} }
@ -410,6 +414,35 @@ class DboSource extends DataSource {
} }
} }
/**
* 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. * Returns a single field of the first of query results for a given SQL query, or false if empty.
* *
@ -420,7 +453,6 @@ class DboSource extends DataSource {
*/ */
function field($name, $sql) { function field($name, $sql) {
$data = $this->fetchRow($sql); $data = $this->fetchRow($sql);
if (!isset($data[$name]) || empty($data[$name])) { if (!isset($data[$name]) || empty($data[$name])) {
return false; return false;
} else { } else {
@ -1363,7 +1395,7 @@ class DboSource extends DataSource {
* @access public * @access public
* @see DboSource::renderStatement() * @see DboSource::renderStatement()
*/ */
function buildStatement($query, $model) { function buildStatement($query, &$model) {
$query = array_merge(array('offset' => null, 'joins' => array()), $query); $query = array_merge(array('offset' => null, 'joins' => array()), $query);
if (!empty($query['joins'])) { if (!empty($query['joins'])) {
$count = count($query['joins']); $count = count($query['joins']);
@ -1378,7 +1410,7 @@ class DboSource extends DataSource {
'fields' => implode(', ', $query['fields']), 'fields' => implode(', ', $query['fields']),
'table' => $query['table'], 'table' => $query['table'],
'alias' => $this->alias . $this->name($query['alias']), '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']), 'limit' => $this->limit($query['limit'], $query['offset']),
'joins' => implode(' ', $query['joins']), 'joins' => implode(' ', $query['joins']),
'group' => $this->group($query['group']) 'group' => $this->group($query['group'])
@ -1657,13 +1689,23 @@ class DboSource extends DataSource {
if (!isset($params[1])) { if (!isset($params[1])) {
$params[1] = 'count'; $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 'max':
case 'min': case 'min':
if (!isset($params[1])) { if (!isset($params[1])) {
$params[1] = $params[0]; $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; break;
} }
} }
@ -1790,6 +1832,24 @@ class DboSource extends DataSource {
return $data; return $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. * Generates the fields list of an SQL query.
* *
@ -1804,7 +1864,8 @@ class DboSource extends DataSource {
if (empty($alias)) { if (empty($alias)) {
$alias = $model->alias; $alias = $model->alias;
} }
if (empty($fields)) { $allFields = empty($fields);
if ($allFields) {
$fields = array_keys($model->schema()); $fields = array_keys($model->schema());
} elseif (!is_array($fields)) { } elseif (!is_array($fields)) {
$fields = String::tokenize($fields); $fields = String::tokenize($fields);
@ -1814,10 +1875,19 @@ class DboSource extends DataSource {
if (!$quote) { if (!$quote) {
return $fields; return $fields;
} }
$virtual = array();
if ($model->getVirtualField()) {
$keys = array_keys($model->getVirtualField());
$virtual = ($allFields) ? $keys : array_intersect($keys, $fields);
}
$count = count($fields); $count = count($fields);
if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) { if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) {
for ($i = 0; $i < $count; $i++) { 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 (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){
continue; continue;
} elseif (!preg_match('/^.+\\(.*\\)/', $fields[$i])) { } elseif (!preg_match('/^.+\\(.*\\)/', $fields[$i])) {
@ -1871,6 +1941,9 @@ class DboSource extends DataSource {
} }
} }
} }
if (!empty($virtual)) {
$fields = array_merge($fields,$this->_constructVirtualFields($model, $alias, $virtual));
}
return array_unique($fields); return array_unique($fields);
} }
@ -2053,6 +2126,11 @@ class DboSource extends DataSource {
} }
} }
$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); $type = (is_object($model) ? $model->getColumnType($key) : null);
@ -2067,7 +2145,7 @@ class DboSource extends DataSource {
$value = $this->value($value, $type); $value = $this->value($value, $type);
if ($key !== '?') { if (!$virtual && $key !== '?') {
$isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false); $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false);
$key = $isKey ? $this->__quoteFields($key) : $this->name($key); $key = $isKey ? $this->__quoteFields($key) : $this->name($key);
} }
@ -2105,7 +2183,9 @@ class DboSource extends DataSource {
break; break;
} }
} }
if ($virtual) {
return "({$key}) {$operator} {$value}";
}
return "{$key} {$operator} {$value}"; return "{$key} {$operator} {$value}";
} }
@ -2172,69 +2252,71 @@ class DboSource extends DataSource {
* *
* @param string $key Field reference, as a key (i.e. Post.title) * @param string $key Field reference, as a key (i.e. Post.title)
* @param string $direction Direction (ASC or DESC) * @param string $direction Direction (ASC or DESC)
* @param object $model model reference (used to look for virtual field)
* @return string ORDER BY clause * @return string ORDER BY clause
* @access public * @access public
*/ */
function order($keys, $direction = 'ASC') { function order($keys, $direction = 'ASC', &$model = null) {
if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) { if (!is_array($keys)) {
$keys = array_map('trim', explode(',', $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)) { if (is_numeric($key)) {
$keys = array_filter($keys); $key = $dir;
} $dir = $direction;
}
if (empty($keys) || (is_array($keys) && isset($keys[0]) && empty($keys[0]))) { if (is_string($key) && strpos($key, ',') && !preg_match('/\(.+\,.+\)/', $key)) {
return ''; $key = array_map('trim', explode(',', $key));
} }
if (is_array($key)) {
if (is_array($keys)) { //Flatten the array
$keys = (Set::countDim($keys) > 1) ? array_map(array(&$this, 'order'), $keys) : $keys; $key = array_reverse($key, true);
foreach ($key as $k => $v) {
foreach ($keys as $key => $value) { if (is_numeric($k)) {
if (is_numeric($key)) { array_unshift($keys, $v);
$key = $value = ltrim(str_replace('ORDER BY ', '', $this->order($value)));
$value = (!preg_match('/\\x20ASC|\\x20DESC/i', $key) ? ' ' . $direction : '');
} else {
$value = ' ' . $value;
}
if (!preg_match('/^.+\\(.*\\)/', $key) && !strpos($key, ',')) {
if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $dir)) {
$dir = $dir[0];
$key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
} else { } else {
$dir = ''; $keys = array($k => $v) + $keys;
} }
$key = trim($key);
if (!preg_match('/\s/', $key)) {
$key = $this->name($key);
}
$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, '.')) { if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $_dir)) {
preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $keys, $result, PREG_PATTERN_ORDER); $dir = $_dir[0];
$pregCount = count($result[0]); $key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key);
}
for ($i = 0; $i < $pregCount; $i++) { if (strpos($key, '.')) {
if (!is_numeric($result[0][$i])) { preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $key, $matches, PREG_PATTERN_ORDER);
$keys = preg_replace('/' . $result[0][$i] . '/', $this->name($result[0][$i]), $keys); $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)) { $key = trim($key);
$direction = $match[1]; if (!preg_match('/\s/', $key) && !strpos($key,'.')) {
return ' ORDER BY ' . preg_replace('/' . $match[1] . '/', '', $keys) . $direction; if (is_object($model) && $model->isVirtualField($key)) {
$key = '('.$this->__quoteFields($model->getVirtualField($key)).')';
} else {
$key = $this->name($key);
}
}
$key .= ' ' . trim($dir);
$result[] = $key;
} }
return ' ORDER BY ' . $keys . ' ' . $direction; if (!empty($result)) {
return ' ORDER BY ' . implode(', ', $result);
}
return '';
} }
/** /**

View file

@ -802,7 +802,7 @@ class Model extends Overloadable {
} }
/** /**
* This function does two things: * This function does two things:
* *
* 1. it scans the array $one for the primary key, * 1. it scans the array $one for the primary key,
* and if that's found, it sets the current id to the value of $one[id]. * and if that's found, it sets the current id to the value of $one[id].
@ -1009,21 +1009,28 @@ class Model extends Overloadable {
* Returns true if the supplied field exists in the model's database table. * Returns true if the supplied field exists in the model's database table.
* *
* @param mixed $name Name of field to look for, or an array of names * @param mixed $name Name of field to look for, or an array of names
* @param boolean $checkVirtual checks if the field is declared as virtual
* @return mixed If $name is a string, returns a boolean indicating whether the field exists. * @return mixed If $name is a string, returns a boolean indicating whether the field exists.
* If $name is an array of field names, returns the first field that exists, * If $name is an array of field names, returns the first field that exists,
* or false if none exist. * or false if none exist.
* @access public * @access public
*/ */
function hasField($name) { function hasField($name, $checkVirtual = false) {
if (is_array($name)) { if (is_array($name)) {
foreach ($name as $n) { foreach ($name as $n) {
if ($this->hasField($n)) { if ($this->hasField($n, $checkVirtual)) {
return $n; return $n;
} }
} }
return false; return false;
} }
if ($checkVirtual && !empty($this->virtualFields)) {
if ($this->isVirtualField($name)) {
return true;
}
}
if (empty($this->_schema)) { if (empty($this->_schema)) {
$this->schema(); $this->schema();
} }
@ -1034,9 +1041,39 @@ class Model extends Overloadable {
return false; return false;
} }
/**
* Returns true if the supplied field is a model Virtual Field
*
* @param mixed $name Name of field to look for
* @return boolean indicating whether the field exists as a model virtual field.
* @access public
*/
function isVirtualField($field) {
return !empty($this->virtualFields) && is_string($field) && array_key_exists($field, $this->virtualFields);
}
/**
* Returns the expression for a model virtual field
*
* @param mixed $name Name of field to look for
* @return mixed If $field is string expression bound to virtual field $field
* If $field is null, returns an array of all model virtual fields
* or false if none $field exist.
* @access public
*/
function getVirtualField($field = null) {
if ($field == null) {
return empty($this->virtualFields) ? false : $this->virtualFields;
}
if ($this->isVirtualField($field)) {
return $this->virtualFields[$field];
}
return false;
}
/** /**
* Initializes the model for writing a new record, loading the default values * Initializes the model for writing a new record, loading the default values
* for those fields that are not defined in $data, and clearing previous validation errors. * for those fields that are not defined in $data, and clearing previous validation errors.
* Especially helpful for saving data in loops. * Especially helpful for saving data in loops.
* *
* @param mixed $data Optional data array to assign to the model after it is created. If null or false, * @param mixed $data Optional data array to assign to the model after it is created. If null or false,
@ -1121,7 +1158,8 @@ class Model extends Overloadable {
} else { } else {
$recursive = $this->recursive; $recursive = $this->recursive;
} }
if ($data = $this->find($conditions, $name, $order, $recursive)) { $fields = $name;
if ($data = $this->find('first', compact('conditions', 'fields', 'order', 'recursive'))) {
if (strpos($name, '.') === false) { if (strpos($name, '.') === false) {
if (isset($data[$this->alias][$name])) { if (isset($data[$this->alias][$name])) {
return $data[$this->alias][$name]; return $data[$this->alias][$name];
@ -1132,9 +1170,8 @@ class Model extends Overloadable {
return $data[$name[0]][$name[1]]; return $data[$name[0]][$name[1]];
} }
} }
if (!empty($data[0])) { if (isset($data[0]) && count($data[0]) > 0) {
$name = key($data[0]); return array_shift($data[0]);
return $data[0][$name];
} }
} else { } else {
return false; return false;
@ -1989,7 +2026,7 @@ class Model extends Overloadable {
* second parameter options for finding ( indexed array, including: 'conditions', 'limit', * second parameter options for finding ( indexed array, including: 'conditions', 'limit',
* 'recursive', 'page', 'fields', 'offset', 'order') * 'recursive', 'page', 'fields', 'offset', 'order')
* *
* Eg: * Eg:
* {{{ * {{{
* find('all', array( * find('all', array(
* 'conditions' => array('name' => 'Thomas Anderson'), * 'conditions' => array('name' => 'Thomas Anderson'),
@ -2560,7 +2597,7 @@ class Model extends Overloadable {
$valid = preg_match($rule, $data[$fieldName]); $valid = preg_match($rule, $data[$fieldName]);
} elseif (Configure::read('debug') > 0) { } elseif (Configure::read('debug') > 0) {
$error = sprintf( $error = sprintf(
__('Could not find validation handler %s for %s', true), __('Could not find validation handler %s for %s', true),
$rule, $rule,
$fieldName $fieldName
); );

View file

@ -1281,5 +1281,26 @@ class ControllerTest extends CakeTestCase {
$expected = array(404 => 'Sorry Bro'); $expected = array(404 => 'Sorry Bro');
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
} }
function testPaginateOrderVirtualField() {
$Controller =& new Controller();
$Controller->uses = array('ControllerPost', 'ControllerComment');
$Controller->params['url'] = array();
$Controller->constructClasses();
$Controller->ControllerPost->virtualFields = array(
'offset_test' => 'ControllerPost.id + 1'
);
$Controller->paginate = array(
'fields' => array('id', 'title', 'offset_test'),
'order' => array('offset_test' => 'DESC')
);
$result = $Controller->paginate('ControllerPost');
$this->assertEqual(Set::extract($result, '{n}.ControllerPost.offset_test'), array(4, 3, 2));
$Controller->passedArgs = array('sort' => 'offset_test', 'direction' => 'asc');
$result = $Controller->paginate('ControllerPost');
$this->assertEqual(Set::extract($result, '{n}.ControllerPost.offset_test'), array(2, 3, 4));
}
} }
?> ?>

View file

@ -3403,7 +3403,7 @@ class DboSourceTest extends CakeTestCase {
$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC\s*$/', $result); $this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC\s*$/', $result);
$result = $this->testDb->order("Dealer.id = 7 desc, Dealer.id = 3 desc, Dealer.title asc"); $result = $this->testDb->order("Dealer.id = 7 desc, Dealer.id = 3 desc, Dealer.title asc");
$expected = " ORDER BY `Dealer`.`id` = 7 desc, `Dealer`.`id` = 3 desc, `Dealer`.`title` asc"; $expected = " ORDER BY `Dealer`.`id` = 7 desc, `Dealer`.`id` = 3 desc, `Dealer`.`title` asc";
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $this->testDb->order(array("Page.name" => "='test' DESC")); $result = $this->testDb->order(array("Page.name" => "='test' DESC"));
@ -3609,8 +3609,8 @@ class DboSourceTest extends CakeTestCase {
$this->testDb->fieldParameters['param'] = array( $this->testDb->fieldParameters['param'] = array(
'value' => 'COLLATE', 'value' => 'COLLATE',
'quote' => false, 'quote' => false,
'join' => ' ', 'join' => ' ',
'column' => 'Collate', 'column' => 'Collate',
'position' => 'beforeDefault', 'position' => 'beforeDefault',
'options' => array('GOOD', 'OK') 'options' => array('GOOD', 'OK')
); );
@ -4081,5 +4081,147 @@ class DboSourceTest extends CakeTestCase {
$this->assertNoPattern('/Num:/s', $contents); $this->assertNoPattern('/Num:/s', $contents);
$this->assertNoPattern('/Took:/s', $contents); $this->assertNoPattern('/Took:/s', $contents);
} }
/**
* test fields generating usable virtual fields to use in query
*
* @return void
*/
function testVirtualFields() {
$this->loadFixtures('Article');
$Article =& ClassRegistry::init('Article');
$Article->virtualFields = array(
'this_moment' => 'NOW()',
'two' => '1 + 1',
'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
);
$result = $this->db->fields($Article);
$expected = array(
'`Article`.`id`',
'`Article`.`user_id`',
'`Article`.`title`',
'`Article`.`body`',
'`Article`.`published`',
'`Article`.`created`',
'`Article`.`updated`',
'(NOW()) AS `Article__this_moment`',
'(1 + 1) AS `Article__two`',
'(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS `Article__comment_count`'
);
$this->assertEqual($expected,$result);
$result = $this->db->fields($Article, null, array('this_moment','title'));
$expected = array(
'`Article`.`title`',
'(NOW()) AS `Article__this_moment`',
);
$this->assertEqual($expected,$result);
}
/**
* test conditions to generate query conditions for virtual fields
*
* @return void
*/
function testVirtualFieldsInConditions() {
$this->loadFixtures('Article');
$Article =& ClassRegistry::init('Article');
$Article->virtualFields = array(
'this_moment' => 'NOW()',
'two' => '1 + 1',
'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
);
$conditions = array('two' => 2);
$result = $this->db->conditions($conditions,true,false,$Article);
$expected = '(1 + 1) = 2';
$this->assertEqual($expected, $result);
$conditions = array('this_moment BETWEEN ? AND ?' => array(1,2));
$expected = 'NOW() BETWEEN 1 AND 2';
$result = $this->db->conditions($conditions, true, false, $Article);
$this->assertEqual($expected, $result);
$conditions = array('comment_count >' => 5);
$expected = '(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) > 5';
$result = $this->db->conditions($conditions, true, false, $Article);
$this->assertEqual($expected, $result);
$conditions = array('NOT' => array('two' => 2));
$result = $this->db->conditions($conditions, true, false, $Article);
$expected = 'NOT ((1 + 1) = 2)';
$this->assertEqual($expected, $result);
}
/**
* test order to generate query order clause for virtual fields
*
* @return void
*/
function testVirtualFieldsInOrder() {
$this->loadFixtures('Article');
$Article =& ClassRegistry::init('Article');
$Article->virtualFields = array(
'this_moment' => 'NOW()',
'two' => '1 + 1',
);
$order = array('two', 'this_moment');
$result = $this->db->order($order, 'ASC', $Article);
$expected = ' ORDER BY (1 + 1) ASC, (NOW()) ASC';
$this->assertEqual($expected, $result);
}
/**
* test calculate to generate claculate statements on virtual fields
*
* @return void
*/
function testVirtualFieldsInCalculate() {
$this->loadFixtures('Article');
$Article =& ClassRegistry::init('Article');
$Article->virtualFields = array(
'this_moment' => 'NOW()',
'two' => '1 + 1',
'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
' WHERE Article.id = ' . $this->db->fullTableName('comments'). '.article_id'
);
$result = $this->db->calculate($Article, 'count', array('this_moment'));
$expected = 'COUNT(NOW()) AS `count`';
$this->assertEqual($expected, $result);
$result = $this->db->calculate($Article, 'max', array('comment_count'));
$expected = 'MAX(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS `comment_count`';
$this->assertEqual($expected, $result);
}
/**
* test a full example of using virtual fields
*
* @return void
*/
function testVirtualFieldsFetch() {
$this->loadFixtures('Article', 'Comment');
$Article =& ClassRegistry::init('Article');
$Article->virtualFields = array(
'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
);
$conditions = array('comment_count >' => 2);
$query = 'SELECT ' . join(',',$this->db->fields($Article, null, array('id', 'comment_count'))) .
' FROM ' . $this->db->fullTableName($Article) . ' Article ' . $this->db->conditions($conditions, true, true, $Article);
$result = $this->db->fetchAll($query);
$expected = array(array(
'Article' => array('id' => 1, 'comment_count' => 4)
));
$this->assertEqual($expected, $result);
}
} }
?> ?>

View file

@ -215,14 +215,14 @@ class ModelReadTest extends BaseModelTest {
array('Product' => array('type' => 'Toy'), array('price' => 3)) array('Product' => array('type' => 'Toy'), array('price' => 3))
); );
$result = $Product->find('all',array( $result = $Product->find('all',array(
'fields'=>array('Product.type','MIN(Product.price) as price'), 'fields'=>array('Product.type', 'MIN(Product.price) as price'),
'group'=> 'Product.type', 'group'=> 'Product.type',
'order' => 'Product.type ASC' 'order' => 'Product.type ASC'
)); ));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $Product->find('all', array( $result = $Product->find('all', array(
'fields'=>array('Product.type','MIN(Product.price) as price'), 'fields'=>array('Product.type', 'MIN(Product.price) as price'),
'group'=> array('Product.type'), 'group'=> array('Product.type'),
'order' => 'Product.type ASC')); 'order' => 'Product.type ASC'));
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
@ -7161,5 +7161,85 @@ class ModelReadTest extends BaseModelTest {
$comments = $Comment->find('first'); $comments = $Comment->find('first');
$this->assertEqual($comments['Comment']['querytype'], 'first'); $this->assertEqual($comments['Comment']['querytype'], 'first');
} }
/**
* testVirtualFields()
*
* Test correct fetching of virtual fields
* currently is not possible to do Relation.virtualField
*
* @access public
* @return void
*/
function testVirtualFields() {
$this->loadFixtures('Post','Author');
$Post = ClassRegistry::init('Post');
$Post->virtualFields = array('two' => "1 + 1");
$result = $Post->find('first');
$this->assertEqual($result['Post']['two'], 2);
$Post->Author->virtualFields = array('false' => '1 = 2');
$result = $Post->find('first');
$this->assertEqual($result['Post']['two'], 2);
$this->assertEqual($result['Author']['false'],false);
$result = $Post->find('first',array('fields' => array('author_id')));
$this->assertFalse(isset($result['Post']['two']));
$this->assertFalse(isset($result['Author']['false']));
$result = $Post->find('first',array('fields' => array('author_id', 'two')));
$this->assertEqual($result['Post']['two'], 2);
$this->assertFalse(isset($result['Author']['false']));
$result = $Post->find('first',array('fields' => array('two')));
$this->assertEqual($result['Post']['two'], 2);
$Post->id = 1;
$result = $Post->field('two');
$this->assertEqual($result, 2);
$result = $Post->find('first',array(
'conditions' => array('two' => 2),
'limit' => 1
));
$this->assertEqual($result['Post']['two'], 2);
$result = $Post->find('first',array(
'conditions' => array('two <' => 3),
'limit' => 1
));
$this->assertEqual($result['Post']['two'], 2);
$result = $Post->find('first',array(
'conditions' => array('NOT' => array('two >' => 3)),
'limit' => 1
));
$this->assertEqual($result['Post']['two'], 2);
$dbo =& $Post->getDataSource();
$Post->virtualFields = array('other_field' => 'Post.id + 1');
$result = $Post->find('first',array(
'conditions' => array('other_field' => 3),
'limit' => 1
));
$this->assertEqual($result['Post']['id'], 2);
$Post->virtualFields = array('other_field' => 'Post.id + 1');
$result = $Post->find('all',array(
'fields' => array($dbo->calculate($Post, 'max',array('other_field')))
));
$this->assertEqual($result[0][0]['other_field'], 4);
ClassRegistry::flush();
$Writing = ClassRegistry::init(array('class' => 'Post', 'alias' => 'Writing'), 'Model');
$Writing->virtualFields = array('two' => "1 + 1");
$result = $Writing->find('first');
$this->assertEqual($result['Writing']['two'], 2);
$Post->create();
$Post->virtualFields = array('other_field' => 'COUNT(Post.id) + 1');
$result = $Post->field('other_field');
$this->assertEqual($result, 4);
}
} }
?> ?>