I am seeing several use cases where it would be nice to have ability to get data (indexed results, for example) as JSON output, but in a block/page display output, which can be rendered freely afterwards via theme alterations or theme templates.

For example, I had several issues where I was sending a SINGLE request to a JSON view and then just render the same View output in various formats via theme templates - as a grid of teasers and a Map of results (in D7). In D7 there's https://www.drupal.org/project/views_datasource Views Datasource module which allows to get JSON output. At the same time, it provides an option to specify how to retrieve the output - with JSON content-type header for D8 REST-like output, or as a standard View rendering on the page (the page will render with all the regions and stuff + JSON-encoded view results).

screenshot

Is it possible to add the same kind of feature to REST View Display functionality?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

veronicaSeveryn created an issue. See original summary.

Wim Leers’s picture

Priority: Major » Normal
Status: Active » Postponed (maintainer needs more info)

Thanks for creating this feature request!

Can you clarify what you are requesting by providing an example of what the response you expect would contain exactly?

Wim Leers’s picture

Issue tags: +VDC
veronicaSeveryn’s picture

I really need this feature for D8 projects.. It might be useful for some other people, too.. I don't see a better way to be able to invoke a View execution just once and be able to output results in several different layouts... But I can't figure out how to possibly "adjust" this REST Plugin so that everyone can be happy :)

I think it's more about the presentation of the response. I'll try to explain.

Let's say I have created a View that pulls some data from DB. My final goal is to re-use the SAME VIEW RESULTS I receive from its execution (instead of creating another View or triggering the same View execution twice), but then on the rendering step present the data the way I want it: create HTML output that looks like HTML List <UL> of nodes, for example, and another HTML output that can be presented with a Google Map.

For my request, I still need to be able to get the data in a standard JSON format. The way that I get it with REST export is just fine. The problem I have is that REST plugin renders the output of the View with "application/json" header, thus, all we see as a result is a PAGE with JSON. For my use case, I am trying to eliminate execution of the same View several times just to get the same results from DB and render them differently.

So, let's say if RETS Display mode for the view could have that Configuration Option, for example, like "Views Datasource" from D7, then:

  • 1. In case I want to render the View just like a traditional Page View Display - with all the regions of the page rendered as normal (with the only difference that data from the View execution will be formatted as JSON, instead of some sort HTML list or Table, etc. ) - check this "Views API mode" option ON .
    screenshot2
    Then I will get result like this:
    screenshot1
  • 2. Otherwise, if I uncheck that option, I will get a result as a straight JSON output - no regions or any other page elements will be rendered, just the results from the View execution:
    screenshot3

At the same time, "Views Datasource" from D7 provides a template.tpl.php file for the View Display formatting, where it renders result based on the "Views API mode" option selected. And, of course, I can manipulate the JSON result from the view within template_preprocess:

/**
 * @file views-views-json-style-simple.tpl.php
 * Default template for the Views JSON style plugin using the simple format
 *
 * Variables:
 * - $view: The View object.
 * - $rows: Hierachial array of key=>value pairs to convert to JSON
 * - $options: Array of options for this style
 *
 * @ingroup views_templates
 */

$jsonp_prefix = $options['jsonp_prefix'];

if ($view->override_path) {
  // We're inside a live preview where the JSON is pretty-printed.
  $json = _views_json_encode_formatted($rows, $options);
  if ($jsonp_prefix) $json = "$jsonp_prefix($json)";
  print "$json";
}
else {
  $json = _views_json_json_encode($rows, $bitmask);
  if ($options['remove_newlines']) {
     $json = preg_replace(array('/\\\\n/'), '', $json);
  }

  if (isset($_GET[$jsonp_prefix]) && $jsonp_prefix) {
    $json = check_plain($_GET[$jsonp_prefix]) . '(' . $json . ')';
  }

  if ($options['using_views_api_mode']) {
    // We're in Views API mode.
    print $json;
  }
  else {
    // We want to send the JSON as a server response so switch the content
    // type and stop further processing of the page.
    $content_type = ($options['content_type'] == 'default') ? 'application/json' : $options['content_type'];
    drupal_add_http_header("Content-Type", "$content_type; charset=utf-8");
    print $json;
    drupal_page_footer();
    exit;
  }
}
/**
 * @file
 * Views theme to render view fields as JSON.
 *
 * - $view: The view in use.
 * - $rows: Array of row objects as rendered by _views_json_render_fields
 * - $attachment: Not used currently
 * - $options: The options for the style passed in from the UI.
 *
 * @ingroup views_templates
 * @see views_json.views.inc
 */
