If I want to display, in a view, a table of main nodes (e.g. A,B,C,...) with a relationship to another entity (1,2,3,...) so that:
A is related to 1, 2
B is related to 3
C is related to 4,5

If I create a view displaying the field node title (A,B,...) and related entity title (1,2,...), filtering by node title and aggregating by related entity title, I get:
A; 1,2,3,4,5
B; 1,2,3,4,5
C; 1,2,3,4,5

Instead of the expected behaviour:
A; 1,2
B; 3
C; 4,5

Is this designed or a bug?

Comments

jay.dansand’s picture

Assigned: Unassigned » jay.dansand
Status: Active » Postponed (maintainer needs more info)

Are you performing your filtering and aggregation post-render (the "Use the rendered output of this field" option is enabled) or pre-render (the aforementioned option is NOT enabled)? Are both fields using the same timing (i.e. are they both post-render, or not, or some mix)?

The aggregation step is performed before the filtering step, so in general I might expect that output, but not necessary the part where the related entities are aggregated for every node, regardless of relation. You may have luck applying the filter pre-render and aggregation post-render, but I'd like to make sure this isn't a bug, if you have some time to help me investigate.

I think a Views export would really help, or some extra information. For starters,

  1. is the base table the node table, or the related entity?
  2. what does the View look like without Views Distinct? Is it something like:
    A: 1
    A: 2
    B: 3
    C: 4
    C: 5
inno81’s picture

1. The base table is the node table.
2. Yes.

See below the view export. The display is "Full list", the base node is "nonconformity" and the related entity is "corrective action". There are other nodes in relationships (e.g. "sorting") but removing them does not have any effect.

Edited: CODE REMOVED. See comments below for a better example.

inno81’s picture

Status: Postponed (maintainer needs more info) » Active

Setting back to active.
Please let me know if you need more info.

inno81’s picture

I have tested applying the filter pre-render and aggregation post-render, but it does not solve the issue.

inno81’s picture

Ok, on a clean install, with the simple view incuded below, I get the following scenarios:

1) No "view distinct"
Main Node Referencing Node
Apple | One
Apple | Two
Banana | Three
Pear | Four
Pear | Five

2) View distinct, using pre-rendered on filtered, and post-rendered on aggregated
Main Node Referencing Node
Apple | One, Three, Four
Banana | One, Three, Four
Pear | One, Three, Four

3) View distinct, using post-rendered on both
Main Node Referencing Node
Apple | One, Two, Three, Four, Five
Banana | One, Two, Three, Four, Five
Pear | One, Two, Three, Four, Five

View (related to third scenario):

$view = new view();
$view->name = 'test_view';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Test View';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */

/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Test View';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'full';
$handler->display->display_options['pager']['options']['items_per_page'] = '10';
$handler->display->display_options['style_plugin'] = 'table';
/* Relationship: Entity Reference: Referencing entity */
$handler->display->display_options['relationships']['reverse_field_ref_to_a_node']['id'] = 'reverse_field_ref_to_a_node';
$handler->display->display_options['relationships']['reverse_field_ref_to_a_node']['table'] = 'node';
$handler->display->display_options['relationships']['reverse_field_ref_to_a_node']['field'] = 'reverse_field_ref_to_a_node';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Field: Content: Title */
$handler->display->display_options['fields']['title_1']['id'] = 'title_1';
$handler->display->display_options['fields']['title_1']['table'] = 'node';
$handler->display->display_options['fields']['title_1']['field'] = 'title';
$handler->display->display_options['fields']['title_1']['relationship'] = 'reverse_field_ref_to_a_node';
/* Sort criterion: Content: Title */
$handler->display->display_options['sorts']['title']['id'] = 'title';
$handler->display->display_options['sorts']['title']['table'] = 'node';
$handler->display->display_options['sorts']['title']['field'] = 'title';
/* Filter criterion: Content: Published */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'node';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = 1;
$handler->display->display_options['filters']['status']['group'] = 1;
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
/* Filter criterion: Content: Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
  'content_a' => 'content_a',
);

/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['path'] = 'test-view';
$handler->display->display_options['menu']['type'] = 'normal';
$handler->display->display_options['menu']['title'] = 'Test View';
$handler->display->display_options['menu']['name'] = 'main-menu';

I cannot imagine how the module can be useful with this bug! Maybe when there is only one main node?

inno81’s picture

FileSize
26.27 KB
23.28 KB
25.93 KB
jay.dansand’s picture

Sorry for the delay in response! Work has been pulling me in a dozen directions. I'll try to take another look today.

In case I have this wrong, it looks like the example view above is a view built on "Content" (base table is node and content type is "content_a"), and a relationship via Entity Reference to nodes that have a field (reverse_field_ref_to_a_node) which references the "content_a" nodes. The content_a nodes themselves do not have entity references, but it's the other way around, right? Or do I have that backwards?

inno81’s picture

Yes Jay, you are right, but using a direct relationship (view of content B with relationship to contant A) give me the same problem. View attached.

$view = new view();
$view->name = 'test_view_2';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Test view 2';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */

/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Test view 2';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'full';
$handler->display->display_options['pager']['options']['items_per_page'] = '10';
$handler->display->display_options['style_plugin'] = 'table';
/* Relationship: Entity Reference: Referenced Entity */
$handler->display->display_options['relationships']['field_ref_to_a_target_id']['id'] = 'field_ref_to_a_target_id';
$handler->display->display_options['relationships']['field_ref_to_a_target_id']['table'] = 'field_data_field_ref_to_a';
$handler->display->display_options['relationships']['field_ref_to_a_target_id']['field'] = 'field_ref_to_a_target_id';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Field: Content: Title */
$handler->display->display_options['fields']['title_1']['id'] = 'title_1';
$handler->display->display_options['fields']['title_1']['table'] = 'node';
$handler->display->display_options['fields']['title_1']['field'] = 'title';
$handler->display->display_options['fields']['title_1']['relationship'] = 'field_ref_to_a_target_id';
$handler->display->display_options['fields']['title_1']['label'] = '';
$handler->display->display_options['fields']['title_1']['element_label_colon'] = FALSE;
/* Sort criterion: Content: Post date */
$handler->display->display_options['sorts']['created']['id'] = 'created';
$handler->display->display_options['sorts']['created']['table'] = 'node';
$handler->display->display_options['sorts']['created']['field'] = 'created';
/* Filter criterion: Content: Published */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'node';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = 1;
$handler->display->display_options['filters']['status']['group'] = 1;
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
/* Filter criterion: Content: Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
  'content_b' => 'content_b',
);

/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['path'] = 'test-view-2';
jay.dansand’s picture

Well, I can definitely reproduce the issue. Filtering works as you'd expect, but aggregation works as designed but maybe not how you'd expect.

The current design for aggregation is that when you flag a field to be aggregated, it literally just aggregates the field across all occurrences. That's relatively consistent with the stated purpose of Views Distinct, which is that when you set a field as a Distinct field (which is Aggregated or Filtered), you are saying that "this field establishes uniqueness of the row".

I'm completely open to changing that behavior, we just have to decide what makes sense. There are at least 2 ways we can address this. Here are some thoughts, given this view:

Referenced Node Referencing Node Referencing Author
Apple One jay.dansand
Apple Two jay.dansand
Apple Three inno81
Banana Four inno81
  1. Setting "Referenced Node" to "aggregated" basically flags it to say "this field should be unique, and if it repeats in results, aggregate the other fields into one row per this field."
    So, given the example table, you'd get:
    Referenced Node (aggregated) Referencing Node Referencing Author
    Apple One, Two, Three jay.dansand, jay.dansand, inno81
    Banana Four inno81

    That seems valid for, say, a view based on nodes and authors, where you want one row per author and all of their nodes aggregated into a single row. It would also work great for your use case, just set "Referenced Node's Title" as the aggregation point, and then all of the "Referencing Nodes" would be aggregated into one row.
    That's also more in line with what the documentation says about Views Distinct - basically, you are trying to say that field X establishes the uniqueness of a row.

  2. When you flag a field (this time Referencing Node in our example) as an aggregated field, then if any 2 rows are the same except for the value in the selected field, the Referencing Node field will be aggregated in order to fold the otherwise-duplicate rows together:
    Referenced Node Referencing Node (aggregated) Referencing Author
    Apple One, Two jay.dansand
    Apple Three inno81
    Banana Four inno81

    That's probably easier to explain and more what folks would expect. It could still be used for your case, too.

Preference?

inno81’s picture

Thanks for your feedback. I do not know how SQL query works, but i'll throw in what would be an ideal situation.
From what I understood from this module, I would expect:

- Behaviour from example 1, if I set "filter" for field "referenced node" and "aggregate" for field "referencing node" AND "referencing author" (i.e. I want a single line for "referenced node", no matter what, pull everything else together).

- Behaviour from example 2, if I set "filter" for "referenced node", and "aggregate" for "referencing node", but nothing for "Referencing author" (i.e. I want a single line for "reference node" if I can pull together the entries from "referencing node". The content of "Referencing author" will duplicate the lines, as usual).

Such behaviour (if feasible from a coding point of view, I have no idea) would leave complete flexibility to the user.
And also what I would expect out the box. I hope other users have the same view (pun intended).

rbrownell’s picture

I too would like to see some improvement to the module in this area. I agree particularly with example 1 above (anything better than what we have now for aggregation would be ideal).

Though I can't help wonder if it is possible to add further flexibility by making the aggregation settings based on another field. That is, in the settings for Field B there is a place where you can select that Field A should be unique and aggregate based on that. Further to improve upon the results of Example 1, there should be also an option to combine like values within the same field so as to prevent duplicates.

CLEE25’s picture

Has there been any movement/fix for this? This is an awesome module, but would really love to have the ability to aggregate relationships fields based on node.

jay.dansand’s picture

Title: Aggregation duplicates aggregated values over entire list » Improve Aggregation Behavior
Version: 7.x-1.0-rc1 » 7.x-1.x-dev
Category: Bug report » Feature request

I haven't had any free time to devote to doing this, but it's still on my to-do list. Alternatively, if anyone gives me a patch, I'd gladly incorporate it with proper attribution :)

I'm still a little confused as to what the ideal action for aggregation should be. Regarding #10, what happens if nothing is marked as "filter," but only one field is marked "aggregate"? Handling that case is not obvious, and we need to pick an algorithm to resolve the questions "what do I aggregate, what do I filter?" from the module's perspective (2 examples of which I tried to detail in #9, though probably badly).

Changing this from bug to feature request (as stated in #9, it's working as designed, that design is just not what people seem to want). Changing to -dev branch, as that's the appropriate version for active development.

Pepper’s picture

I believe this is similar to what I'm experiencing https://drupal.org/node/2283687
I am in dire need of a solution.

inno81’s picture

I think Jay and I consider "aggregate" with opposite meaning:

Setting "Referenced Node" to "aggregated" basically flags it to say "this field should be unique, and if it repeats in results, aggregate the other fields into one row per this field."

I would actually consider that flag to be "filtered". Probably this is the source of the misunderstanding. So the sentence would read:

Setting "Referenced Node" to "filtered" basically flags it to say "this field should be unique, and if it repeats in results, aggregate the other fields into one row per this field."

Edit: I deleted the rest as it made no sense.

inno81’s picture

Actually, re-reading your comment in #9, I think the behaviour 2 you describe for aggregation is actually correct, while the behaviour 1 you describe is my expected behaviour for filter.
To summarise:

A) Filter for "Referenced Node"
Apple | One,Two,Three | jay.dansand,inno81
Banana | Four | inno81
(everything else gets aggregated)

B) Aggregate for "Referencing Node":
Apple | One,Two | jay.dansand
Apple | Three | inno81
Banana | Four | inno81

C) Aggregate for "Referenced Node" and "Referencing Node":
Apple | One,Two | jay.dansand
Apple,Banana | Three,Four | inno81

D) Aggregate for "Referencing Node" and "Referencing Author":
Apple | One,Two,Three | jay.dansand,inno81
Banana | Four | inno81

E) Aggregate for "Referenced Node" and "Referencing Node" and "Referencing author":
Apple,Banana | One,Two,Three,Four | jay.dansand,inno81

This way, "filter" becomes just a quick way to aggregate everything except one column.

jay.dansand’s picture

I think it's important to establish what "filter" does, which I don't think is up for changing. When you mark a field as "filtered duplicates", it means that for all rows in the set, if the field's value repeats, that row is filtered out. Given the example table:

Referenced Node Referencing Node Referencing Author
Apple One jay.dansand
Apple Two jay.dansand
Apple Three inno81
Banana Four inno81

If we filter duplicates on "Referenced Node", the two repeated "Apple" rows are removed.

Referenced Node (filtered) Referencing Node Referencing Author
Apple One jay.dansand
Banana Four inno81

So, really, at least with filtering what we're setting is "what to do with future rows that repeat a value in this field." If we extend that idea to aggregating, then the behavior in example #1 in comment #9 seems more appropriate:

  • Filtered: If any future row repeats the value in this field, then remove it.
  • Aggregated: If any future row repeats the value in this field, then aggregate all of its other rows (basically, instead of deleting the future row, we roll it up into this row).

The deal breaker for me is that with the behavior in example #2, I think it becomes virtually impossible to get the behavior you might want, where for any given column you end up with no duplicates in the column. Since the module is called Views Distinct and is all about eliminating all duplicates in a given column, that seems like a problem to me.

Can we all be happy with behavior #1 in comment #9?

o.feldmanis’s picture

Has this been solved?

I too have the same problem.
I need to show users with the connected content.
Right now the Views Distincts aggregates all of the related content for all the users to all of the users.

This is the needed functionality:

USER RELATED CONTENT
user1 Content 1, Content 2
user2 Content 1, Content 4
user3 Content 3, Content 5

Right now it is:

USER RELATED CONTENT
user1 Content 1, Content 2, Content 3, Content 4, Content 5
user2 Content 1, Content 2, Content 3, Content 4, Content 5
user3 Content 1, Content 2, Content 3, Content 4, Content 5

Is there a way to solve this?

asrob’s picture

I will be happy with behavior #1 in comment 9. I have a view where I display orders. (order number, date/time, username, etc-etc.) But I would like to display referenced products.

Order number Referenced product(s)
23 product 1, product 12
55 product 2
77 product 123, product 3, product 43
cmonnow’s picture

I was a bit perplexed by the output as well since the universal concatenation was not what I expected. I haven't tried the Aggregator Plus module (https://www.drupal.org/project/views_aggregator) but I believe that module restricts you to its own table format (although it has other useful features). From the readme I ascertained it also performs aggregation after SQL execution but I don't know how it handles rendering.

I've hacked together a modification of views_distinct_process_views_view() so that it works as I would expect on post-rendered fields. While ideally I would prefer aggregation at the SQL level (much like the Views module does itself) to allow standard pagination and potentially improve performance I don't know if it will be feasible without re-writing all of the views data and field handlers to loop equivalent rendering functions upon an array of fields.

Right now it would be nonsensical to apply a standard row-based pager since you can end up splitting one record onto different pages. You end up duplicating your filtered base records and would segment the aggregated fields onto different pages. A contextual date pager would still be feasible though.

Without changing the current interface at all, I simply based my assumption of how simple aggregation should work based on how SQL (or even views in-built aggregation) does it. If a selected field isn't being aggregated you need to GROUP BY it (in this module's equivalent case, "filter" it). Filtering will only work properly if you concatenate the values of all the other fields into a hidden global field to simulate a GROUP BY or alternately, use a known unique ID (e.g. filter by nid when you're aggregating only the external fields (we know author, publishing date, body etc are going to be duplicates)). I've moved the aggregation loop within the filtering loop so that the filter group "ids" can be used for more specific aggregation.

Here's the code to switch over. Just make sure the filter (there should only be one - although the code allows more) and aggregators are set for post-render mode.

/**
 * Implements hook_process_views_view().
 *
 * We only use this hook when we need to use rendered output to remove dupes.
 */
