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:
Loz Calver 2013-10-23 21:59:06 +01:00 committed by Loz Calver
parent ad52cc20c1
commit a1a402224f
3 changed files with 228 additions and 2 deletions

View File

@ -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);
}
}

View 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'
);
}

View 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