ENHANCEMENT: Added support for dot syntax to DataObject::update()

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63493 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2008-10-02 00:45:13 +00:00
parent c5ed0c3c2e
commit cba8e71170
3 changed files with 79 additions and 10 deletions

View File

@ -392,18 +392,41 @@ class DataObject extends ViewableData implements DataObjectInterface {
}
/**
* Pass a number of field changes in a map.
* Doesn't write to the database. To write the data,
* use the write() method.
* Update a number of fields on this object, given a map of the desired changes.
*
* The field names can be simple names, or you can use a dot syntax to access relations.
* For example, array("Author.FirstName" => "Jim") will set $this->Author()->FirstName to "Jim".
*
* update() doesn't write the main object, but if you use the dot syntax, it will write()
* the related objects that it alters.
*
* @param array $data A map of field name to data values to update.
*/
public function update($data) {
foreach($data as $k => $v) {
$this->$k = $v;
// Implement dot syntax for updates
if(strpos($k,'.') !== false) {
$relations = explode('.', $k);
$fieldName = array_pop($relations);
$relObj = $this;
foreach($relations as $i=>$relation) {
$relObj = $relObj->$relation();
// If the intermediate relationship objects have been created, then write them
if($i<sizeof($relation)-1 && !$relObj->ID) $relObj->write();
}
if($relObj) {
$relObj->$fieldName = $v;
$relObj->write();
$relObj->flushCache();
} else {
user_error("Couldn't follow dot syntax '$k' on '$this->class' object", E_USER_WARNING);
}
} else {
$this->$k = $v;
}
}
}
/**
* Pass changes as a map, and try to
* get automatic casting for these fields.
@ -1644,7 +1667,6 @@ class DataObject extends ViewableData implements DataObjectInterface {
if(!isset($this->record[$fieldName]) || $this->record[$fieldName] !== $val) {
// TODO Add check for php-level defaults which are not set in the db
// TODO Add check for hidden input-fields (readonly) which are not set in the db
if(
// Main non type-based check
(isset($this->record[$fieldName]) && $this->record[$fieldName] != $val)

View File

@ -289,6 +289,17 @@ class DataObjectTest extends SapphireTest {
$this->assertEquals(0, DB::query("SELECT CaptainID FROM DataObjectTest_Team WHERE ID = $existingTeam->ID")->value());
}
function testCanAccessHasOneObjectsAsMethods() {
/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the object itself should
* be accessible as $obj->Captain() */
$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
$team->CaptainID = $captainID;
$this->assertNotNull($team->Captain());
$this->assertEquals($captainID, $team->Captain()->ID);
}
function testFieldNamesThatMatchMethodNamesWork() {
/* Check that a field name that corresponds to a method on DataObject will still work */
$obj = new DataObjectTest_FunnyFieldNames();
@ -428,13 +439,42 @@ class DataObjectTest extends SapphireTest {
'databaseFields() on subclass contains only fields defined on instance'
);
}
function testDataObjectUpdate() {
/* 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 = $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);
/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in the database. Although update()
* doesn't usually write, it does write related records automatically. */
$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
$this->assertEquals('Jim', $captain1->FirstName);
$this->assertEquals('jim@example.com', $captain1->Email);
/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.FavouriteTeam.Title made */
$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
}
}
class DataObjectTest_Player extends Member implements TestOnly {
static $belongs_many_many = array(
'Teams' => 'DataObjectTest_Team'
);
static $has_one = array(
'FavouriteTeam' => 'DataObjectTest_Team',
);
static $belongs_many_many = array(
'Teams' => 'DataObjectTest_Team'
);
}

View File

@ -42,6 +42,13 @@ DataObjectTest_Team:
Title: Team 2
DataObjectTest_Player:
captain1:
FirstName: Captain 1
FavouriteTeam: =>DataObjectTest_Team.team1
Teams: =>DataObjectTest_Team.team1
captain2:
FirstName: Captain 2
Teams: =>DataObjectTest_Team.team2
player1:
FirstName: Player 1
player2: