ENHANCEMENT: DataObject and SiteTree duplicate now duplicate relations (except for has_many relations, as the object at the other end of such a relation has an existing relation to the original object and that should not be modified by the duplication)

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@110845 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Julian Seidenberg 2010-09-16 03:40:34 +00:00
parent 94c93e64e7
commit ed666b71b7
3 changed files with 150 additions and 2 deletions

View File

@ -379,7 +379,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/**
* Create a duplicate of this node.
* Caution: Doesn't duplicate relations.
* Note: now also duplicates relations.
*
* @param $doWrite Perform a write() operation before returning the object. If this is true, it will create the duplicate in the database.
* @return DataObject A duplicate of this node. The exact type will be the type of this node.
@ -388,10 +388,55 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$className = $this->class;
$clone = new $className( $this->record );
$clone->ID = 0;
if($doWrite) $clone->write();
if($doWrite) {
$clone->write();
$this->duplicateManyManyRelations($this, $clone);
}
return $clone;
}
/**
* Copies the many_many and belongs_many_many relations from one object to another instance of the name of object
* The destinationObject must be written to the database already and have an ID. Writing is performed automatically when adding the new relations.
* @return DataObject with the new many_many relations copied in */
protected function duplicateManyManyRelations($sourceObject, $destinationObject) {
if (!$destinationObject || $destinationObject->ID < 1) user_error("Can't duplicate relations for an object that has not been written to the database", E_USER_ERROR);
//duplicate complex relations
// DO NOT copy has_many relations, because copying the relation would result in us changing the has_one relation
// on the other side of this relation to point at the copy and no longer the original (being a has_one, it can
// only point at one thing at a time). So, all relations except has_many can and are copied
if ($sourceObject::$has_one) foreach($sourceObject::$has_one as $name => $type) {
$this->duplicateRelations($sourceObject, $destinationObject, $name);
}
if ($sourceObject::$many_many) foreach($sourceObject::$many_many as $name => $type) {
$this->duplicateRelations($sourceObject, $destinationObject, $name);
}
if ($sourceObject::$belongs_many_many) foreach($sourceObject::$belongs_many_many as $name => $type) {
$this->duplicateRelations($sourceObject, $destinationObject, $name);
}
return $destinationObject;
}
/** Helper function to duplicate relations from one object to another */
private function duplicateRelations($sourceObject, $destinationObject, $name) {
$relations = $sourceObject->$name();
if ($relations) {
if ($relations instanceOf ComponentSet) { //many-to-something relation
if ($relations->Count() > 0) { //with more than one thing it is related to
foreach($relations as $relation) {
$destinationObject->$name()->add($relation);
}
}
} else { //one-to-one relation
$destinationObject->$name = $relations;
}
}
}
/**
* Set the ClassName attribute. {@link $class} is also updated.
* Warning: This will produce an inconsistent record, as the object

View File

@ -538,6 +538,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if($doWrite) {
$page->write();
$page = $this->duplicateManyManyRelations($this, $page);
}
$this->extend('onAfterDuplicate', $page);

View File

@ -0,0 +1,101 @@
<?php
class DataObjectDuplicationTest extends SapphireTest {
function testDuplicateManyManyClasses() {
//create new test classes below
$one = new DataObjectDuplicateTestClass1();
$two = new DataObjectDuplicateTestClass2();
$three = new DataObjectDuplicateTestClass3();
//set some simple fields
$text1 = "Test Text 1";
$text2 = "Test Text 2";
$text3 = "Test Text 3";
$one->text = $text1;
$two->text = $text2;
$three->text = $text3;
//write the to DB
$one->write();
$two->write();
$three->write();
//create relations
$one->twos()->add($two);
$one->threes()->add($three);
$one = DataObject::get_by_id("DataObjectDuplicateTestClass1", $one->ID);
$two = DataObject::get_by_id("DataObjectDuplicateTestClass2", $two->ID);
$three = DataObject::get_by_id("DataObjectDuplicateTestClass3", $three->ID);
//test duplication
$oneCopy = $one->duplicate();
$twoCopy = $two->duplicate();
$threeCopy = $three->duplicate();
$oneCopy = DataObject::get_by_id("DataObjectDuplicateTestClass1", $oneCopy->ID);
$twoCopy = DataObject::get_by_id("DataObjectDuplicateTestClass2", $twoCopy->ID);
$threeCopy = DataObject::get_by_id("DataObjectDuplicateTestClass3", $threeCopy->ID);
$this->assertNotNull($oneCopy, "Copy of 1 exists");
$this->assertNotNull($twoCopy, "Copy of 2 exists");
$this->assertNotNull($threeCopy, "Copy of 3 exists");
$this->assertEquals($text1, $oneCopy->text);
$this->assertEquals($text2, $twoCopy->text);
$this->assertEquals($text3, $threeCopy->text);
$this->assertNotEquals($one->twos()->Count(), $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)");
$this->assertEquals($one->threes()->Count(), $oneCopy->threes()->Count(), "Object has the correct number of relations");
$this->assertEquals($three->ones()->Count(), $threeCopy->ones()->Count(), "Object has the correct number of relations");
$this->assertEquals($one->ID, $twoCopy->one()->ID, "Match between relation of copy and the original");
$this->assertEquals(0, $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)");
$this->assertEquals($three->ID, $oneCopy->threes()->First()->ID, "Match between relation of copy and the original");
$this->assertEquals($one->ID, $threeCopy->ones()->First()->ID, "Match between relation of copy and the original");
}
}
class DataObjectDuplicateTestClass1 extends SiteTree {
static $db = array(
'text' => 'Varchar'
);
static $has_many = array(
'twos' => 'DataObjectDuplicateTestClass2'
);
static $many_many = array(
'threes' => 'DataObjectDuplicateTestClass3'
);
}
class DataObjectDuplicateTestClass2 extends SiteTree {
static $db = array(
'text' => 'Varchar'
);
static $has_one = array(
'one' => 'DataObjectDuplicateTestClass1'
);
}
class DataObjectDuplicateTestClass3 extends SiteTree {
static $db = array(
'text' => 'Varchar'
);
static $belongs_many_many = array(
'ones' => 'DataObjectDuplicateTestClass1'
);
}
?>