mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge pull request #3462 from tractorcow/pulls/3.2/fix-versioned
BUG Fix versioned failing to generate new versions
This commit is contained in:
commit
bbd4e8b8c1
@ -559,73 +559,100 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
|||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function augmentWrite(&$manipulation) {
|
/**
|
||||||
$tables = array_keys($manipulation);
|
* Generates a ($table)_version DB manipulation and injects it into the current $manipulation
|
||||||
$version_table = array();
|
*
|
||||||
foreach($tables as $table) {
|
* @param array $manipulation Source manipulation data
|
||||||
|
* @param string $table Name of table
|
||||||
|
* @param int $recordID ID of record to version
|
||||||
|
*/
|
||||||
|
protected function augmentWriteVersioned(&$manipulation, $table, $recordID) {
|
||||||
$baseDataClass = ClassInfo::baseDataClass($table);
|
$baseDataClass = ClassInfo::baseDataClass($table);
|
||||||
|
|
||||||
$isRootClass = ($table == $baseDataClass);
|
// Set up a new entry in (table)_versions
|
||||||
|
$newManipulation = array(
|
||||||
|
"command" => "insert",
|
||||||
|
"fields" => isset($manipulation[$table]['fields']) ? $manipulation[$table]['fields'] : null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add any extra, unchanged fields to the version record.
|
||||||
|
$data = DB::prepared_query("SELECT * FROM \"$table\" WHERE \"ID\" = ?", array($recordID))->record();
|
||||||
|
if($data) foreach($data as $k => $v) {
|
||||||
|
if (!isset($newManipulation['fields'][$k])) {
|
||||||
|
$newManipulation['fields'][$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the ID is instead written to the RecordID field
|
||||||
|
$newManipulation['fields']['RecordID'] = $recordID;
|
||||||
|
unset($newManipulation['fields']['ID']);
|
||||||
|
|
||||||
|
// Generate next version ID to use
|
||||||
|
$nextVersion = 0;
|
||||||
|
if($recordID) {
|
||||||
|
$nextVersion = DB::prepared_query("SELECT MAX(\"Version\") + 1
|
||||||
|
FROM \"{$baseDataClass}_versions\" WHERE \"RecordID\" = ?",
|
||||||
|
array($recordID)
|
||||||
|
)->value();
|
||||||
|
}
|
||||||
|
$nextVersion = $nextVersion ?: 1;
|
||||||
|
|
||||||
|
// Add the version number to this data
|
||||||
|
$manipulation[$table]['fields']['Version'] = $nextVersion;
|
||||||
|
$newManipulation['fields']['Version'] = $nextVersion;
|
||||||
|
|
||||||
|
// Write AuthorID for baseclass
|
||||||
|
if($table === $baseDataClass) {
|
||||||
|
$userID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
|
||||||
|
$newManipulation['fields']['AuthorID'] = $userID;
|
||||||
|
}
|
||||||
|
$manipulation["{$table}_versions"] = $newManipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite the given manipulation to update the selected (non-default) stage
|
||||||
|
*
|
||||||
|
* @param array $manipulation Source manipulation data
|
||||||
|
* @param string $table Name of table
|
||||||
|
* @param int $recordID ID of record to version
|
||||||
|
*/
|
||||||
|
protected function augmentWriteStaged(&$manipulation, $table, $recordID) {
|
||||||
|
// If the record has already been inserted in the (table), get rid of it.
|
||||||
|
if($manipulation[$table]['command'] == 'insert') {
|
||||||
|
DB::prepared_query(
|
||||||
|
"DELETE FROM \"{$table}\" WHERE \"ID\" = ?",
|
||||||
|
array($recordID)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newTable = $table . '_' . Versioned::current_stage();
|
||||||
|
$manipulation[$newTable] = $manipulation[$table];
|
||||||
|
unset($manipulation[$table]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function augmentWrite(&$manipulation) {
|
||||||
|
$tables = array_keys($manipulation);
|
||||||
|
foreach($tables as $table) {
|
||||||
|
|
||||||
// Make sure that the augmented write is being applied to a table that can be versioned
|
// Make sure that the augmented write is being applied to a table that can be versioned
|
||||||
if( !$this->canBeVersioned($table) ) {
|
if( !$this->canBeVersioned($table) ) {
|
||||||
unset($manipulation[$table]);
|
unset($manipulation[$table]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$id = $manipulation[$table]['id'] ? $manipulation[$table]['id'] : $manipulation[$table]['fields']['ID'];;
|
|
||||||
|
// Get ID field
|
||||||
|
$id = $manipulation[$table]['id'] ? $manipulation[$table]['id'] : $manipulation[$table]['fields']['ID'];
|
||||||
if(!$id) user_error("Couldn't find ID in " . var_export($manipulation[$table], true), E_USER_ERROR);
|
if(!$id) user_error("Couldn't find ID in " . var_export($manipulation[$table], true), E_USER_ERROR);
|
||||||
|
|
||||||
$rid = isset($manipulation[$table]['RecordID']) ? $manipulation[$table]['RecordID'] : $id;
|
|
||||||
|
|
||||||
$newManipulation = array(
|
|
||||||
"command" => "insert",
|
|
||||||
"fields" => isset($manipulation[$table]['fields']) ? $manipulation[$table]['fields'] : null
|
|
||||||
);
|
|
||||||
|
|
||||||
if($this->migratingVersion) {
|
if($this->migratingVersion) {
|
||||||
$manipulation[$table]['fields']['Version'] = $this->migratingVersion;
|
$manipulation[$table]['fields']['Version'] = $this->migratingVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we haven't got a version #, then we're creating a new version.
|
// If we haven't got a version #, then we're creating a new version.
|
||||||
// Otherwise, we're just copying a version to another table
|
// Otherwise, we're just copying a version to another table
|
||||||
if(!isset($manipulation[$table]['fields']['Version'])) {
|
if(empty($manipulation[$table]['fields']['Version'])) {
|
||||||
// Add any extra, unchanged fields to the version record.
|
$this->augmentWriteVersioned($manipulation, $table, $id);
|
||||||
$data = DB::prepared_query("SELECT * FROM \"$table\" WHERE \"ID\" = ?", array($id))->record();
|
|
||||||
if($data) foreach($data as $k => $v) {
|
|
||||||
if (!isset($newManipulation['fields'][$k])) {
|
|
||||||
$newManipulation['fields'][$k] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up a new entry in (table)_versions
|
|
||||||
$newManipulation['fields']['RecordID'] = $rid;
|
|
||||||
unset($newManipulation['fields']['ID']);
|
|
||||||
|
|
||||||
// Create a new version #
|
|
||||||
if (isset($version_table[$table])) $nextVersion = $version_table[$table];
|
|
||||||
else unset($nextVersion);
|
|
||||||
|
|
||||||
if($rid && !isset($nextVersion)) {
|
|
||||||
$nextVersion = DB::prepared_query("SELECT MAX(\"Version\") + 1
|
|
||||||
FROM \"{$baseDataClass}_versions\" WHERE \"RecordID\" = ?",
|
|
||||||
array($rid)
|
|
||||||
)->value();
|
|
||||||
}
|
|
||||||
|
|
||||||
$newManipulation['fields']['Version'] = $nextVersion ? $nextVersion : 1;
|
|
||||||
|
|
||||||
if($isRootClass) {
|
|
||||||
$userID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
|
|
||||||
$newManipulation['fields']['AuthorID'] = $userID;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$manipulation["{$table}_versions"] = $newManipulation;
|
|
||||||
|
|
||||||
// Add the version number to this data
|
|
||||||
$manipulation[$table]['fields']['Version'] = $newManipulation['fields']['Version'];
|
|
||||||
$version_table[$table] = $nextVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Putting a Version of -1 is a signal to leave the version table alone, despite their being no version
|
// Putting a Version of -1 is a signal to leave the version table alone, despite their being no version
|
||||||
@ -633,6 +660,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
|||||||
unset($manipulation[$table]['fields']['Version']);
|
unset($manipulation[$table]['fields']['Version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For base classes of versioned data objects
|
||||||
if(!$this->hasVersionField($table)) unset($manipulation[$table]['fields']['Version']);
|
if(!$this->hasVersionField($table)) unset($manipulation[$table]['fields']['Version']);
|
||||||
|
|
||||||
// Grab a version number - it should be the same across all tables.
|
// Grab a version number - it should be the same across all tables.
|
||||||
@ -646,17 +674,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
|||||||
&& Versioned::current_stage() != $this->defaultStage
|
&& Versioned::current_stage() != $this->defaultStage
|
||||||
&& in_array(Versioned::current_stage(), $this->stages)
|
&& in_array(Versioned::current_stage(), $this->stages)
|
||||||
) {
|
) {
|
||||||
// If the record has already been inserted in the (table), get rid of it.
|
$this->augmentWriteStaged($manipulation, $table, $id);
|
||||||
if($manipulation[$table]['command']=='insert') {
|
|
||||||
DB::prepared_query(
|
|
||||||
"DELETE FROM \"{$table}\" WHERE \"ID\" = ?",
|
|
||||||
array($id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$newTable = $table . '_' . Versioned::current_stage();
|
|
||||||
$manipulation[$newTable] = $manipulation[$table];
|
|
||||||
unset($manipulation[$table]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ class VersionedTest extends SapphireTest {
|
|||||||
protected $extraDataObjects = array(
|
protected $extraDataObjects = array(
|
||||||
'VersionedTest_DataObject',
|
'VersionedTest_DataObject',
|
||||||
'VersionedTest_Subclass',
|
'VersionedTest_Subclass',
|
||||||
|
'VersionedTest_AnotherSubclass',
|
||||||
'VersionedTest_RelatedWithoutVersion',
|
'VersionedTest_RelatedWithoutVersion',
|
||||||
'VersionedTest_SingleStage',
|
'VersionedTest_SingleStage',
|
||||||
'VersionedTest_WithIndexes',
|
'VersionedTest_WithIndexes',
|
||||||
@ -627,6 +628,98 @@ class VersionedTest extends SapphireTest {
|
|||||||
$this->assertEquals('Stage.Live', Versioned::get_reading_mode());
|
$this->assertEquals('Stage.Live', Versioned::get_reading_mode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the latest version of a record is the expected value
|
||||||
|
*
|
||||||
|
* @param type $record
|
||||||
|
* @param type $version
|
||||||
|
*/
|
||||||
|
protected function assertRecordHasLatestVersion($record, $version) {
|
||||||
|
foreach(ClassInfo::ancestry(get_class($record), true) as $table) {
|
||||||
|
$versionForClass = DB::prepared_query(
|
||||||
|
$sql = "SELECT MAX(\"Version\") FROM \"{$table}_versions\" WHERE \"RecordID\" = ?",
|
||||||
|
array($record->ID)
|
||||||
|
)->value();
|
||||||
|
$this->assertEquals($version, $versionForClass, "That the table $table has the latest version $version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that multi-table dataobjects are correctly versioned
|
||||||
|
*/
|
||||||
|
public function testWriteToStage() {
|
||||||
|
// Test subclass with versioned extension directly added
|
||||||
|
$record = VersionedTest_Subclass::create();
|
||||||
|
$record->Title = "Test A";
|
||||||
|
$record->ExtraField = "Test A";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->Title = "Test A2";
|
||||||
|
$record->ExtraField = "Test A2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
|
||||||
|
// Test subclass without changes to base class
|
||||||
|
$record = VersionedTest_Subclass::create();
|
||||||
|
$record->ExtraField = "Test B";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->ExtraField = "Test B2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
|
||||||
|
// Test subclass without changes to sub class
|
||||||
|
$record = VersionedTest_Subclass::create();
|
||||||
|
$record->Title = "Test C";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->Title = "Test C2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
|
||||||
|
// Test subclass with versioned extension only added to the base clases
|
||||||
|
$record = VersionedTest_AnotherSubclass::create();
|
||||||
|
$record->Title = "Test A";
|
||||||
|
$record->AnotherField = "Test A";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->Title = "Test A2";
|
||||||
|
$record->AnotherField = "Test A2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
|
||||||
|
|
||||||
|
// Test subclass without changes to base class
|
||||||
|
$record = VersionedTest_AnotherSubclass::create();
|
||||||
|
$record->AnotherField = "Test B";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->AnotherField = "Test B2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
|
||||||
|
// Test subclass without changes to sub class
|
||||||
|
$record = VersionedTest_AnotherSubclass::create();
|
||||||
|
$record->Title = "Test C";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 1);
|
||||||
|
$record->Title = "Test C2";
|
||||||
|
$record->writeToStage("Stage");
|
||||||
|
$this->assertRecordHasLatestVersion($record, 2);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -701,6 +794,16 @@ class VersionedTest_Subclass extends VersionedTest_DataObject implements TestOnl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
|
class VersionedTest_AnotherSubclass extends VersionedTest_DataObject implements TestOnly {
|
||||||
|
private static $db = array(
|
||||||
|
"AnotherField" => "Varchar"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage tests
|
* @subpackage tests
|
||||||
|
Loading…
x
Reference in New Issue
Block a user