diff --git a/cake/libs/model/datasources/dbo/dbo_adodb.php b/cake/libs/model/datasources/dbo/dbo_adodb.php index c1b0faf57..b4e63875b 100644 --- a/cake/libs/model/datasources/dbo/dbo_adodb.php +++ b/cake/libs/model/datasources/dbo/dbo_adodb.php @@ -426,10 +426,7 @@ class DboAdodb extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } } ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_firebird.php b/cake/libs/model/datasources/dbo/dbo_firebird.php index b51ac441c..cd7066e23 100644 --- a/cake/libs/model/datasources/dbo/dbo_firebird.php +++ b/cake/libs/model/datasources/dbo/dbo_firebird.php @@ -511,10 +511,7 @@ class DboFirebird extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } } ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_mssql.php b/cake/libs/model/datasources/dbo/dbo_mssql.php index 6ad7f6bf6..f71cd852d 100644 --- a/cake/libs/model/datasources/dbo/dbo_mssql.php +++ b/cake/libs/model/datasources/dbo/dbo_mssql.php @@ -603,11 +603,8 @@ class DboMssql extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } - } + ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_oracle.php b/cake/libs/model/datasources/dbo/dbo_oracle.php index e52263739..7e12b3b70 100644 --- a/cake/libs/model/datasources/dbo/dbo_oracle.php +++ b/cake/libs/model/datasources/dbo/dbo_oracle.php @@ -624,10 +624,8 @@ class DboOracle extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } } + ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_postgres.php b/cake/libs/model/datasources/dbo/dbo_postgres.php index 5485aa627..9edaf72cc 100644 --- a/cake/libs/model/datasources/dbo/dbo_postgres.php +++ b/cake/libs/model/datasources/dbo/dbo_postgres.php @@ -586,10 +586,8 @@ class DboPostgres extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } } + ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_sqlite.php b/cake/libs/model/datasources/dbo/dbo_sqlite.php index 42d026b33..a974fc59a 100644 --- a/cake/libs/model/datasources/dbo/dbo_sqlite.php +++ b/cake/libs/model/datasources/dbo/dbo_sqlite.php @@ -422,10 +422,7 @@ class DboSqlite extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } /** * Generate a database-native column schema string diff --git a/cake/libs/model/datasources/dbo/dbo_sybase.php b/cake/libs/model/datasources/dbo/dbo_sybase.php index d58afe014..74a86d522 100644 --- a/cake/libs/model/datasources/dbo/dbo_sybase.php +++ b/cake/libs/model/datasources/dbo/dbo_sybase.php @@ -388,10 +388,8 @@ class DboSybase extends DboSource { * @param array $values */ function insertMulti($table, $fields, $values) { - $count = count($values); - for ($x = 0; $x < $count; $x++) { - $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); - } + parent::__insertMulti($table, $fields, $values); } } + ?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php index 07f9e8652..8f7c090cb 100644 --- a/cake/libs/model/datasources/dbo_source.php +++ b/cake/libs/model/datasources/dbo_source.php @@ -721,7 +721,7 @@ class DboSource extends DataSource { $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey']; $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']); - list($with, $habtmFields) = $this->getJoinModel($model, $model->hasAndBelongsToMany[$association]['with'], $joinKeys); + list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys); $habtmFieldsCount = count($habtmFields); $q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack); @@ -1116,7 +1116,7 @@ class DboSource extends DataSource { if (isset($assocData['with']) && !empty($assocData['with'])) { $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']); - list($with, $joinFields) = $this->getJoinModel($model, $assocData['with'], $joinKeys); + list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys); if (is_array($joinFields) && !empty($joinFields)) { $joinFields = $this->fields($model->{$with}, $model->{$with}->alias, $joinFields); @@ -1186,24 +1186,6 @@ class DboSource extends DataSource { } return array(); } -/** - * Gets the name and fields to be used by a join model. This allows specifying join fields in the association definition. - * - * @param object $model The model to be joined - * @param mixed $with The 'with' key of the model association - * @param array $keys Any join keys which must be merged with the keys queried - * @return array - */ - function getJoinModel($model, $assoc, $keys = array()) { - if (is_string($assoc)) { - return array($assoc, array_keys($model->{$assoc}->schema())); - } elseif (is_array($assoc)) { - $with = key($assoc); - return array($with, array_unique(array_merge($assoc[$with], $keys))); - } else { - trigger_error(sprintf(__('Invalid join model settings in %s', true), $model->alias), E_USER_WARNING); - } - } function buildJoinStatement($join) { $data = array_merge(array( @@ -1965,9 +1947,29 @@ class DboSource extends DataSource { * @param array $values */ function insertMulti($table, $fields, $values) { + if (is_object($table)) { + $table = $this->fullTableName($table); + } $values = implode(', ', $values); $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}"); } +/** + * Inserts multiple values into a join table + * + * @param string $table + * @param string $fields + * @param array $values + * @access protected + */ + function __insertMulti($table, $fields, $values) { + if (is_object($table)) { + $table = $this->fullTableName($table); + } + $count = count($values); + for ($x = 0; $x < $count; $x++) { + $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); + } + } /** * Returns an array of the indexes in given datasource name. * diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php index 6ef61860b..7254fcf53 100644 --- a/cake/libs/model/model.php +++ b/cake/libs/model/model.php @@ -722,6 +722,9 @@ class Model extends Overloadable { $data = $class; break; + case 'unique': + $data = true; + break; } $this->{$type}[$assocKey][$key] = $data; } @@ -1170,8 +1173,11 @@ class Model extends Overloadable { $fields = $values = array(); foreach ($this->data as $n => $v) { - if (isset($v[$n]) && in_array($n, array_keys($this->hasAndBelongsToMany))) { - $joined[] = $v; + if (isset($this->hasAndBelongsToMany[$n])) { + if (isset($v[$n])) { + $v = $v[$n]; + } + $joined[$n] = $v; } else { if ($n === $this->alias) { foreach (array('created', 'updated', 'modified') as $field) { @@ -1182,8 +1188,7 @@ class Model extends Overloadable { foreach ($v as $x => $y) { if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) { - $fields[] = $x; - $values[] = $y; + list($fields[], $values[]) = array($x, $y); } } } @@ -1206,8 +1211,7 @@ class Model extends Overloadable { foreach ($this->_schema as $field => $properties) { if ($this->primaryKey === $field) { if (empty($this->data[$this->alias][$this->primaryKey]) && $this->_schema[$field]['type'] === 'string' && $this->_schema[$field]['length'] === 36) { - $fields[] = $this->primaryKey; - $values[] = String::uuid(); + list($fields[], $values[]) = array($this->primaryKey, String::uuid()); } break; } @@ -1260,43 +1264,46 @@ class Model extends Overloadable { */ function __saveMulti($joined, $id) { $db =& ConnectionManager::getDataSource($this->useDbConfig); - foreach ($joined as $x => $y) { - foreach ($y as $assoc => $value) { - if (isset($this->hasAndBelongsToMany[$assoc])) { - $joinTable[$assoc] = $this->hasAndBelongsToMany[$assoc]['joinTable']; - $mainKey[$assoc] = $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']); - $keys[] = $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']); - $keys[] = $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']); - $fields[$assoc] = join(',', $keys); - unset($keys); - foreach ($value as $update) { - if (!empty($update)) { - $values[] = $db->value($id, $this->getColumnType($this->primaryKey)); - $values[] = $db->value($update); - $values = join(',', $values); + foreach ($joined as $assoc => $value) { + if (isset($this->hasAndBelongsToMany[$assoc])) { + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + $conditions = array($this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id); + $links = array(); + + if ($this->hasAndBelongsToMany[$assoc]['unique']) { + $this->{$join}->deleteAll($conditions); + } else { + list($recursive, $fields) = array(-1, $this->hasAndBelongsToMany[$assoc]['associationForeignKey']); + $links = Set::extract( + $this->{$join}->find('all', compact('conditions', 'recursive', 'fields')), + "{n}.{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey'] + ); + } + + foreach ($value as $update) { + if (!empty($update)) { + if (is_array($update)) { + $update[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id; + $this->{$join}->create($update); + $this->{$join}->save(); + } elseif (!in_array($update, $links)) { + $values = join(',', array( + $db->value($id, $this->getColumnType($this->primaryKey)), + $db->value($update) + )); $newValues[] = "({$values})"; - unset ($values); + unset($values); } } - - if (!empty($newValues)) { - $newValue[$assoc] = $newValues; - unset($newValues); - } else { - $newValue[$assoc] = array(); - } } - } - } - if (isset($joinTable) && is_array($newValue)) { - foreach ($newValue as $loopAssoc => $val) { - $table = $db->name($db->fullTableName($joinTable[$loopAssoc])); - $db->query("DELETE FROM {$table} WHERE {$mainKey[$loopAssoc]} = '{$id}'"); - - if (!empty($newValue[$loopAssoc])) { - $db->insertMulti($table, $fields[$loopAssoc], $newValue[$loopAssoc]); + if (!empty($newValues)) { + $fields = join(',', array( + $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']), + $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']) + )); + $db->insertMulti($this->{$join}, $fields, $newValues); } } } @@ -2427,6 +2434,24 @@ class Model extends Overloadable { return null; } } +/** + * Gets the name and fields to be used by a join model. This allows specifying join fields in the association definition. + * + * @param object $model The model to be joined + * @param mixed $with The 'with' key of the model association + * @param array $keys Any join keys which must be merged with the keys queried + * @return array + */ + function joinModel($assoc, $keys = array()) { + if (is_string($assoc)) { + return array($assoc, array_keys($this->{$assoc}->schema())); + } elseif (is_array($assoc)) { + $with = key($assoc); + return array($with, array_unique(array_merge($assoc[$with], $keys))); + } else { + trigger_error(sprintf(__('Invalid join model settings in %s', true), $model->alias), E_USER_WARNING); + } + } /** * Before find callback * diff --git a/cake/tests/cases/libs/model/model.test.php b/cake/tests/cases/libs/model/model.test.php index 6d4f07027..586c3541a 100644 --- a/cake/tests/cases/libs/model/model.test.php +++ b/cake/tests/cases/libs/model/model.test.php @@ -592,6 +592,33 @@ class ModelTest extends CakeTestCase { 'JoinThing' => array('doomed' => '1', 'something_id' => '3', 'something_else_id' => '1'))))); $this->assertEqual($result, $expected); + + $result = $this->model->findById(1); + $expected = array( + 'Something' => array('id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'), + 'SomethingElse' => array(array('id' => '2', 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31', + 'JoinThing' => array('doomed' => '1', 'something_id' => '1', 'something_else_id' => '2')))); + $this->assertEqual($result, $expected); + + $this->model->hasAndBelongsToMany['SomethingElse']['unique'] = false; + $this->model->create(array( + 'Something' => array('id' => 1), + 'SomethingElse' => array(3, array('something_else_id' => 1, 'doomed' => '1')) + )); + $ts = date('Y-m-d H:i:s'); + $this->model->save(); + + $result = $this->model->findById(1); + $expected = array( + 'Something' => array('id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => $ts), + 'SomethingElse' => array( + array('id' => '2', 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31', + 'JoinThing' => array('doomed' => '1', 'something_id' => '1', 'something_else_id' => '2')), + array('id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31', + 'JoinThing' => array('doomed' => '1', 'something_id' => '1', 'something_else_id' => '1')), + array('id' => '3', 'title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31', + 'JoinThing' => array('doomed' => null, 'something_id' => '1', 'something_else_id' => '3')))); + $this->assertEqual($result, $expected); } function testFindAllRecursiveSelfJoin() { @@ -951,6 +978,8 @@ class ModelTest extends CakeTestCase { $result = Set::combine($this->model->find('all', array('order' => 'Article.title ASC', 'fields' => array('id', 'title', 'user_id'))), '{n}.Article.id', '{n}.Article.title', '{n}.Article.user_id'); $expected = array(1 => array(1 => 'First Article', 3 => 'Third Article'), 3 => array(2 => 'Second Article')); $this->assertEqual($result, $expected); + + $this->model =& new Apple(); } function testFindField() { @@ -1867,9 +1896,7 @@ class ModelTest extends CakeTestCase { $result = $this->model->Comment->create() && $this->model->Comment->save($data); $this->assertTrue($result); - $data = array('Attachment' => array( - 'comment_id' => '7', 'attachment' => 'newattachment.zip', 'created' => '2007-03-18 15:02:23', 'updated' => '2007-03-18 15:04:31' - )); + $data = array('Attachment' => array('comment_id' => '7', 'attachment' => 'newattachment.zip', 'created' => '2007-03-18 15:02:23', 'updated' => '2007-03-18 15:04:31')); $result = $this->model->Comment->Attachment->save($data); $this->assertTrue($result); @@ -3140,6 +3167,10 @@ class ModelTest extends CakeTestCase { $this->assertEqual($afterFindData, $noAfterFindData); } + function testAfterFindAssociation() { + + } + function testDeconstructFields() { $this->model =& new Apple(); diff --git a/cake/tests/cases/libs/view/helpers/form.test.php b/cake/tests/cases/libs/view/helpers/form.test.php index 941235d2d..added51b0 100644 --- a/cake/tests/cases/libs/view/helpers/form.test.php +++ b/cake/tests/cases/libs/view/helpers/form.test.php @@ -478,9 +478,6 @@ class FormHelperTest extends CakeTestCase { $this->assertPattern('/The Legend<\/legend>/', $result); $View = ClassRegistry::getObject('view'); - $View->testing = true; - $this->Form->testing = true; - unset($View->testing, $this->Form->testing); $this->Form->params['prefix'] = 'admin'; $this->Form->action = 'admin_edit'; diff --git a/cake/tests/cases/libs/view/view.test.php b/cake/tests/cases/libs/view/view.test.php index 114d0a839..2024e6157 100644 --- a/cake/tests/cases/libs/view/view.test.php +++ b/cake/tests/cases/libs/view/view.test.php @@ -268,7 +268,6 @@ class ViewTest extends UnitTestCase { ob_start(); $View->render('index'); $result = ob_get_clean(); - //pr($result); $expected = ' diff --git a/cake/tests/fixtures/join_thing_fixture.php b/cake/tests/fixtures/join_thing_fixture.php index ca4886f34..12cbfe11e 100644 --- a/cake/tests/fixtures/join_thing_fixture.php +++ b/cake/tests/fixtures/join_thing_fixture.php @@ -35,7 +35,7 @@ class JoinThingFixture extends CakeTestFixture { var $name = 'JoinThing'; var $fields = array( - 'id' => array('type' => 'integer', 'key' => 'primary'), + 'id' => array('type' => 'integer', 'key' => 'primary', 'extra'=> 'auto_increment'), 'something_id' => array('type' => 'integer', 'length' => 10, 'null' => true), 'something_else_id' => array('type' => 'integer', 'default' => ''), 'doomed' => array('type' => 'boolean'),