Drupal视图Alter钩子获取总行数及去重后计数异常解决
解决Drupal视图去重后的计数异常问题&获取总行数方法
我来帮你搞定这个视图去重后的计数问题,顺便说下怎么在Alter钩子里获取总行数。
首先说你的问题根源:你当前的代码在pre_render阶段直接把$view->total_rows设为当前页去重后的结果数,但分页开启时,$view->result已经是当前页的数据集了,所以总计数自然不对。而且你注释掉了$view->pager->total_items的设置,这也是分页控件显示异常的核心原因。
修正后的pre_render钩子代码
下面是调整后的代码,能正确显示去重后的总结果数,同时修复分页计数问题:
function search_helper_views_pre_render(\Drupal\views\ViewExecutable $view) { $target_views = ["all_news", "all_publications", "all_events", "global_search"]; if (!in_array($view->id(), $target_views)) { return; } // 第一步:获取所有符合查询条件的nid,计算去重后的总数量 $base_query = $view->query->query(); // 只查询nid,减少数据传输开销 $base_query->addField('node_field_data', 'nid'); $all_raw_nids = $base_query->execute()->fetchCol(); $unique_total_count = count(array_unique($all_raw_nids)); // 第二步:处理当前页的结果去重 $current_page_unique_nids = []; foreach ($view->result as $key => $result_item) { if (!in_array($result_item->nid, $current_page_unique_nids)) { $current_page_unique_nids[] = $result_item->nid; } else { unset($view->result[$key]); } } // 重置数组索引,避免视图渲染时出现索引断裂的问题 $view->result = array_values($view->result); // 第三步:更新视图计数和分页信息 $view->total_rows = $unique_total_count; if ($view->pager) { $view->pager->total_items = $unique_total_count; // 可选处理:如果当前页去重后为空,自动跳转到上一页 if (empty($view->result) && $view->current_page > 0) { $view->current_page--; $view->pager->setCurrentPage($view->current_page); // 重新执行查询获取上一页数据 $view->execute(); // 再次对新结果去重 $new_page_unique = []; foreach ($view->result as $k => $res) { if (!in_array($res->nid, $new_page_unique)) { $new_page_unique[] = $res->nid; } else { unset($view->result[$k]); } } $view->result = array_values($view->result); } $view->pager->updatePageInfo(); } }
代码关键点解释
- 获取总去重数量:通过视图的原始查询获取所有nid,再用
array_unique去重后计数,这样得到的是全量去重后的总数,不是当前页的数量。 - 重置结果数组索引:删除重复项后数组会出现索引空缺,重置索引能避免视图渲染时出现报错或显示异常。
- 正确设置分页参数:把
$view->pager->total_items设为总去重数量,分页控件就能显示正确的页数和总计数了。 - 空页面处理:可选逻辑,防止用户进入去重后为空的分页页面,提升体验。
关于在视图Alter钩子中获取总行数
根据不同的钩子阶段,获取方式略有不同:
1. 在hook_views_query_alter中(查询执行前)
这个阶段可以复制原始查询,执行计数操作,还能提前修改查询逻辑:
function search_helper_views_query_alter(\Drupal\views\ViewExecutable $view, \Drupal\views\Plugin\views\query\Sql $query) { $target_views = ["all_news", "all_publications", "all_events", "global_search"]; if (!in_array($view->id(), $target_views)) { return; } // 复制查询,仅做计数(去重版) $count_query = clone $query->query(); $count_query->addExpression('COUNT(DISTINCT node_field_data.nid)', 'total'); $count_query->groupBy = []; // 移除分组保证计数准确 $total_unique_rows = $count_query->execute()->fetchField(); // 可以把结果存在视图临时属性里,供后续钩子使用 $view->set('custom_total_unique', $total_unique_rows); }
2. 在hook_views_pre_render/hook_views_post_render中
- 如果要获取原始查询的总行数(未去重),直接用
$view->total_rows即可。 - 如果要获取去重后的总行数,就用上面解决方案里的查询方式,或者从
query_alter阶段存储的临时属性里读取。
更优的去重方式(推荐)
如果你的重复结果是因为视图关联了其他表导致的,直接在数据库层面去重更高效:
function search_helper_views_query_alter(\Drupal\views\ViewExecutable $view, \Drupal\views\Plugin\views\query\Sql $query) { $target_views = ["all_news", "all_publications", "all_events", "global_search"]; if (in_array($view->id(), $target_views)) { // 添加DISTINCT关键字,让数据库直接返回唯一结果 $query->distinct(); } }
这种方式不需要在PHP层面处理去重,计数也会自动正确,性能更好。
内容的提问来源于stack exchange,提问作者Arunkumar T
相关产品推荐
相关产品推荐