function template_preprocess_views_views_json_style_simple(&$vars) {
  $view = $vars["view"];
  $rows = $vars["rows"];
  $options = $vars["options"];
  $base = $view->base_table;
  $root_object = $options["root_object"];
  $top_child_object = $options["top_child_object"];
  $plaintext_output = $options["plaintext_output"];
  $objects = array();

  // Create bitmask for json_encode.
  $option_defs = $vars['view']->style_plugin->option_definition();
  $bitmasks = $option_defs['encoding']['contains'];
  $bitmask = NULL;
  foreach ($bitmasks as $mask_key => $_bitmask) {
    if (isset($options[$mask_key]) && $options[$mask_key] && !is_array($options[$mask_key])) {
      $bitmask = $bitmask | constant($_bitmask['bitmask']);
    }
  }
  $vars['bitmask'] = $bitmask;

  foreach ($rows as $row) {

    $object = array();
    /* Convert the $rows into a hierachial key=>value array */
    foreach ($row as $field) {
      if ($options["field_output"] == "normal") {
        if ($field->label) {
          $label = $plaintext_output ? strip_tags($field->label) : $field->label;
        }
        else {
          $label = $plaintext_output ? strip_tags($field->id) : $field->id;
        }
        if (!$field->is_multiple) {
          if (is_array($field->content)) {
            $content = array();
            foreach ($field->content as $key => $value) {
              $safe = $plaintext_output ? strip_tags(html_entity_decode($value, ENT_QUOTES)) : $value;
              $safe = mb_check_encoding($safe, 'UTF-8') ? $safe : utf8_encode($safe);
              $content[$key] = $safe;
            }
          }
          else {
            $content = $plaintext_output ? strip_tags(html_entity_decode($field->content, ENT_QUOTES)) : $field->content;
            $content = mb_check_encoding($content, 'UTF-8') ? $content : utf8_encode($content);
          }
        }
        else {
          $content = array();
          foreach ($field->content as $n => $oc) {
            if (is_array($oc)) {
              foreach ($oc as $key => $value) {
                $content[$n][$key] = ($plaintext_output ? strip_tags($value) : $value);
              }
            }
            else {
              $content[$n] = ($plaintext_output ? strip_tags($oc) : $oc);
            }
          }
        }
      }
      elseif ($options["field_output"] == "raw") {
        $label = $plaintext_output ? strip_tags($field->id) : $field->id;
        if (!$field->is_multiple) {
          $content = $plaintext_output ? strip_tags($field->raw) : $field->raw;
        }
        else {
          $content = array();
          foreach ($field->raw as $n => $oc) {
            if (is_array($oc)) {
              foreach ($oc as $key => $value) {
                $content[$n][$key] = ($plaintext_output ? strip_tags($value) : $value);
              }
            }
            else {
              $content[$n] = ($plaintext_output ? strip_tags($oc) : $oc);
            }
          }
        }
      }

      // check if user wants nested arrays
      if (strlen($top_child_object) != 0) {
        $object[$top_child_object][$label] = $content;
      }
      else {
        $object[$label] = $content;
      }
    }
    $objects[] = $object;
  }

  // check if user wants nested arrays
  $vars["rows"] = strlen($root_object) != 0 ? array($root_object => $objects) : $objects;
}

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Wim Leers’s picture

Category: Feature request » Support request
Related issues: +#2852272: Make it possible to call jsonapi from within templates

I'm afraid Rendering a JSON block does not make any sense. Blocks are inherently HTML. Perhaps you want to consume the REST API in a block template, so that you don't have to deal with PHP objects/data, but you can directly interact with the REST API?

If so, this very much reminds me of #2852272: Make it possible to call jsonapi from within templates.

This still needs clarifying.

veronicaSeveryn’s picture

Status: Postponed (maintainer needs more info) » Active
FileSize
555.67 KB
738.14 KB

I guess it's not really about displaying it as a "block"..

I guess my point is that I want to be able to display JSON data output for the fields configured in a view, but at the same time render this View as a normal page - all other elements should be rendered (header, footer, etc.).
Think about it like a normal PAGE view display - we render view results with a format chosen (HTML lists, rendered entities, table, etc) + all page elements are rendered normally, the same kind of way would be nice to get JSON data, but still display it like a normal site's page presentation.

Here're a few screenshots for example:

Instead of this:
Only local images are allowed.

I am looking to be able to also switch to a view like this:
Only local images are allowed.

And that's how Drupal 7 Views Datasource module allows to do it on the same View Display, simply by switching configuration "VIEWS API MODE" on/off on a View Display Configuration Form (presented on my very first screenshot for this issue)

Wim Leers’s picture

Assigned: Unassigned » dawehner

I think this needs to be answered by a Views maintainer.

dawehner’s picture

I tried to understand the usecase, but can't do yet.

I really need this feature for D8 projects.. It might be useful for some other people, too..

For me this sounds as if this feature is useful for a small amount of users. Given that, at least for me, it feels like this is better solved in contrib space rather than implemented in core for everyone. Do you know whether implementing this in contrib would be hard?

Wim Leers’s picture

Category: Support request » Feature request
Status: Active » Closed (works as designed)

Agreed with #9, this is a pretty rare edge case, that we should not support in Drupal core. Of course, a contrib module for this would totally make sense!