如何通过编程对视图结果进行排序?

11

我正在尝试获取视图结果,并使用函数views_get_view_result()将数组按照我无法从Views界面内部完成的方式进行排序。目前为止一切顺利,我有一个包含所有必要内容的$ rows变量。

现在...我该如何将其放回去呢? :) 在需要这种排序之前,我使用了views_embed_view(),但我现在不能这样做了。

非常感谢任何关于此的帮助,感觉我已经快破解了它!

$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

你可以编辑Views界面生成的SQL查询以在查询中进行排序吗? - alxp
不,排序取决于一些在该上下文中不可用的变量。 - Ace
4个回答

19

视图模块提供了一些钩子,用于“外部”操作,就像Drupal核心一样。

您可以在自定义模块中实现hook_views_pre_render(&$view)并操纵$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);
  }
}

这个钩子在视图生成过程的中间被调用,所有结果数据都已经被组装好了,但是在实际输出之前,因此对结果数组的更改将会反映在视图的最终输出中。

编辑:或者,您可以手动处理视图,复制views_get_view_result() 函数的行为,但是不返回结果,而是操作它并继续渲染视图:

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

注意:这里有很多代码重复,因此容易出错 - 我不建议这样做,而是更愿意使用上面的挂钩实现,在其中尝试找到一种获取'$important_var'的方法。


听起来真的很好!但是如果我有一些需要用于排序的外部变量怎么办? - Ace
@Ace:你可以通过在钩子实现中调用important_function(),或通过先前填充的全局变量,或通过调用使用静态存储先前计算的变量的“get_x()”函数来获取外部变量内容,否则你需要手动进行视图处理 - 我会更新我的答案以展示后一种选项。 - Henrik Opel
5
使用 hook_views_pre_render 无法与分页器一起使用,它只会对当前页面的结果进行排序。 - eSentrik

10

根据您的排序逻辑,您可以尝试使用hook_views_query_alter()将您的排序直接实现到查询中。

不过这可能有点棘手,您可能需要熟悉views_query对象。

以下是一个真实世界的示例,在此示例中,我基于页面上下文应用了加入规则,然后又添加了排序规则。

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

另一个使用 SQL ORDER BY CASE 语句的例子可以在这个答案中找到。它允许对列表字段进行任意排序。 - tanius
谢谢你提供使用表别名在add_orderby中的示例 :) - D34dman

3
我用以下代码实现此功能。
/**
 * 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()对我来说不起作用。


0

我将上述示例结合起来,并重写为以下内容。在我的情况下,我必须逐个对项目进行排序。

注意:我没有包括连接条件,也没有尝试使用自定义字段。但我认为你可以很容易地弄清楚。

/** 
 * 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')";
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接