mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW: GridField can sort on relation columns
Slightly improved logic Add support for relations more than one 'level' apart Add unit tests Fixing PostgreSQL support Throw exception if attempting to sort on a has_many/many_many relation
This commit is contained in:
parent
ad52cc20c1
commit
a1a402224f
@ -87,6 +87,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
$state = $gridField->State->GridFieldSortableHeader;
|
||||
$columns = $gridField->getColumns();
|
||||
$currentColumn = 0;
|
||||
$list = $gridField->getList();
|
||||
|
||||
foreach($columns as $columnField) {
|
||||
$currentColumn++;
|
||||
@ -96,7 +97,31 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
if(isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) {
|
||||
$columnField = $this->fieldSorting[$columnField];
|
||||
}
|
||||
if($title && $gridField->getList()->canSortBy($columnField)) {
|
||||
|
||||
$allowSort = ($title && $list->canSortBy($columnField));
|
||||
|
||||
if(strpos($columnField, '.') !== false) {
|
||||
// we have a relation column with dot notation
|
||||
$parts = explode('.', $columnField);
|
||||
|
||||
$tmpItem = singleton($list->dataClass());
|
||||
for($idx = 0; $idx < sizeof($parts); $idx++) {
|
||||
$methodName = $parts[$idx];
|
||||
if($tmpItem instanceof DataObject && $tmpItem->hasField($methodName)) {
|
||||
// If we've found a field, we can sort on it
|
||||
$allowSort = true;
|
||||
} else if ($tmpItem instanceof SS_List) {
|
||||
// It's impossible to sort on a HasManyList/ManyManyList
|
||||
throw new Exception(__CLASS__ . ' is unable to sort on has_many/many_many relations,'
|
||||
. ' please only specify has_one relations');
|
||||
} else {
|
||||
// Else, the part is a relation name, so get the object/list from it
|
||||
$tmpItem = $tmpItem->$methodName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($allowSort) {
|
||||
$dir = 'asc';
|
||||
if($state->SortColumn == $columnField && $state->SortDirection == 'asc') {
|
||||
$dir = 'desc';
|
||||
@ -161,6 +186,14 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the manipulated (sorted) DataList. Field names will simply add an 'ORDER BY'
|
||||
* clause, relation names will add appropriate joins to the DataQuery first.
|
||||
*
|
||||
* @param GridField
|
||||
* @param SS_List
|
||||
* @return SS_List
|
||||
*/
|
||||
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
|
||||
if(!$this->checkDataType($dataList)) return $dataList;
|
||||
|
||||
@ -168,6 +201,45 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
if ($state->SortColumn == "") {
|
||||
return $dataList;
|
||||
}
|
||||
return $dataList->sort($state->SortColumn, $state->SortDirection);
|
||||
|
||||
$column = $state->SortColumn;
|
||||
|
||||
// if we have a relation column with dot notation
|
||||
if(strpos($column, '.') !== false) {
|
||||
$lastAlias = $dataList->dataClass();
|
||||
$tmpItem = singleton($lastAlias);
|
||||
$parts = explode('.', $state->SortColumn);
|
||||
|
||||
for($idx = 0; $idx < sizeof($parts); $idx++) {
|
||||
$methodName = $parts[$idx];
|
||||
|
||||
// If we're not on the last item, we're looking at a relation
|
||||
if($idx !== sizeof($parts) - 1) {
|
||||
// Traverse to the relational list
|
||||
$tmpItem = $tmpItem->$methodName();
|
||||
|
||||
// Add a left join to the query
|
||||
$dataList = $dataList->leftJoin(
|
||||
$tmpItem->class,
|
||||
'"' . $methodName . '"."ID" = "' . $lastAlias . '"."' . $methodName . 'ID"',
|
||||
$methodName
|
||||
);
|
||||
|
||||
// Store the last 'alias' name as it'll be used for the next
|
||||
// join, or the 'sort' column
|
||||
$lastAlias = $methodName;
|
||||
} else {
|
||||
// Change relation.relation.fieldname to alias.fieldname
|
||||
$column = $lastAlias . '.' . $methodName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to manually create our ORDER BY "Foo"."Bar" string for relations,
|
||||
// as ->sort() won't do it by itself. Blame PostgreSQL for making this necessary
|
||||
$pieces = explode('.', $column);
|
||||
$column = '"' . implode('"."', $pieces) . '"';
|
||||
|
||||
return $dataList->sort($column, $state->SortDirection);
|
||||
}
|
||||
}
|
||||
|
115
tests/forms/gridfield/GridFieldSortableHeaderTest.php
Normal file
115
tests/forms/gridfield/GridFieldSortableHeaderTest.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldSortableHeaderTest extends SapphireTest {
|
||||
|
||||
protected static $fixture_file = 'GridFieldSortableHeaderTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'GridFieldSortableHeaderTest_Team',
|
||||
'GridFieldSortableHeaderTest_Cheerleader',
|
||||
'GridFieldSortableHeaderTest_CheerleaderHat'
|
||||
);
|
||||
|
||||
public function testGetManipulatedData() {
|
||||
$list = new DataList('GridFieldSortableHeaderTest_Team');
|
||||
$config = new GridFieldConfig_RecordEditor();
|
||||
$gridField = new GridField('testfield', 'testfield', $list, $config);
|
||||
|
||||
// Test normal sorting
|
||||
$state = $gridField->State->GridFieldSortableHeader;
|
||||
$state->SortColumn = 'City';
|
||||
$state->SortDirection = 'asc';
|
||||
|
||||
$compontent = $gridField->getConfig()->getComponentByType('GridFieldSortableHeader');
|
||||
$listA = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$state->SortDirection = 'desc';
|
||||
$listB = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$this->assertEquals(
|
||||
array('Auckland', 'Cologne', 'Melbourne', 'Wellington'),
|
||||
$listA->column('City')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array('Wellington', 'Melbourne', 'Cologne', 'Auckland'),
|
||||
$listB->column('City')
|
||||
);
|
||||
|
||||
// Test one relation 'deep'
|
||||
$state->SortColumn = 'Cheerleader.Name';
|
||||
$state->SortDirection = 'asc';
|
||||
$relationListA = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$state->SortDirection = 'desc';
|
||||
$relationListB = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$this->assertEquals(
|
||||
array('Wellington', 'Melbourne', 'Cologne', 'Auckland'),
|
||||
$relationListA->column('City')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array('Auckland', 'Cologne', 'Melbourne', 'Wellington'),
|
||||
$relationListB->column('City')
|
||||
);
|
||||
|
||||
// Test two relations 'deep'
|
||||
$state->SortColumn = 'Cheerleader.Hat.Colour';
|
||||
$state->SortDirection = 'asc';
|
||||
$relationListC = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$state->SortDirection = 'desc';
|
||||
$relationListD = $compontent->getManipulatedData($gridField, $list);
|
||||
|
||||
$this->assertEquals(
|
||||
array('Cologne', 'Auckland', 'Wellington', 'Melbourne'),
|
||||
$relationListC->column('City')
|
||||
);
|
||||
$this->assertEquals(
|
||||
array('Melbourne', 'Wellington', 'Auckland', 'Cologne'),
|
||||
$relationListD->column('City')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GridFieldSortableHeaderTest_Team extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar',
|
||||
'City' => 'Varchar'
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'Cheerleader' => 'GridFieldSortableHeaderTest_Cheerleader'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
class GridFieldSortableHeaderTest_Cheerleader extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar'
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'Team' => 'GridFieldSortableHeaderTest_Team',
|
||||
'Hat' => 'GridFieldSortableHeaderTest_CheerleaderHat'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
class GridFieldSortableHeaderTest_CheerleaderHat extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'Colour' => 'Varchar'
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'Cheerleader' => 'GridFieldSortableHeaderTest_Cheerleader'
|
||||
);
|
||||
|
||||
}
|
39
tests/forms/gridfield/GridFieldSortableHeaderTest.yml
Normal file
39
tests/forms/gridfield/GridFieldSortableHeaderTest.yml
Normal file
@ -0,0 +1,39 @@
|
||||
GridFieldSortableHeaderTest_CheerleaderHat:
|
||||
hat1:
|
||||
Colour: Blue
|
||||
hat2:
|
||||
Colour: Red
|
||||
hat3:
|
||||
Colour: Green
|
||||
hat4:
|
||||
Colour: Pink
|
||||
GridFieldSortableHeaderTest_Cheerleader:
|
||||
cheerleader1:
|
||||
Name: Heather
|
||||
Hat: =>GridFieldSortableHeaderTest_CheerleaderHat.hat2
|
||||
cheerleader2:
|
||||
Name: Bob
|
||||
Hat: =>GridFieldSortableHeaderTest_CheerleaderHat.hat4
|
||||
cheerleader3:
|
||||
Name: Jenny
|
||||
Hat: =>GridFieldSortableHeaderTest_CheerleaderHat.hat1
|
||||
cheerleader4:
|
||||
Name: Sam
|
||||
Hat: =>GridFieldSortableHeaderTest_CheerleaderHat.hat3
|
||||
GridFieldSortableHeaderTest_Team:
|
||||
team1:
|
||||
Name: Team 1
|
||||
City: Cologne
|
||||
Cheerleader: =>GridFieldSortableHeaderTest_Cheerleader.cheerleader3
|
||||
team2:
|
||||
Name: Team 2
|
||||
City: Wellington
|
||||
Cheerleader: =>GridFieldSortableHeaderTest_Cheerleader.cheerleader2
|
||||
team3:
|
||||
Name: Team 3
|
||||
City: Auckland
|
||||
Cheerleader: =>GridFieldSortableHeaderTest_Cheerleader.cheerleader4
|
||||
team4:
|
||||
Name: Team 4
|
||||
City: Melbourne
|
||||
Cheerleader: =>GridFieldSortableHeaderTest_Cheerleader.cheerleader1
|
Loading…
Reference in New Issue
Block a user