11

I'm trying to take the results of a view - using the function views_get_view_result() - and sort the array in a way I couldn't do from within the Views interface. So far so good. I've got a $rows variable with all of the stuff I need.

Now... How do I put it back? :) Before I needed this sort, I used views_embed_view(), but I can't do that anymore.

Grateful for any help on this, feels like I'm so close to cracking it!

$important_var = important_function();
$result = views_get_view_result($view, $display, $args);
$result = sorting_function($result, $important_var);

//TODO: Put the result back into the view
kenorb
  • 155,785
  • 88
  • 678
  • 743
Ace
  • 4,443
  • 6
  • 38
  • 46
  • Can you edit the SQL query the Views interface generates to do the sort in the query? – alxp Mar 18 '10 at 16:16
  • No, the sorting depends on some variables that aren't available in that context. – Ace Mar 18 '10 at 16:17

4 Answers4

19

The views module provides some hooks for 'external' manipulations, just like Drupal core.

You can implement hook_views_pre_render(&$view) within a custom module and manipulate the result array available in $view->result:

/**
 * Implementation of hook_views_pre_render()
 *
 * @param view $view
 */
function YourModuleName_views_pre_render(&$view) {
  // Check if this is the view and display you want to manipulate
  // NOTE: Adjust/Remove the display check, if you want to manipulate some/all displays of the view
  if ('YourViewName' == $view->name && 'YourDisplayName' == $view->current_display) {
    // EXAMPLE: Just reverse result order
    // TODO: Replace with your desired (re)ordering logic
    $view->result = array_reverse($view->result);
  }
}

The hook is invoked in the middle of the view generation process, after all result data has been assembled, but before the actual output gets rendered, so changes to the result array will be reflected in the views final output.

EDIT: Alternatively, you could process the view 'manually', by copying the behavior of the views_get_view_result() function, but instead of returning the result, you manipulate it and continue to render the view:

function yourModule_get_custom_sorted_view($display_id = NULL) {
  // As the custom sorting probably only works for a specific view,
  // we 'demote' the former $name function parameter of 'views_get_view_result()'
  // and set it within the function:
  $name = 'yourViewName';
  // Prepare a default output in case the view definition can not be found
  // TODO: Decide what to return in that case (using empty string for now)
  $output = '';

  // Then we create the result just as 'views_get_view_result()' would do it:
  $args = func_get_args();
  if (count($args)) {
    array_shift($args); // remove $display_id
  }

  $view = views_get_view($name);
  if (is_object($view)) {
    if (is_array($args)) {
      $view->set_arguments($args);
    }
    if (is_string($display_id)) {
      $view->set_display($display_id);
    }
    else {
      $view->init_display();
    }
    $view->pre_execute();
    $view->execute();
    // 'views_get_view_result()' would just return $view->result here,
    // but we need to go on, reordering the result:
    $important_var = important_function();
    $view->result = sorting_function($result, $important_var);
    // Now we continue the view processing and generate the rendered output
    // NOTE: $view->render will call $view->execute again,
    // but the execute method will detect that it ran already and not redo it.
    $output = $view->render();
    // Clean up after processing
    $view->post_execute();
  }

  return $output;
}

Note: This is a lot of code duplication and thus error prone - I do not recommend this and would rather go with the hook implementation above, trying to find a way to get access to your '$important_var' from within that.

Henrik Opel
  • 19,341
  • 1
  • 48
  • 64
  • That sounds really good! But what if I have some external variable that I need for the sorting? – Ace Mar 19 '10 at 08:02
  • @Ace: Either you can get the external variable content into the hook implementation (by calling `important_function()` from there, or via a previously populated global, or by calling a 'get_x()' function that uses a static to store the previously calculated variable), or you need to do the view processing 'manually' - I'll update my answer to show the latter option. – Henrik Opel Mar 19 '10 at 10:35
  • 5
    Using hook_views_pre_render does not work with a pager. It will only sort the current page's results. – eSentrik May 13 '13 at 14:40
10

Depending on your sorting logic, you might trying using hook_views_query_alter() to implement your sort directly into the query.

This can be a little tricky though, you may to familiarize yourself with the views_query object.

Here is a real world example where I applied a join rule based on the context of the page, then additionally added sorting rules.

/** 
 * Implementation of hook_views_query_alter(). 
 */ 
function yourmodule_views_query_alter(&$view, &$query) { 
  if ($view->name == 'view_projects' && $view->current_display == 'panel_pane_4') { 
    if (arg(0) == 'node' && is_numeric(arg(1))) { 
      $node = node_load(arg(1)); 
      if ($client_nid = $node->field_ref_client[0]['nid']) { 
        $query->table_queue['node_node_data_field_ref_client']['join']->extra = "field_ref_client_nid = " . $client_nid;
        $query->add_orderby('node', NULL, 'DESC', 'node_node_data_field_ref_client_nid'); 
        $query->add_orderby('node', 'created', 'DESC'); 
      } 
    }
  } 
}
apaderno
  • 28,547
  • 16
  • 75
  • 90
speedytwenty
  • 111
  • 4
  • Another example with a SQL [`ORDER BY CASE`](http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html#operator_case) statement is in [this answer](http://drupal.stackexchange.com/a/44871/16305). Allows an arbitrary sort order for list fields. – tanius Sep 15 '14 at 23:48
  • thanks for the example using table alias in add_orderby :) – D34dman Apr 20 '18 at 18:57
3

I use the following code for it.

/**
 * Implementation of hook_views_post_execute()
 *
 * @param view $view
 */
function YourModuleName_views_post_execute(&$view) {
  // Check if this is the view and display you want to manipulate
  // NOTE: Adjust/Remove the display check, if you want to manipulate some/all displays of the view
  if ('YourViewName' == $view->name && 'YourDisplayName' == $view->current_display) {
    // EXAMPLE: Just reverse result order
    // TODO: Replace with your desired (re)ordering logic
    $view->result = array_reverse($view->result);
  }
}

hook_views_pre_render() doesn't work, for me.

apaderno
  • 28,547
  • 16
  • 75
  • 90
Denis Borisov
  • 166
  • 1
  • 3
0

I combined the above examples and rewrote it as the following. In my case, I had to sort the items individually.

NOTE: I didn't include the join condition and I didn't try it with custom field. But I feel you can easily figure it out.

/** 
 * Implementation of hook_views_query_alter(). 
 */ 
function yourmodule_views_query_alter(&$view, &$query) { 
  if ($view->name == 'your_view_name' && $view->current_display == 'your_pane_name') {
    if ($query->orderby[0]['field'] == 'my_order_field') {
      $query->orderby[0]['field'] = "FIELD(my_order_field, 'ord3','ord1','ord2')";
    }
  }
}