From 7f871fa18b20f697d53dbb14f871abacdb979181 Mon Sep 17 00:00:00 2001 From: Marcus Dalgren Date: Thu, 9 May 2013 01:45:25 +0200 Subject: [PATCH] Fix orphaned records when running update When DataObject::update() is run with relation fields and the relationship is new the relationship ID was not set on the DataObject. This patch fixes this. Fixes issue 6195 in open.silverstripe.org. --- model/DataObject.php | 11 +++++++++-- tests/model/DataObjectTest.php | 27 +++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/model/DataObject.php b/model/DataObject.php index b8f26ea6d..20a20a4fa 100644 --- a/model/DataObject.php +++ b/model/DataObject.php @@ -772,10 +772,15 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity // no support for has_many or many_many relationships, // as the updater wouldn't know which object to write to (or create) if($relObj->$relation() instanceof DataObject) { + $parentObj = $relObj; $relObj = $relObj->$relation(); - // If the intermediate relationship objects have been created, then write them - if($iID) $relObj->write(); + if($iID || (!$relObj->ID && $parentObj != $this)) { + $relObj->write(); + $relatedFieldName = $relation."ID"; + $parentObj->$relatedFieldName = $relObj->ID; + $parentObj->write(); + } } else { user_error( "DataObject::update(): Can't traverse relationship '$relation'," . @@ -791,6 +796,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity if($relObj) { $relObj->$fieldName = $v; $relObj->write(); + $relatedFieldName = $relation."ID"; + $this->$relatedFieldName = $relObj->ID; $relObj->flushCache(); } else { user_error("Couldn't follow dot syntax '$k' on '$this->class' object", E_USER_WARNING); diff --git a/tests/model/DataObjectTest.php b/tests/model/DataObjectTest.php index aa8a45782..261aa696c 100644 --- a/tests/model/DataObjectTest.php +++ b/tests/model/DataObjectTest.php @@ -619,14 +619,14 @@ class DataObjectTest extends SapphireTest { * objects */ $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); $team1->CaptainID = $this->idFromFixture('DataObjectTest_Player', 'captain1'); - + $team1->update(array( 'DatabaseField' => 'Something', 'Captain.FirstName' => 'Jim', 'Captain.Email' => 'jim@example.com', 'Captain.FavouriteTeam.Title' => 'New and improved team 1', )); - + /* Test the simple case of updating fields on the object itself */ $this->assertEquals('Something', $team1->DatabaseField); @@ -642,6 +642,29 @@ class DataObjectTest extends SapphireTest { $this->assertEquals('New and improved team 1', $reloadedTeam1->Title); } + public function testDataObjectUpdateNew() { + /* update() calls can use the dot syntax to reference has_one relations and other methods that return + * objects */ + $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); + $team1->CaptainID = 0; + + $team1->update(array( + 'Captain.FirstName' => 'Jim', + 'Captain.FavouriteTeam.Title' => 'New and improved team 1', + )); + /* Test that the captain ID has been updated */ + $this->assertGreaterThan(0, $team1->CaptainID); + + /* Fetch the newly created captain */ + $captain1 = DataObjectTest_Player::get()->byID($team1->CaptainID); + $this->assertEquals('Jim', $captain1->FirstName); + + /* Grab the favourite team and make sure it has the correct values */ + $reloadedTeam1 = $captain1->FavouriteTeam(); + $this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID); + $this->assertEquals('New and improved team 1', $reloadedTeam1->Title); + } + public function testWritingInvalidDataObjectThrowsException() { $validatedObject = new DataObjectTest_ValidatedObject();