function views_distinct_process_views_view(&$vars) {
  // This function used to exist as an implementation of
  // hook_views_post_render(&$view, &$output, &$cache), so pull out pieces of
  // $vars in order to reproduce the previous variables.
  $view = &$vars['view'];
  $output = &$view->display_handler->output;
  // This $cache logic is based on view::render(), which uses this to determine
  // $cache before passing it to hook_views_post_render() implementations.
  $cache = FALSE;
  if (!empty($view->live_preview)) {
    $cache = $view->display_handler->get_plugin('cache');
  }

  // Get the query fields that will need filtering/aggregation:
  $actions = _views_distinct_get_view_actions($view);
  $filter_row_fields = $actions['post_render']['filter_fields'];
  $aggregated_row_fields = $actions['post_render']['aggregated_fields'];

  // Check if we have any action we need to take (there are rows to filter or
  // aggregate):
  if (empty($aggregated_row_fields) && empty($filter_row_fields)) {
    return;
  }

  // Iterate every rendered row and either filter or aggregate it.
  $filter_row_fields_list = array_keys($filter_row_fields);
  // Some style plugins (notably views_plugin_style_summary, which Contextual
  // Filters uses to "display summary") do not support fields
  // (style_plugin->uses_fields() returns FALSE due to uses_row_plugin()
  // returning FALSE, seemingly regardless of the row plugin). In these cases,
  // $rendered_fields is always NULL and we cannot force these to render. Bail.
  if (empty($view->style_plugin->rendered_fields)) {
    return;
  }
  foreach ($view->style_plugin->rendered_fields as $row_index => $row) {   
    //There should only be one filter - that is a concatenation of ALL the fields (using Global text field)
    //that SHOULD NOT be aggregated (or a unique field - only when unchanging fields are known).
    foreach ($filter_row_fields_list as $filter_field_name) {
      $value = $row[$filter_field_name];
			foreach ($aggregated_row_fields as $agg_field_name => &$aggregated_values) {
				if (isset($row[$agg_field_name])) {
					// Add this value to the field's list for aggregation later
					// (we use array keys here to automatically remove dupes; the TRUE
					// value is a dummy value.)
					$aggregated_values['values'][$value][$row[$agg_field_name]] = TRUE;
				}
			}
      if (!empty($filter_row_fields[$filter_field_name][$value])) {
        // This is a repeated row!
        unset($view->style_plugin->row_tokens[$row_index]);
        unset($view->style_plugin->render_tokens[$row_index]);
        unset($view->style_plugin->rendered_fields[$row_index]);
        unset($view->result[$row_index]);
        --$view->total_rows;
      }
      $filter_row_fields[$filter_field_name][$value] = TRUE;
    }
  }

	foreach ($filter_row_fields_list as $filter_field_name) {
		// Now, iterate each remaining result one last time to assign the newly
		// aggregated field values, if any:
		if (!empty($aggregated_row_fields)) {
			foreach ($aggregated_row_fields as $agg_field_name => &$aggregated_values) {
					foreach ($aggregated_values['values'] as $key => $value) {
						$aggregated_values['values'][$key] = implode($aggregated_values['separator'], array_keys($value));
					}
			}
			foreach ($view->style_plugin->rendered_fields as &$row) {
				foreach ($aggregated_row_fields as $agg_field_name => &$aggregated_values) {
					if (isset($row[$agg_field_name]) && isset($row[$filter_field_name])) {
						foreach ($aggregated_values['values'] as $filter_value => $agg_value) {
							if ($row[$filter_field_name] == $filter_value) {
								$row[$agg_field_name] = $agg_value;
								
							}
						}
					}
				}
			}
		}
	}

  // Update the pager, if we're using one.  Note: this only updates the page
  // count that the pager displays, and even that it does not do fully:
  // at most we will only be reducing $view->total_rows by (N - 1) where N is
  // the per-page count of items, which may not affect the "total pages"
  // sufficiently.  For example, if each pager page is showing 10 items, and
  // we aggregate rows 1-9 into row 0, we've removed 9 rows.  If the total
  // results for the query was 100 (even if they all end up being duplicates as
  // well! We can't know at this point), which is 10 pages, our "fixed" result
  // count would be "91", which would still show 10 pages.  In reality, once all
  // dupes are filtered/aggregated, we may only have 2 pages.
  if ($view->query->pager->use_pager()) {
    $view->query->pager->total_items = $view->total_rows;
    $view->query->pager->update_page_info();
    // This logic borrowed from template_preprocess_views_view().
    if (!empty($vars['pager'])) {
      $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL;
      $vars['pager'] = $view->query->render_pager($exposed_input);
    }
  }

  // Since we've changed the post-rendered field output, we need to run render()
  // on $rows again. This will call views_plugin_style::render_grouping(),
  // which in turn calls views_plugin_style::get_field(), which refers to our
  // modified views_plugin_style::rendered_fields[] array values.
  $vars['rows'] = $view->style_plugin->render();
}
inno81’s picture

