Skip to content

Commit

Permalink
NEW Add support for ManyManyThrough relations
Browse files Browse the repository at this point in the history
Previously relationships defiend as many_many came in a special type
of RelationList - however now this can be one of two types of
RelationList depending on the type of definition, with both being
valid many_many relationships.

This had the unfortunate side effect of seeing the OrderableRows
component in (the least) cease functioning correctly. No longer.
  • Loading branch information
Dylan Wagstaff committed May 31, 2018
1 parent ba0d23a commit 872b04f
Showing 1 changed file with 74 additions and 7 deletions.
81 changes: 74 additions & 7 deletions src/GridFieldOrderableRows.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\ManyManyList;
use SilverStripe\ORM\ManyManyThroughList;
use SilverStripe\ORM\ManyManyThroughQueryManipulator;
use SilverStripe\ORM\Map;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\FieldType\DBDatetime;
Expand Down Expand Up @@ -141,6 +144,16 @@ public function getExtraSortFields()
return $this->extraSortFields;
}

/**
* Checks to see if the relationship list is for a type of many_many
*
* @param SS_List $list
*/
protected function isManyMany(SS_List $list)
{
return $list instanceof ManyManyList || $list instanceof ManyManyThroughList;
}

/**
* Sets extra sort fields to apply before the sort field.
*
Expand Down Expand Up @@ -189,6 +202,9 @@ public function validateSortField(SS_List $list)
if ($extra && array_key_exists($field, $extra)) {
return;
}
} elseif ($list instanceof ManyManyThroughList) {
$manipulator = $this->getManyManyInspector($list);
return DataObject::getSchema()->tableName($manipulator->getJoinClass(), $field);
}

$classes = ClassInfo::dataClassesFor($list->dataClass());
Expand Down Expand Up @@ -217,6 +233,8 @@ public function getSortTable(SS_List $list)
if ($extra && array_key_exists($field, $extra)) {
return $table;
}
} elseif ($list instanceof ManyManyThroughList) {
return $this->getManyManyInspector($list)->getJoinAlias();
}
$classes = ClassInfo::dataClassesFor($list->dataClass());
foreach ($classes as $class) {
Expand Down Expand Up @@ -340,9 +358,10 @@ public function handleReorder($grid, $request)
}
$list = $grid->getList();
$modelClass = $grid->getModelClass();
if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
$isManyMany = $this->isManyMany($list);
if ($isManyMany && !singleton($modelClass)->canView()) {
$this->httpError(403);
} elseif (!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) {
} elseif (!$isManyMany && !singleton($modelClass)->canEdit()) {
$this->httpError(403);
}

Expand Down Expand Up @@ -510,6 +529,16 @@ protected function executeReorder(GridField $grid, $sortedIDs)
$current[$record->ID] = $record->$field;
}
}
} elseif ($items instanceof ManyManyThroughList) {
$manipulator = $this->getManyManyInspector($list);
$joinClass = $manipulator->getJoinClass();
$fromRelationName = $manipulator->getForeignKey();
$toRelationName = $manipulator->getLocalKey();
$list = DataList::create($joinClass)->filter([
$toRelationName => $items->column('ID'),
$fromRelationName => $items->first()->getJoin()->$fromRelationName,
]);
$current = $list->map('ID', $field)->toArray();
} else {
$current = $items->map('ID', $field)->toArray();
}
Expand Down Expand Up @@ -629,7 +658,7 @@ protected function populateSortValues(DataList $list)
$this->getSortTableClauseForIds($list, $id)
));

if (!$isBaseTable && !$list instanceof ManyManyList) {
if (!$isBaseTable && !$this->isManyMany($list)) {
DB::query(sprintf(
'UPDATE "%s" SET "LastEdited" = \'%s\' WHERE %s',
$baseTable,
Expand All @@ -640,6 +669,18 @@ protected function populateSortValues(DataList $list)
}
}

/**
* Forms a WHERE clause for the table the sort column is defined on.
* e.g. ID = 5
* e.g. ID IN(5, 8, 10)
* e.g. SortOrder = 5 AND RelatedThing.ID = 3
* e.g. SortOrder IN(5, 8, 10) AND RelatedThing.ID = 3
*
* @param DataList $list
* @param int|string|array $ids a single number, or array of numbers
*
* @return string
*/
protected function getSortTableClauseForIds(DataList $list, $ids)
{
if (is_array($ids)) {
Expand All @@ -648,10 +689,13 @@ protected function getSortTableClauseForIds(DataList $list, $ids)
$value = '= ' . (int) $ids;
}

if ($list instanceof ManyManyList) {
$extra = $list->getExtraFields();
$key = $list->getLocalKey();
$foreignKey = $list->getForeignKey();
if ($this->isManyMany($list)) {
$intropector = $this->getManyManyInspector($list);
$extra = $list instanceof ManyManyList ?
$intropector->getExtraFields() :
DataObjectSchema::create()->fieldSpecs($intropector->getJoinClass(), DataObjectSchema::DB_ONLY);
$key = $intropector->getLocalKey();
$foreignKey = $intropector->getForeignKey();
$foreignID = (int) $list->getForeignID();

if ($extra && array_key_exists($this->getSortField(), $extra)) {
Expand All @@ -667,4 +711,27 @@ protected function getSortTableClauseForIds(DataList $list, $ids)

return "\"ID\" $value";
}

/**
* A ManyManyList defines functions such as getLocalKey, however on ManyManyThroughList
* these functions are moved to ManyManyThroughQueryManipulator, but otherwise retain
* the same signature.
*
* @param ManyManyList|ManyManyThroughList
*
* @return ManyManyList|ManyManyThroughQueryManipulator
*/
protected function getManyManyInspector($list)
{
$inspector = $list;
if ($list instanceof ManyManyThroughList) {
foreach ($list->dataQuery()->getDataQueryManipulators() as $manipulator) {
if ($manipulator instanceof ManyManyThroughQueryManipulator) {
$inspector = $manipulator;
break;
}
}
}
return $inspector;
}
}

0 comments on commit 872b04f

Please sign in to comment.