Added support for updating stages on versioned extensions managed through GridFieldSortableRows (ref #77)

This commit is contained in:
Ed Chipman 2016-02-06 17:00:23 -04:00
parent c9ab0909f8
commit 10a52463f4
6 changed files with 311 additions and 37 deletions

View File

@ -48,12 +48,20 @@ To move an item to another page drag the row over the respective move to page bu
GridFieldSortableRows provides 4 "events" onBeforeGridFieldRowSort(), onAfterGridFieldRowSort(), onBeforeGridFieldPageSort() and onAfterGridFieldPageSort(). These "events" are passed a clone of the DataList used in GridFieldSortableRows, in the case of page sorting this list has a limit that shows you the current page plus/minus one object. For GridFieldSortableRows that are on ModelAdmin decendents these events are called on the ModelAdmin if they do not have a owner DataObject, if you are using GridFieldSortableRows on a GridField for a DataObject's relationship the events are called on that DataObject.
#### Appending to the top instead of the bottom
By default GridFieldSortable rows appends to the bottom of the list for performance on large data sets, however you can set new records to append new records to the top by calling setAppendToTop(true) on your GridFieldSortableRows instance.
By default GridFieldSortableRows appends to the bottom of the list for performance on large data sets, however you can set new records to append new records to the top by calling setAppendToTop(true) on your GridFieldSortableRows instance.
```php
$myGridConfig->addComponent($sortable=new GridFieldSortableRows('SortOrder'));
$sortable->setAppendToTop(true);
```
#### Working with versioned records
By default GridFieldSortableRows does not update any other stage for versioned than the base stage. However you can enable this by calling setUpdateVersionedStage() and passing in the name of the stage you want to update along with the base stage. For example passing in "Live" will also update the "Live" stage when any sort happens.
```php
$myGridConfig->addComponent($sortable=new GridFieldSortableRows('SortOrder'));
$sortable->setUpdateVersionedStage('Live');
```
## Migrating from SilverStripe 2.4 and Data Object Manager's SortableDataObject
SortableGridField is not the same as SortableDataObject, since it is only a component of GridField it does not have the ability to catch the object when it is saved for the first time. So SortableGridField uses 1 as the first sort index because 0 is the default for an integer field/column in the database. For migrations from 2.4 with SortableDataObject you need to setup your DataObject based on the instructions above however you must name your sort column "SortOrder" to maintain your sort indexes defined by SortableDataObject. Then you need to run the following query on the table containing your sort field, for many_many relationships this will be something like {RelationshipClass}_{RelationshipName}. This query will maintain your sort order from SortableDataObject but increment the index by 1 giving it a starting number of 1.

View File

@ -8,14 +8,17 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
protected $sortColumn;
protected $disable_selection=true;
protected $append_to_top=false;
protected $update_versioned_stage=null;
/**
* @param String $sortColumn Column that should be used to update the sort information
* @param bool $disableSelection
* @param string $sortColumn Column that should be used to update the sort information
* @param bool $disableSelection Disable selection on the GridField when dragging
* @param string $updateVersionStage Name of the versioned stage to update this disabled by default unless this is set
*/
public function __construct($sortColumn, $disableSelection = true) {
public function __construct($sortColumn, $disableSelection = true, $updateVersionStage = null) {
$this->sortColumn = $sortColumn;
$this->disable_selection = $disableSelection;
$this->update_versioned_stage = $updateVersionStage;
}
/**
@ -142,6 +145,15 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
$this->disable_selection = $value;
return $this;
}
/**
* Sets the suffix of the versioned stage that should be updated along side the default stage
* @param string $value Versioned Stage to update this is disabled by default unless this is set
* @return GridFieldSortableRows Returns the current instance
*/
public function setUpdateVersionedStage($value) {
$this->update_versioned_stage=$value;
return $this;
}
/**
* Detects and corrects items with a sort column value of 0, by appending them to the bottom of the list
@ -256,7 +268,19 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
//LastEdited
DB::query('UPDATE "' . $baseDataClass
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE '.($list instanceof RelationList ? '"' . $list->foreignKey . '" = '. $owner->ID:$idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\''.implode('\',\'', $topIncremented).'\')':''));
. ' WHERE '.($list instanceof RelationList ? '"' . $list->foreignKey . '" = '. $owner->ID:$idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\''.implode('\',\'', $topIncremented).'\')':''));
if($this->update_versioned_stage && class_exists($table) && Object::has_extension($table, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = "' . $sortColumn .'"+1'
. ' WHERE "ID" = '. ($list instanceof RelationList ? '"' . $list->foreignKey . '" = '. $owner->ID:$idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\''.implode('\',\'', $topIncremented).'\')':''));
if(Object::has_extension($baseDataClass, 'Versioned')) {
DB::query('UPDATE "' . $baseDataClass . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE ' . ($list instanceof RelationList ? '"' . $list->foreignKey . '" = '. $owner->ID:$idCondition) . (!empty($topIncremented) ? ' AND "ID" NOT IN(\''.implode('\',\'', $topIncremented).'\')':''));
}
}
$topIncremented[]=$obj->ID;
}else {
@ -268,6 +292,18 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
DB::query('UPDATE "' . $baseDataClass
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = '. $obj->ID);
if($this->update_versioned_stage && class_exists($table) && Object::has_extension($table, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . ($max + $i)
. ' WHERE "ID" = '. $obj->ID);
if(Object::has_extension($baseDataClass, 'Versioned')) {
DB::query('UPDATE "' . $baseDataClass . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = '. $obj->ID);
}
}
}
$i++;
@ -410,6 +446,18 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
DB::query('UPDATE "' . $baseDataClass
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = '. $id);
if($this->update_versioned_stage && class_exists($table) && Object::has_extension($table, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn . '" = ' . (($sort + 1) + $pageOffset)
. ' WHERE "ID" = '. $id);
if(Object::has_extension($baseDataClass, 'Versioned')) {
DB::query('UPDATE "' . $baseDataClass . '_' . $this->update_versioned_stage
. '" SET "LastEdited" = \'' . date('Y-m-d H:i:s') . '\''
. ' WHERE "ID" = '. $id);
}
}
}
}
@ -491,6 +539,25 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
}
//Find the sort column
if($this->update_versioned_stage && Object::has_extension($className, 'Versioned')) {
$table=false;
$classes=ClassInfo::ancestry($className, true);
foreach($classes as $class) {
$db = Config::inst()->get($className, "db", CONFIG::UNINHERITED);
if(!empty($db) && array_key_exists($sortColumn, $db)) {
$table=$class;
break;
}
}
if($table===false) {
user_error('Sort column '.$this->sortColumn.' could not be found in '.$gridField->getModelClass().'\'s ancestry', E_USER_ERROR);
exit;
}
}
//Start transaction if supported
if(DB::getConn()->supportsTransactions()) {
DB::getConn()->transactionStart();
@ -504,6 +571,12 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
} else {
$targetItem->$sortColumn = $sortPositions[0];
$targetItem->write();
if($this->update_versioned_stage && Object::has_extension($className, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn.'" = ' . $sortPositions[0]
. ' WHERE "ID" = ' . $targetItem->ID);
}
}
@ -521,6 +594,12 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
if($this->update_versioned_stage && Object::has_extension($className, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn.'" = ' . $sortPositions[$i]
. ' WHERE "ID" = ' . $obj->ID);
}
}
$i++;
@ -533,6 +612,12 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
} else {
$targetItem->$sortColumn = $sortPositions[count($sortPositions) - 1];
$targetItem->write();
if($this->update_versioned_stage && Object::has_extension($className, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn.'" = ' . $sortPositions[count($sortPositions) - 1]
. ' WHERE "ID" = ' . $targetItem->ID);
}
}
@ -550,6 +635,12 @@ class GridFieldSortableRows implements GridField_HTMLProvider, GridField_ActionP
} else {
$obj->$sortColumn = $sortPositions[$i];
$obj->write();
if($this->update_versioned_stage && Object::has_extension($className, 'Versioned')) {
DB::query('UPDATE "' . $table . '_' . $this->update_versioned_stage
. '" SET "' . $sortColumn.'" = ' . $sortPositions[$i]
. ' WHERE "ID" = ' . $obj->ID);
}
}
$i++;

View File

@ -1,68 +1,162 @@
<?php
class GridFieldSortableRowsAutoSortTest extends SapphireTest {
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'GridFieldSortableRowsAutoSortTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_SortOrder_Player');
public function setUp() {
parent::setUp();
$this->list = GridFieldAction_SortOrder_Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
protected $extraDataObjects = array('GridFieldAction_SortOrder_Player', 'GridFieldAction_SortOrder_VPlayer');
public function testAutoSort() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$list = GridFieldAction_SortOrder_Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($gridField)), new FieldList());
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'sortableRowsToggle', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$gridField->gridFieldAlterAction(array('StateID'=>$stateID), $form, $request);
//Insure sort ran
$this->assertEquals(3, $this->list->last()->SortOrder, 'Auto sort should have run');
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run');
//Check for duplicates (there shouldn't be any)
$count=$this->list->Count();
$indexes=count(array_unique($this->list->column('SortOrder')));
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected');
}
public function testAppendToTopAutoSort() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$this->gridField->getConfig()->getComponentByType('GridFieldSortableRows')->setAppendToTop(true);
$list = GridFieldAction_SortOrder_Player::get();
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($gridField)), new FieldList());
$this->assertEquals(0, $this->list->last()->SortOrder, 'Auto sort should not have run');
$gridField->getConfig()->getComponentByType('GridFieldSortableRows')->setAppendToTop(true);
$this->assertEquals(0, $list->last()->SortOrder, 'Auto sort should not have run');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'sortableRowsToggle', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$gridField->gridFieldAlterAction(array('StateID'=>$stateID), $form, $request);
//Insure sort ran
$this->assertEquals(3, $this->list->last()->SortOrder, 'Auto sort should have run');
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run');
//Check for duplicates (there shouldn't be any)
$count=$this->list->Count();
$indexes=count(array_unique($this->list->column('SortOrder')));
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected');
}
public function testAutoSortVersioned() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
//Force versioned to reset
Versioned::reset();
$list = GridFieldAction_SortOrder_VPlayer::get();
//Publish all records
foreach($list as $item) {
$item->publish('Stage', 'Live');
}
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder', true, 'Live'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($gridField)), new FieldList());
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'sortableRowsToggle', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$gridField->gridFieldAlterAction(array('StateID'=>$stateID), $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Stage"');
//Check for duplicates (there shouldn't be any)
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected on Versioned stage "Stage"');
//Force versioned over to Live stage
Versioned::reading_stage('Live');
//Get live instance
$obj=Versioned::get_one_by_stage('GridFieldAction_SortOrder_VPlayer', 'Live', 'ID='.$list->last()->ID);
//Insure sort ran
$this->assertEquals(3, $obj->SortOrder, 'Auto sort should have run on Versioned stage "Live"');
//Check for duplicates (there shouldn't be any)
$list=Versioned::get_by_stage('GridFieldAction_SortOrder_VPlayer', 'Live');
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected on Versioned stage "Live"');
}
public function testAppendToTopAutoSortVersioned() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
//Force versioned to reset
Versioned::reset();
$list = GridFieldAction_SortOrder_VPlayer::get();
//Publish all records
foreach($list as $item) {
$item->publish('Stage', 'Live');
}
$config = GridFieldConfig::create()->addComponent(new GridFieldSortableRows('SortOrder', true, 'Live'));
$gridField = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($gridField)), new FieldList());
$gridField->getConfig()->getComponentByType('GridFieldSortableRows')->setAppendToTop(true);
$this->assertEquals(0, $list->last()->SortOrder, 'Auto sort should not have run on Versioned stage "Stage"');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'sortableRowsToggle', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$gridField->gridFieldAlterAction(array('StateID'=>$stateID), $form, $request);
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Stage"');
//Check for duplicates (there shouldn't be any)
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected on Versioned stage "Stage"');
//Force versioned over to Live stage
Versioned::reading_stage('Live');
//Insure sort ran
$this->assertEquals(3, $list->last()->SortOrder, 'Auto sort should have run on Versioned stage "Live"');
//Check for duplicates (there shouldn't be any)
$count=$list->Count();
$indexes=count(array_unique($list->column('SortOrder')));
$this->assertEquals(0, $count-$indexes, 'Duplicate indexes detected on Versioned stage "Live"');
}
}
class GridFieldAction_SortOrder_Player extends DataObject implements TestOnly {
@ -73,4 +167,17 @@ class GridFieldAction_SortOrder_Player extends DataObject implements TestOnly {
static $default_sort='SortOrder';
}
class GridFieldAction_SortOrder_VPlayer extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar',
'SortOrder' => 'Int'
);
static $default_sort='SortOrder';
static $extensions=array(
"Versioned('Stage', 'Live')"
);
}
?>

View File

@ -7,4 +7,15 @@ GridFieldAction_SortOrder_Player:
SortOrder: 0
player3:
Name: Player 3
SortOrder: 0
SortOrder: 0
GridFieldAction_SortOrder_VPlayer:
player1:
Name: Player 1
SortOrder: 0
player2:
Name: Player 2
SortOrder: 0
player3:
Name: Player 3
SortOrder: 0

View File

@ -14,7 +14,7 @@ class GridFieldSortableRowsTest extends SapphireTest {
public static $fixture_file = 'GridFieldSortableRowsTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_SortOrder_Team');
protected $extraDataObjects = array('GridFieldAction_SortOrder_Team', 'GridFieldAction_SortOrder_VTeam');
public function setUp() {
parent::setUp();
@ -49,6 +49,35 @@ class GridFieldSortableRowsTest extends SapphireTest {
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals($team2->ID, $this->list->last()->ID, 'User should be able to sort records with ADMIN permission.');
}
public function testSortActionVersioned() {
//Force versioned to reset
Versioned::reset();
$list = GridFieldAction_SortOrder_VTeam::get();
$this->gridField->setList($list);
$this->gridField->getConfig()->getComponentByType('GridFieldSortableRows')->setUpdateVersionedStage('Live');
//Publish all records
foreach($list as $item) {
$item->publish('Stage', 'Live');
}
$team1 = $this->objFromFixture('GridFieldAction_SortOrder_VTeam', 'team1');
$team2 = $this->objFromFixture('GridFieldAction_SortOrder_VTeam', 'team2');
$team3 = $this->objFromFixture('GridFieldAction_SortOrder_VTeam', 'team3');
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'saveGridRowSort', 'args'=>array('GridFieldSortableRows'=>array('sortableToggle'=>true))));
$request = new SS_HTTPRequest('POST', 'url', array('ItemIDs'=>"$team1->ID, $team3->ID, $team2->ID"), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals($team2->ID, $list->last()->ID, 'Sort should have happened on Versioned stage "Stage"');
$list=Versioned::get_by_stage('GridFieldAction_SortOrder_VTeam', 'Live');
$this->assertEquals($team2->ID, $list->last()->ID, 'Sort should have happened on Versioned stage "Live"');
}
}
class GridFieldAction_SortOrder_Team extends DataObject implements TestOnly {
@ -60,4 +89,18 @@ class GridFieldAction_SortOrder_Team extends DataObject implements TestOnly {
static $default_sort='SortOrder';
}
class GridFieldAction_SortOrder_VTeam extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar',
'SortOrder' => 'Int'
);
static $default_sort='SortOrder';
static $extensions=array(
"Versioned('Stage', 'Live')"
);
}
?>

View File

@ -10,4 +10,18 @@ GridFieldAction_SortOrder_Team:
team3:
Name: Team 3
City: Auckland
SortOrder: 3
SortOrder: 3
GridFieldAction_SortOrder_VTeam:
team1:
Name: Team 1
City: Cologne
SortOrder: 1
team2:
Name: Team 2
City: Wellington
SortOrder: 2
team3:
Name: Team 3
City: Auckland
SortOrder: 3