2

I am rebuilding an API in Drupal. The structure of the JSON must remain the same.

I am using Views and Draggable Views so the user can order items on an overview page. I Enabled the view for exporting REST JSON.

The response JSON must have a structure like this

{
  items: [{...},{...}]
}

But currently views output is like this

{[{...},{...}]}

I already managed to get my desired output without views and a special export controller but unfortunately, I need draggable views in additions to views. I also managed to create nested structures with Entity Normalizers inside an item but I want the whole output of the view is wrapped in "items". Any help would be appreciated.

2
  • 2
    You can create a new views style plugin based on the json one and just wrap the output with the additional layer.
    – Kevin
    Dec 16, 2019 at 18:01
  • Can't this even be done from a template?
    – leymannx
    Dec 16, 2019 at 18:02

2 Answers 2

2

You can write a views style plugin to get through this fast. Here is an example of that, which adds an outer wrapper of results plus a totalCount item:

<?php

namespace Drupal\mymodule\Plugin\views\style;

use Drupal\rest\Plugin\views\style\Serializer;

/**
 * The style plugin for serialized output formats.
 *
 * @ingroup views_style_plugins
 *
 * @ViewsStyle(
 *   id = "serializer_count",
 *   title = @Translation("Serializer with Count"),
 *   help = @Translation("Serializes views row data using the Serializer component and adds a count."),
 *   display_types = {"data"}
 * )
 */
class SerializerCount extends Serializer {

  /**
   * {@inheritdoc}
   */
  public function render() {
    $rows = [];

    if (isset($this->view->pager)) {
      $count = $this->view->pager->getTotalItems();
    }
    else {
      $count = 0;
    }

    // If the Data Entity row plugin is used, this will be an array of entities
    // which will pass through Serializer to one of the registered Normalizers,
    // which will transform it to arrays/scalars. If the Data field row plugin
    // is used, $rows will not contain objects and will pass directly to the
    // Encoder.
    foreach ($this->view->result as $row_index => $row) {
      $this->view->row_index = $row_index;
      $rows[$row_index] = $this->view->rowPlugin->render($row);
    }

    unset($this->view->row_index);

    // Get the content type configured in the display or fallback to the
    // default.
    if ((empty($this->view->live_preview))) {
      $content_type = $this->displayHandler->getContentType();
    }
    else {
      $content_type = !empty($this->options['formats']) ? reset($this->options['formats']) : 'json';
    }

    return $this->serializer->serialize(['results' => ['items' => $rows, 'totalCount' => (int) $count]], $content_type, ['views_style_plugin' => $this]);
  }

}

Then clear cache, and change the plugin for the View under "Style".

So in your case, the final line would look like:

return $this->serializer->serialize(['items' => $rows], $content_type, ['views_style_plugin' => $this]);
3
  • Thx for the help. Unfortunaly my plugin is not showing up in views under style? I put it in this directory: my_module/src/Plugin/views/style/MySerializer.php but not success. Do i have add some yml definitions etc.?
    – Oliver Pal
    Dec 17, 2019 at 7:31
  • It works now with your code base. And i added my code. I think for the views plugin to work it is really important to have the comment section: /** * The style plugin for serialized output formats. * * @ingroup views_style_plugins * * @ViewsStyle( * id = "serializer_count", * title = @Translation("Serializer with Count"), * help = @Translation("Serializes views row data using the Serializer component and adds a count."), * display_types = {"data"} * ) */
    – Oliver Pal
    Dec 17, 2019 at 7:49
  • Yes, you need the annotation so it is discovered.
    – Kevin
    Dec 17, 2019 at 13:04
1

Drupal's Core Serializer does not provide options to customize the json, nor does it include the pagination data.

The Pager Serializer module does include the pagination information, and the property names are customizable, so your api can remain unchanged.

By default, it produces something like this.

{
  rows: [
    {...},
    {...}
  ],
  pager: {
    current_page: 0,
    total_items: 6,
    total_pages: 2,
    items_per_page: 5,
  }
}

Use the module's configuration form to customize the rows key, and disable the unneeded properties to meet your needs.

{
  items: [
    {...},
    {...}
  ]
}

or if you need the total count as provided in the answer by @kevin.

{
  items: [
    {...},
    {...}
  ],
  totalCount: 6
}

or

{
  items: [
    {...},
    {...}
  ],
  pager: {
    total_items: 6
  }
}

Your Answer

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

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