It might be worth mentioning that another module that provides a solution managing duplicates is Views Merge Rows. It solved my use case.

cmonnow’s picture

Thanks inno81. It's nice to know I was only half-way to re-inventing the wheel instead of the whole thing.

From a quick look at the code it appears to use very similar code and principles, except Views Merge Rows uses hook_views_pre_render and manually callsrender_fields() to work on the post-rendered fields while Views Distinct uses hook_process_views_view (where the view is already post-render).

The issue queue there also has some bugs that I wonder if can be fixed by hooking in elsewhere (although perhaps those bugs are the lesser of two evils). The bugs are probably common though e.g. https://www.drupal.org/node/1836542 vs https://www.drupal.org/node/2103995 (Views data as a service don't work in either due to the absence of rendered_fields array).

jay.dansand’s picture

Thanks for #20. I've rolled your version of views_distinct_process_views_view() into a patch against 7.x-1.x-dev (attached), but have not tested it at all.

jay.dansand’s picture

Just a quick note re: #22, you can see a comment at the top of views_distinct_process_views_view():

  // This function used to exist as an implementation of
  // hook_views_post_render(&$view, &$output, &$cache), so pull out pieces of
  // $vars in order to reproduce the previous variables.

This was changed in commit 388050d and the reasoning was:

Change hook_views_post_render() implementation to hook_process_views_view(), theoretically fixing doubled view::render()ing and corresponding AJAX issues. This change also re-enables support for rendered output caching.

I haven't experimented with hook_views_pre_render enough to know if the same issues would pop up with that method... I can hazard a guess that unless (after manually calling render()) you are very sure to prevent anything else from rendering the same rows, you'll end up double-rendering (and hence breaking some AJAX-ing plugins). In general it feels sort of wrong to render() something in a pre_render() hook, but maybe I'm just a bit biased.

dillix’s picture

#23 doesn't work