I have a View of song
nodes, and each node has a programmatically calculated total number of points associated with it. (For background: song
nodes are referenced in a custom field_chart_items
field on a chart
node, hence the $chartHistory
node query seen in the hook below. The custom field includes a numeric Position that's entered on the chart
node, and a numeric Points total that's hidden from view and calculated based on Position in a chart_field_entity_presave()
hook.) I'm trying to sort the View's results by points, with highest points totals listed first.
In a views_pre_render()
hook, I calculate the points total for each row and place it into an array, $songAndPoints
, that contains the song's nid
and points total. The array is sorted so that the song with the highest points total comes first – therefore, the order of the nid
s in the array is the order in which I want my results to appear.
Can I programmatically reorder the $view->result
array based on the array of nid
s I'm getting? My attempt below, using a uasort()
callback, doesn't work, but I'm not sure if or how I can do what I'm looking for (or if, perhaps, it needs to happen in a different hook).
<?php
/**
* Implements hook_views_pre_render().
*/
function MODULE_views_pre_render(&$view) {
if ($view->current_display === 'DISPLAYID'
&& $view->id() === 'VIEWID') {
$entity_type_manager = \Drupal::entityTypeManager();
$node_storage = $entity_type_manager->getStorage('node');
foreach ($view->result as $key => $song) {
$node_type = 'chart';
$songNID = $song->nid;
$chartHistory = $node_storage->getQuery()
->condition('type', $node_type)
->condition('field_chart_items', $songNID, 'IN')
->condition('status', 1)
->execute();
if ($chartHistory) {
foreach ($chartHistory as $chartNID) {
// ...
// Various calculations to get points total, $songPoints, for each song.
// ...
}
$songAndPoints[$key] = ['points' => $songPoints, 'nid' => $songNID];
// Sort based on point values.
arsort($songAndPoints);
} else {
$songAndPoints[] = ['points' => 0, 'nid' => $songNID];
}
}
kint($songAndPoints); // Array with original row number as key and two values, points and NID, sorted with highest points first. This is how I want to sort the results.
uasort($view->result, "MODULE_sort_by_points_total");
}
}
/**
* Custom uasort() callback to sort view results by points totals.
*/
function MODULE_sort_by_points_total($left, $right) {
$order = ['704', '712', '707', '713', '711', '709', '708', '883', '882', '715', '714', '710', '705']; // TEMPORARY: Hardcoded list of node IDs in order; should be replaced by something like array_column($songAndPoints, 'nid'), but hardcoded values don't work either.
$flipped = array_flip($order);
$leftPos = $flipped[$left]->nid;
$rightPos = $flipped[$right]->nid;
return $leftPos >= $rightPos;
// via https://stackoverflow.com/questions/17324708/php-array-multisort-not-sorting-my-multidimensional-array-as-expected/17325584#17325584
}