mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENH Refactor eagerloading fetch into separate methods
It is hard to work with the current structure - having each relation type in its own method makes it way easier to see where my concerns start and end with a given relation. This also reduces the chance of variable bleed-over, where the value in a variable in the first loop (in the same or other relation type) could bleed into the next iteration of the loop.
This commit is contained in:
parent
60ca35c02d
commit
85e503d012
@ -1099,117 +1099,187 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li
|
|||||||
}
|
}
|
||||||
// has_one
|
// has_one
|
||||||
if ($hasOneIDField) {
|
if ($hasOneIDField) {
|
||||||
$itemArray = [];
|
list($prevRelationArray, $ids) = $this->fetchEagerLoadHasOne(
|
||||||
$relationItemIDs = [];
|
$query,
|
||||||
if ($dataClass === $dataClasses[0]) {
|
$prevRelationArray,
|
||||||
while ($row = $query->record()) {
|
$hasOneIDField,
|
||||||
$itemArray[] = [
|
$relationDataClass,
|
||||||
'ID' => $row['ID'],
|
$eagerLoadRelation,
|
||||||
$hasOneIDField => $row[$hasOneIDField]
|
$relation,
|
||||||
];
|
$dataClass,
|
||||||
$relationItemIDs[] = $row[$hasOneIDField];
|
$dataClasses
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
foreach ($prevRelationArray as $itemData) {
|
|
||||||
$itemArray[] = [
|
|
||||||
'ID' => $itemData->ID,
|
|
||||||
$hasOneIDField => $itemData->$hasOneIDField
|
|
||||||
];
|
|
||||||
$relationItemIDs[] = $itemData->$hasOneIDField;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$relationArray = DataObject::get($relationDataClass)->filter(['ID' => $relationItemIDs])->toArray();
|
|
||||||
foreach ($itemArray as $itemData) {
|
|
||||||
foreach ($relationArray as $relationItem) {
|
|
||||||
$eagerLoadID = $itemData['ID'];
|
|
||||||
if ($relationItem->ID === $itemData[$hasOneIDField]) {
|
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $relationItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$prevRelationArray = $relationArray;
|
|
||||||
$ids = $relationItemIDs;
|
|
||||||
// belongs_to
|
// belongs_to
|
||||||
} elseif ($belongsToIDField) {
|
} elseif ($belongsToIDField) {
|
||||||
$relationArray = DataObject::get($relationDataClass)->filter([$belongsToIDField => $ids])->toArray();
|
list($prevRelationArray, $ids) = $this->fetchEagerLoadBelongsTo(
|
||||||
$relationItemIDs = [];
|
$ids,
|
||||||
foreach ($relationArray as $relationItem) {
|
$belongsToIDField,
|
||||||
$relationItemIDs[] = $relationItem->ID;
|
$relationDataClass,
|
||||||
$eagerLoadID = $relationItem->$belongsToIDField;
|
$eagerLoadRelation,
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $relationItem;
|
$relation
|
||||||
}
|
);
|
||||||
$prevRelationArray = $relationArray;
|
|
||||||
$ids = $relationItemIDs;
|
|
||||||
// has_many
|
// has_many
|
||||||
} elseif ($hasManyIDField) {
|
} elseif ($hasManyIDField) {
|
||||||
$relationArray = DataObject::get($relationDataClass)->filter([$hasManyIDField => $ids])->toArray();
|
list($prevRelationArray, $ids) = $this->fetchEagerLoadHasMany(
|
||||||
$relationItemIDs = [];
|
$ids,
|
||||||
foreach ($relationArray as $relationItem) {
|
$hasManyIDField,
|
||||||
$relationItemIDs[] = $relationItem->ID;
|
$relationDataClass,
|
||||||
$eagerLoadID = $relationItem->$hasManyIDField;
|
$eagerLoadRelation,
|
||||||
if (!isset($this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation])) {
|
$relation
|
||||||
$arrayList = ArrayList::create();
|
);
|
||||||
$arrayList->setDataClass($relationItem->dataClass);
|
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $arrayList;
|
|
||||||
}
|
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation]->push($relationItem);
|
|
||||||
}
|
|
||||||
$prevRelationArray = $relationArray;
|
|
||||||
$ids = $relationItemIDs;
|
|
||||||
// many_many + belongs_many_many & many_many_through
|
// many_many + belongs_many_many & many_many_through
|
||||||
} elseif ($manyManyLastComponent) {
|
} elseif ($manyManyLastComponent) {
|
||||||
$parentField = $manyManyLastComponent['parentField'];
|
list($prevRelationArray, $ids) = $this->fetchEagerLoadManyMany(
|
||||||
$childField = $manyManyLastComponent['childField'];
|
$manyManyLastComponent,
|
||||||
// $join will either be:
|
$ids,
|
||||||
// - the join table name for many-many
|
$relationDataClass,
|
||||||
// - the join data class for many-many-through
|
$eagerLoadRelation,
|
||||||
$join = $manyManyLastComponent['join'];
|
$relation
|
||||||
// many_many_through
|
);
|
||||||
if (is_a($manyManyLastComponent['relationClass'], ManyManyThroughList::class, true)) {
|
|
||||||
$joinThroughObjs = $join::get()->filter([$parentField => $ids]);
|
|
||||||
$relationItemIDs = [];
|
|
||||||
$rows = [];
|
|
||||||
foreach ($joinThroughObjs as $joinThroughObj) {
|
|
||||||
$rows[] = [
|
|
||||||
$parentField => $joinThroughObj->$parentField,
|
|
||||||
$childField => $joinThroughObj->$childField
|
|
||||||
];
|
|
||||||
$relationItemIDs[] = $joinThroughObj->$childField;
|
|
||||||
}
|
|
||||||
// many_many + belongs_many_many
|
|
||||||
} else {
|
|
||||||
$joinTableQuery = DB::query('SELECT * FROM "' . $join . '" WHERE "' . $parentField . '" IN (' . implode(',', $ids) . ')');
|
|
||||||
$relationItemIDs = [];
|
|
||||||
$rows = [];
|
|
||||||
while ($row = $joinTableQuery->record()) {
|
|
||||||
$rows[] = [
|
|
||||||
$parentField => $row[$parentField],
|
|
||||||
$childField => $row[$childField]
|
|
||||||
];
|
|
||||||
$relationItemIDs[] = $row[$childField];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$relationArray = DataObject::get($relationDataClass)->filter(['ID' => $relationItemIDs])->toArray();
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$eagerLoadID = $row[$parentField];
|
|
||||||
if (!isset($this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation])) {
|
|
||||||
$arrayList = ArrayList::create();
|
|
||||||
$arrayList->setDataClass($manyManyLastComponent['childClass']);
|
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $arrayList;
|
|
||||||
}
|
|
||||||
$relationItem = array_values(array_filter($relationArray, function ($relationItem) use ($row, $childField) {
|
|
||||||
return $relationItem->ID === $row[$childField];
|
|
||||||
}))[0];
|
|
||||||
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation]->push($relationItem);
|
|
||||||
}
|
|
||||||
$prevRelationArray = $relationArray;
|
|
||||||
$ids = $relationItemIDs;
|
|
||||||
} else {
|
} else {
|
||||||
throw new LogicException('Something went wrong with the eager loading');
|
throw new LogicException('Something went wrong with the eager loading');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function fetchEagerLoadHasOne(
|
||||||
|
Query $query,
|
||||||
|
array $prevRelationArray,
|
||||||
|
string $hasOneIDField,
|
||||||
|
string $relationDataClass,
|
||||||
|
string $eagerLoadRelation,
|
||||||
|
string $relation,
|
||||||
|
string $dataClass,
|
||||||
|
array $dataClasses
|
||||||
|
): array
|
||||||
|
{
|
||||||
|
$itemArray = [];
|
||||||
|
$relationItemIDs = [];
|
||||||
|
if ($dataClass === $dataClasses[0]) {
|
||||||
|
while ($row = $query->record()) {
|
||||||
|
$itemArray[] = [
|
||||||
|
'ID' => $row['ID'],
|
||||||
|
$hasOneIDField => $row[$hasOneIDField]
|
||||||
|
];
|
||||||
|
$relationItemIDs[] = $row[$hasOneIDField];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($prevRelationArray as $itemData) {
|
||||||
|
$itemArray[] = [
|
||||||
|
'ID' => $itemData->ID,
|
||||||
|
$hasOneIDField => $itemData->$hasOneIDField
|
||||||
|
];
|
||||||
|
$relationItemIDs[] = $itemData->$hasOneIDField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$relationArray = DataObject::get($relationDataClass)->filter(['ID' => $relationItemIDs])->toArray();
|
||||||
|
foreach ($itemArray as $itemData) {
|
||||||
|
foreach ($relationArray as $relationItem) {
|
||||||
|
$eagerLoadID = $itemData['ID'];
|
||||||
|
if ($relationItem->ID === $itemData[$hasOneIDField]) {
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $relationItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [$relationArray, $relationItemIDs];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchEagerLoadBelongsTo(
|
||||||
|
array $ids,
|
||||||
|
string $belongsToIDField,
|
||||||
|
string $relationDataClass,
|
||||||
|
string $eagerLoadRelation,
|
||||||
|
string $relation
|
||||||
|
): array
|
||||||
|
{
|
||||||
|
$relationArray = DataObject::get($relationDataClass)->filter([$belongsToIDField => $ids])->toArray();
|
||||||
|
$relationItemIDs = [];
|
||||||
|
foreach ($relationArray as $relationItem) {
|
||||||
|
$relationItemIDs[] = $relationItem->ID;
|
||||||
|
$eagerLoadID = $relationItem->$belongsToIDField;
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $relationItem;
|
||||||
|
}
|
||||||
|
return [$relationArray, $relationItemIDs];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchEagerLoadHasMany(
|
||||||
|
array $ids,
|
||||||
|
string $hasManyIDField,
|
||||||
|
string $relationDataClass,
|
||||||
|
string $eagerLoadRelation,
|
||||||
|
string $relation
|
||||||
|
): array
|
||||||
|
{
|
||||||
|
$relationArray = DataObject::get($relationDataClass)->filter([$hasManyIDField => $ids])->toArray();
|
||||||
|
$relationItemIDs = [];
|
||||||
|
foreach ($relationArray as $relationItem) {
|
||||||
|
$relationItemIDs[] = $relationItem->ID;
|
||||||
|
$eagerLoadID = $relationItem->$hasManyIDField;
|
||||||
|
if (!isset($this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation])) {
|
||||||
|
$arrayList = ArrayList::create();
|
||||||
|
$arrayList->setDataClass($relationItem->dataClass);
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $arrayList;
|
||||||
|
}
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation]->push($relationItem);
|
||||||
|
}
|
||||||
|
return [$relationArray, $relationItemIDs];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchEagerLoadManyMany(
|
||||||
|
array $manyManyLastComponent,
|
||||||
|
array $ids,
|
||||||
|
string $relationDataClass,
|
||||||
|
string $eagerLoadRelation,
|
||||||
|
string $relation
|
||||||
|
): array
|
||||||
|
{
|
||||||
|
$parentField = $manyManyLastComponent['parentField'];
|
||||||
|
$childField = $manyManyLastComponent['childField'];
|
||||||
|
// $join will either be:
|
||||||
|
// - the join table name for many-many
|
||||||
|
// - the join data class for many-many-through
|
||||||
|
$join = $manyManyLastComponent['join'];
|
||||||
|
// many_many_through
|
||||||
|
if (is_a($manyManyLastComponent['relationClass'], ManyManyThroughList::class, true)) {
|
||||||
|
$joinThroughObjs = $join::get()->filter([$parentField => $ids]);
|
||||||
|
$relationItemIDs = [];
|
||||||
|
$rows = [];
|
||||||
|
foreach ($joinThroughObjs as $joinThroughObj) {
|
||||||
|
$rows[] = [
|
||||||
|
$parentField => $joinThroughObj->$parentField,
|
||||||
|
$childField => $joinThroughObj->$childField
|
||||||
|
];
|
||||||
|
$relationItemIDs[] = $joinThroughObj->$childField;
|
||||||
|
}
|
||||||
|
// many_many + belongs_many_many
|
||||||
|
} else {
|
||||||
|
$joinTableQuery = DB::query('SELECT * FROM "' . $join . '" WHERE "' . $parentField . '" IN (' . implode(',', $ids) . ')');
|
||||||
|
$relationItemIDs = [];
|
||||||
|
$rows = [];
|
||||||
|
while ($row = $joinTableQuery->record()) {
|
||||||
|
$rows[] = [
|
||||||
|
$parentField => $row[$parentField],
|
||||||
|
$childField => $row[$childField]
|
||||||
|
];
|
||||||
|
$relationItemIDs[] = $row[$childField];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$relationArray = DataObject::get($relationDataClass)->filter(['ID' => $relationItemIDs])->toArray();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$eagerLoadID = $row[$parentField];
|
||||||
|
if (!isset($this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation])) {
|
||||||
|
$arrayList = ArrayList::create();
|
||||||
|
$arrayList->setDataClass($manyManyLastComponent['childClass']);
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation] = $arrayList;
|
||||||
|
}
|
||||||
|
$relationItem = array_values(array_filter($relationArray, function ($relationItem) use ($row, $childField) {
|
||||||
|
return $relationItem->ID === $row[$childField];
|
||||||
|
}))[0];
|
||||||
|
$this->eagerLoadedData[$eagerLoadRelation][$eagerLoadID][$relation]->push($relationItem);
|
||||||
|
}
|
||||||
|
return [$relationArray, $relationItemIDs];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eager load relations for DataObjects in this DataList including nested relations
|
* Eager load relations for DataObjects in this DataList including nested relations
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user