Merge branch '2.0' into 2.1

Conflicts:
	lib/Cake/Model/BehaviorCollection.php
	lib/Cake/basics.php
This commit is contained in:
mark_story 2012-01-08 21:59:18 -05:00
commit 0e56d742b8
14 changed files with 206 additions and 105 deletions

View file

@ -333,7 +333,7 @@ class PaginatorComponent extends Component {
$options['order'] = array($options['sort'] => $direction);
}
if (!empty($whitelist)) {
if (!empty($whitelist) && isset($options['order']) && is_array($options['order'])) {
$field = key($options['order']);
if (!in_array($field, $whitelist)) {
$options['order'] = null;

View file

@ -312,7 +312,7 @@ class Configure {
$keys = array_keys($values);
foreach ($keys as $key) {
if (($c = self::read($key)) && is_array($values[$key]) && is_array($c)) {
$values[$key] = array_merge_recursive($c, $values[$key]);
$values[$key] = Set::merge($c, $values[$key]);
}
}
}

View file

@ -34,6 +34,20 @@ class TranslateBehavior extends ModelBehavior {
*/
public $runtime = array();
/**
* Stores the joinTable object for generating joins.
*
* @var object
*/
var $_joinTable;
/**
* Stores the runtime model for generating joins.
*
* @var Model
*/
var $_runtimeModel;
/**
* Callback
*
@ -94,6 +108,7 @@ class TranslateBehavior extends ModelBehavior {
}
$db = $model->getDataSource();
$RuntimeModel = $this->translateModel($model);
if (!empty($RuntimeModel->tablePrefix)) {
$tablePrefix = $RuntimeModel->tablePrefix;
} else {
@ -104,8 +119,11 @@ class TranslateBehavior extends ModelBehavior {
$joinTable->table = $RuntimeModel->table;
$joinTable->schemaName = $RuntimeModel->getDataSource()->getSchemaName();
$this->_joinTable = $joinTable;
$this->_runtimeModel = $RuntimeModel;
if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) {
$query['fields'] = 'COUNT(DISTINCT(' . $db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
$query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
$query['joins'][] = array(
'type' => 'INNER',
'alias' => $RuntimeModel->alias,
@ -116,6 +134,11 @@ class TranslateBehavior extends ModelBehavior {
$RuntimeModel->alias.'.locale' => $locale
)
);
$conditionFields = $this->_checkConditions($model, $query);
foreach ($conditionFields as $field) {
$query = $this->_addJoin($model, $query, $field, $field, $locale);
}
unset($this->_joinTable, $this->_runtimeModel);
return $query;
}
@ -145,45 +168,93 @@ class TranslateBehavior extends ModelBehavior {
unset($query['fields'][$key]);
}
}
if (is_array($locale)) {
foreach ($locale as $_locale) {
$model->virtualFields['i18n_' . $field . '_' . $_locale] = 'I18n__' . $field . '__' . $_locale . '.content';
if (!empty($query['fields'])) {
$query['fields'][] = 'i18n_' . $field . '_' . $_locale;
}
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__' . $field . '__' . $_locale,
'table' => $joinTable,
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
'I18n__' . $field . '__' . $_locale . '.model' => $model->name,
'I18n__' . $field . '__' . $_locale . '.' . $RuntimeModel->displayField => $aliasField,
'I18n__' . $field . '__' . $_locale . '.locale' => $_locale
)
);
}
} else {
$model->virtualFields['i18n_' . $field] = 'I18n__' . $field . '.content';
if (!empty($query['fields'])) {
$query['fields'][] = 'i18n_' . $field;
}
$query['joins'][] = array(
'type' => 'INNER',
'alias' => 'I18n__' . $field,
'table' => $joinTable,
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
'I18n__' . $field . '.model' => $model->name,
'I18n__' . $field . '.' . $RuntimeModel->displayField => $aliasField,
'I18n__' . $field . '.locale' => $locale
)
);
}
$query = $this->_addJoin($model, $query, $field, $aliasField, $locale);
}
}
$this->runtime[$model->alias]['beforeFind'] = $addFields;
unset($this->_joinTable, $this->_runtimeModel);
return $query;
}
/**
* Check a query's conditions for translated fields.
* Return an array of translated fields found in the conditions.
*
* @param Model $model The model being read.
* @param array $query The query array.
* @return array The list of translated fields that are in the conditions.
*/
protected function _checkConditions(Model $model, $query) {
$conditionFields = array();
if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions'])) ) {
return $conditionFields;
}
foreach ($query['conditions'] as $col => $val) {
foreach ($this->settings[$model->alias] as $field => $assoc) {
if (is_numeric($field)) {
$field = $assoc;
}
if (strpos($col, $field) !== false) {
$conditionFields[] = $field;
}
}
}
return $conditionFields;
}
/**
* Appends a join for translated fields and possibly a field.
*
* @param Model $model The model being worked on.
* @param object $joinTable The jointable object.
* @param array $query The query array to append a join to.
* @param string $field The field name being joined.
* @param string $aliasField The aliased field name being joined.
* @param mixed $locale The locale(s) having joins added.
* @param boolean $addField Whether or not to add a field.
* @return array The modfied query
*/
protected function _addJoin(Model $model, $query, $field, $aliasField, $locale, $addField = false) {
$db = ConnectionManager::getDataSource($model->useDbConfig);
$RuntimeModel = $this->_runtimeModel;
$joinTable = $this->_joinTable;
if (is_array($locale)) {
foreach ($locale as $_locale) {
$model->virtualFields['i18n_' . $field . '_' . $_locale] = 'I18n__' . $field . '__' . $_locale . '.content';
if (!empty($query['fields']) && is_array($query['fields'])) {
$query['fields'][] = 'i18n_'.$field.'_'.$_locale;
}
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__'.$field.'__'.$_locale,
'table' => $joinTable,
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $aliasField,
'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
)
);
}
} else {
$model->virtualFields['i18n_' . $field] = 'I18n__' . $field . '.content';
if (!empty($query['fields']) && is_array($query['fields'])) {
$query['fields'][] = 'i18n_'.$field;
}
$query['joins'][] = array(
'type' => 'INNER',
'alias' => 'I18n__'.$field,
'table' => $joinTable,
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
'I18n__'.$field.'.model' => $model->name,
'I18n__'.$field.'.'.$RuntimeModel->displayField => $aliasField,
'I18n__'.$field.'.locale' => $locale
)
);
}
return $query;
}

