3

I need to write an impossible view and I achieve to retrieve the correct list of nodes in two different views and now I need to merge the results in one single table.

How can I merge it?

If the views_1 give me nodes: [2, 52, 68, 41] and the views_2 give me nodes: [2, 66, 88]
I want ONE table with the nodes: [2, 52, 68, 41, 66, 88]

I can't just merge filter's views because they are based on two relationships so I can't say to require one relationship OR the other one, but only to require one or both relationship.

5

5 Answers 5

3

Output one view as you normally would.

  1. Implement views_pre_render.
  2. Call view::preview on the second view.
  3. Manually merge the results from 2 into 1.
2

To expand on @Letharion's Answer here's an example:

function HOOK_views_pre_render(&$view) {

  if($view->name == 'views_1' && $view->current_display == 'default') {

    //Load views_2
    $view2 = views_get_view('views_2'); //View Name
    $view2->preview('default'); //View Display

    //Check for empty results
    if(empty($view2->result)){
      return;
    }

    $view->result = array_merge($view->result, $view2->result);
  }
}
2
  • 1
    Now with drupal 9 dorea_custom_views_post_execute appears to be the better choice - also to avoid strange caching issues.
    – Andreas
    Commented Sep 28, 2022 at 15:54
  • In Drupal 9, EntityTranslationRendererBase::preRender() already renders the rows, so modifying the $view->results in your own pre_render hook (which is executed after the style plugin's preRender() by ViewExecutable) is too late. hook_views_post_execute is the way to go.
    – cweiske
    Commented Jun 28 at 12:53
0

Here is alternative way of merging results from two impossible views by using hook_views_post_render:

/**
 * Implements hook_views_post_render().
 */
function foo_views_post_render(&$view, &$output, &$cache) {
  if ($view->name == 'my_view_1') {

    // API: http://api.drupal.org/api/views/includes!view.inc/class/view/7
    $view2 = views_get_view('my_view_2');
    $view2->preview('page');
    if (empty($view2->result)) {
      return; // Return on empty results.
    }

    $dom = new DOMdocument();
    $dom->loadHTML($output); // Load HTML from a string into DOM object.

    foreach ($view2->result as $item) {

      // Create field values.
      $row1 = clone $row2 = clone $row3 = clone $row4 = clone $row5 = $dom->createDocumentFragment();
      // TODO: Here, get the values assigned from $item ($view2->result).
      $row2_title = 'same value from view2';
      $url = 'some/url';
      $row1->appendXML('<div>foo</div>');
      $row2->appendXML(l($row2_title, $url)); // Link example.
      $row3->appendXML('<div>foo</div>');
      $row4->appendXML('<div>foo</div>');
      $row5->appendXML('<div>foo</div>');

      // Create columns and append content.
      $td1 = $dom->createElement('td'); // row1
      $td1->appendChild($row1);
      $td2 = $dom->createElement('td'); // row2
      $td2->appendChild($row2);
      $td3 = $dom->createElement('td'); // row3
      $td3->appendChild($row3);
      $td4 = $dom->createElement('td'); // row4
      $td4->appendChild($row4);
      $td5 = $dom->createElement('td'); // row5
      $td5->appendChild($row5);

      // Create row and append columns.
      $tr = $dom->createElement('tr');
      $tr->appendChild($td1)->setAttribute('class', 'views-field views-field-row1');
      $tr->appendChild($td2)->setAttribute('class', 'views-field views-field-row2');
      $tr->appendChild($td3)->setAttribute('class', 'views-field views-field-row3');
      $tr->appendChild($td4)->setAttribute('class', 'views-field views-field-row4');
      $tr->appendChild($td5)->setAttribute('class', 'views-field views-field-row5');

      // Apply changes to table and document.
      $table = $dom->getElementsByTagName('table')->item(0)->getElementsByTagName('tbody')->item(0);
      $table->appendChild($tr);
    }

    $output = $dom->saveHTML();
  }
}

This solution is for table display only and it's adding new items of view2 into view1 results by parsing directly html output.

It has advantage of merging different type of views which doesn't share the same data, not using SQL or proper node objects or using different hooks (e.g. Search API, or YQL View).

0

The below code combines two views based on message table which have different relationships. Same should apply on node table.

/**
 * Implements hook_views_pre_execute().
 */
function MY_MODULE_views_pre_execute(&$view) {
  if ($view->name == 'VIEW_1' && $view->current_display == 'DISPLAY_OF_VIEW_1') {

    $view2 = views_get_view('VIEW_2');
    $view2->build('DISPLAY_OF_VIEW_2');

    $view->build_info['query'] = db_select( //this is for cardinality errrors
      $view->build_info['query']
        ->fields('table_alias', array('timestamp'))
        ->range() //unset offset and limit to set them after the union
        ->union(
          $view2->build_info['query']
            ->fields('table_alias', array('timestamp'))
            ->range()
      ), 'table_alias'
    )
      ->fields('table_alias', array('mid', 'timestamp'))
      ->orderBy('timestamp', 'DESC') //you should order manually
      ->range(
        $view->query->pager->current_page * $view->query->limit, 
        $view->query->limit);

    $view->build_info['count_query'] = $view->build_info['query'];
  };
}

The evolution of above code has been like this:

/**
 * Implements hook_views_pre_execute().
 */
function MY_MODULE_views_pre_execute(&$view) {
  if ($view->name == 'VIEW_1' && $view->current_display == 'DISPLAY_OF_VIEW_1') {

    $view2 = views_get_view('VIEW_2');
    $view2->build('DISPLAY_OF_VIEW_2');

    $view->build_info['query']
      ->fields('table_alias', array('timestamp'))
      ->union(
          $view2->build_info['query']
            ->range()
            ->fields('table_alias', array('timestamp'))
            ->orderBy('timestamp', 'DESC')
      );

    $view->build_info['count_query']
      ->union(
          $view2->build_info['count_query']
          ->range()
      );
  };
}
0

From what I understood you have two different content types, you want to select the content of both of them but the result should be distinct values. Create a view of both content types and set Use aggregation:Yes to create distinct values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.