View file

@ -107,6 +107,9 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
$alias = $behavior;
$behavior = $config['className'];
}
$configDisabled = isset($config['enabled']) && $config['enabled'] === false;
unset($config['enabled'], $config['className']);
list($plugin, $name) = pluginSplit($behavior, true);
if (!isset($alias)) {
$alias = $name;
@ -166,8 +169,7 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener {
}
}
$enable = isset($config['enabled']) ? $config['enabled'] : true;
if ($enable) {
if (!in_array($alias, $this->_enabled) && !$configDisabled) {
$this->enable($alias);
} else {
$this->disable($alias);

View file

@ -182,7 +182,7 @@ class Sqlserver extends DboSource {
} else {
$tables = array();
while ($line = $result->fetch()) {
while ($line = $result->fetch(PDO::FETCH_NUM)) {
$tables[] = $line[0];
}
@ -222,7 +222,7 @@ class Sqlserver extends DboSource {
throw new CakeException(__d('cake_dev', 'Could not describe table for %s', $table));
}
foreach ($cols as $column) {
while ($column = $cols->fetch(PDO::FETCH_OBJ)) {
$field = $column->Field;
$fields[$field] = array(
'type' => $this->column($column),
@ -645,14 +645,7 @@ class Sqlserver extends DboSource {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
}
$table = $this->fullTableName($table);
$fields = implode(', ', array_map(array(&$this, 'name'), $fields));
$this->begin();
foreach ($values as $value) {
$holder = implode(', ', array_map(array(&$this, 'value'), $value));
$this->_execute("INSERT INTO {$table} ({$fields}) VALUES ({$holder})");
}
$this->commit();
parent::insertMulti($table, $fields, $values);
if ($hasPrimaryKey) {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF');
@ -717,9 +710,6 @@ class Sqlserver extends DboSource {
* @return string
*/
protected function _getPrimaryKey($model) {
if (!is_object($model)) {
$model = new Model(false, $model);
}
$schema = $this->describe($model);
foreach ($schema as $field => $props) {
if (isset($props['key']) && $props['key'] == 'primary') {

View file

@ -776,6 +776,26 @@ class PaginatorComponentTest extends CakeTestCase {
$this->assertEquals($expected, $result['order']);
}
/**
* Test that no sort doesn't trigger an error.
*
* @return void
*/
public function testValidateSortNoSort() {
$model = $this->getMock('Model');
$model->alias = 'model';
$model->expects($this->any())->method('hasField')->will($this->returnValue(true));
$options = array('direction' => 'asc');
$result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
$this->assertFalse(isset($result['order']));
$options = array('order' => 'invalid desc');
$result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
$this->assertEquals($options['order'], $result['order']);
}
/**
* test that maxLimit is respected
*

View file

@ -224,6 +224,8 @@ class ConfigureTest extends CakeTestCase {
$this->assertEquals('value2', Configure::read('Read'));
$this->assertEquals('buried2', Configure::read('Deep.Second.SecondDeepest'));
$this->assertEquals('buried', Configure::read('Deep.Deeper.Deepest'));
$this->assertEquals('Overwrite', Configure::read('TestAcl.classname'));
$this->assertEquals('one', Configure::read('TestAcl.custom'));
}
/**

View file

@ -58,6 +58,24 @@ class TranslateBehaviorTest extends CakeTestCase {
ClassRegistry::flush();
}
/**
* Test that count queries with conditions get the correct joins
*
* @return void
*/
function testCountWithConditions() {
$this->loadFixtures('Translate', 'TranslatedItem');
$Model =& new TranslatedItem();
$Model->locale = 'eng';
$result = $Model->find('count', array(
'conditions' => array(
'I18n__content.locale' => 'eng'
)
));
$this->assertEqual(3, $result);
}
/**
* testTranslateModel method
*

View file

@ -419,9 +419,22 @@ class BehaviorCollectionTest extends CakeTestCase {
*/
public $fixtures = array(
'core.apple', 'core.sample', 'core.article', 'core.user', 'core.comment',
'core.attachment', 'core.tag', 'core.articles_tag'
'core.attachment', 'core.tag', 'core.articles_tag', 'core.translate'
);
/**
* Test load() with enabled => false
*
*/
public function testLoadDisabled() {
$Apple = new Apple();
$this->assertSame($Apple->Behaviors->attached(), array());
$Apple->Behaviors->load('Translate', array('enabled' => false));
$this->assertTrue($Apple->Behaviors->attached('Translate'));
$this->assertFalse($Apple->Behaviors->enabled('Translate'));
}
/**
* Tests loading aliased behaviors
*/

View file

@ -111,7 +111,7 @@ class SqlserverTestDb extends Sqlserver {
*
* @package Cake.Test.Case.Model.Datasource.Database
*/
class SqlserverTestModel extends Model {
class SqlserverTestModel extends CakeTestModel {
/**
* name property
@ -183,7 +183,7 @@ class SqlserverTestModel extends Model {
*
* @package Cake.Test.Case.Model.Datasource.Database
*/
class SqlserverClientTestModel extends Model {
class SqlserverClientTestModel extends CakeTestModel {
/**
* name property
*
@ -224,6 +224,20 @@ class SqlserverTestResultIterator extends ArrayIterator {
* @return void
*/
public function closeCursor() {}
/**
* fetch method
*
* @return void
*/
public function fetch() {
if (!$this->valid()) {
return null;
}
$current = $this->current();
$this->next();
return $current;
}
}
/**
@ -283,7 +297,7 @@ class SqlserverTest extends CakeTestCase {
* @return void
*/
public function testQuoting() {
$expected = "1.200000";
$expected = "1.2";
$result = $this->db->value(1.2, 'float');
$this->assertSame($expected, $result);
@ -586,43 +600,6 @@ class SqlserverTest extends CakeTestCase {
$this->assertNull($result);
}
/**
* testInsertMulti
*
* @return void
*/
public function testInsertMulti() {
$this->db->describe = $this->model->schema();
$fields = array('id', 'name', 'login');
$values = array(
array(1, 'Larry', 'PhpNut'),
array(2, 'Renan', 'renan.saddam'));
$this->db->simulated = array();
$this->db->insertMulti($this->model, $fields, $values);
$result = $this->db->simulated;
$expected = array(
'SET IDENTITY_INSERT [sqlserver_test_models] ON',
"INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (1, N'Larry', N'PhpNut')",
"INSERT INTO [sqlserver_test_models] ([id], [name], [login]) VALUES (2, N'Renan', N'renan.saddam')",
'SET IDENTITY_INSERT [sqlserver_test_models] OFF'
);
$this->assertEquals($expected, $result);
$fields = array('name', 'login');
$values = array(
array('Larry', 'PhpNut'),
array('Renan', 'renan.saddam'));
$this->db->simulated = array();
$this->db->insertMulti($this->model, $fields, $values);
$result = $this->db->simulated;
$expected = array(
"INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES (N'Larry', N'PhpNut')",
"INSERT INTO [sqlserver_test_models] ([name], [login]) VALUES (N'Renan', N'renan.saddam')",
);
$this->assertEquals($expected, $result);
}
/**
* SQL server < 11 doesn't have proper limit/offset support, test that our hack works.
*

View file

@ -493,6 +493,9 @@ class DboSourceTest extends CakeTestCase {
* @return void
*/
public function testValue() {
if ($this->db instanceof Sqlserver) {
$this->markTestSkipped('Cannot run this test with SqlServer');
}
$result = $this->db->value('{$__cakeForeignKey__$}');
$this->assertEquals($result, '{$__cakeForeignKey__$}');

View file

@ -208,11 +208,11 @@ class ModelIntegrationTest extends BaseModelTest {
public function testDynamicBehaviorAttachment() {
$this->loadFixtures('Apple', 'Sample', 'Author');
$TestModel = new Apple();
$this->assertEquals($TestModel->Behaviors->attached(), array());
$this->assertEquals(array(), $TestModel->Behaviors->attached());
$TestModel->Behaviors->attach('Tree', array('left' => 'left_field', 'right' => 'right_field'));
$this->assertTrue(is_object($TestModel->Behaviors->Tree));
$this->assertEquals($TestModel->Behaviors->attached(), array('Tree'));
$this->assertEquals(array('Tree'), $TestModel->Behaviors->attached());
$expected = array(
'parent' => 'parent_id',
@ -223,16 +223,14 @@ class ModelIntegrationTest extends BaseModelTest {
'__parentChange' => false,
'recursive' => -1
);
$this->assertEquals($expected, $TestModel->Behaviors->Tree->settings['Apple']);
$this->assertEquals($TestModel->Behaviors->Tree->settings['Apple'], $expected);
$expected['enabled'] = false;
$TestModel->Behaviors->attach('Tree', array('enabled' => false));
$this->assertEquals($TestModel->Behaviors->Tree->settings['Apple'], $expected);
$this->assertEquals($TestModel->Behaviors->attached(), array('Tree'));
$this->assertEquals($expected, $TestModel->Behaviors->Tree->settings['Apple']);
$this->assertEquals(array('Tree'), $TestModel->Behaviors->attached());
$TestModel->Behaviors->detach('Tree');
$this->assertEquals($TestModel->Behaviors->attached(), array());
$this->assertEquals(array(), $TestModel->Behaviors->attached());
$this->assertFalse(isset($TestModel->Behaviors->Tree));
}

View file

@ -5,5 +5,8 @@ $config = array(
'Deeper' => array(
'Deepest' => 'buried'
)
),
'TestAcl' => array(
'classname' => 'Original'
)
);
);

View file

@ -5,5 +5,9 @@ $config = array(
'Second' => array(
'SecondDeepest' => 'buried2'
)
),
'TestAcl' => array(
'classname' => 'Overwrite',
'custom' => 'one'
)
);
);