Skip to content

Instantly share code, notes, and snippets.

@daggerhart
Last active January 27, 2021 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daggerhart/63333cb47d9c8be7caf639b6dcb35bdb to your computer and use it in GitHub Desktop.
Save daggerhart/63333cb47d9c8be7caf639b6dcb35bdb to your computer and use it in GitHub Desktop.
Drupal 8 views style plugin for a better rest serializer that leverages the Facets module.
<?php
namespace Drupal\my_module\Plugin\views\style;
use Drupal\facets_rest\Plugin\views\style\FacetsSerializer;
/**
* The style plugin for serialized output formats.
*
* @ingroup views_style_plugins
*
* @ViewsStyle(
* id = "my_module_facets_serializer",
* title = @Translation("Better Facets Serializer"),
* help = @Translation("Extends existing serializer styles to provide additional view data."),
* display_types = {"data"}
* )
*/
class BetterRestFacetsSerializer extends FacetsSerializer {
/**
* {@inheritdoc}
*/
public function render() {
$rows['search_results'] = $this->getSearchResults();
$rows['facets'] = $this->options['show_facets'] ? $this->getFacets() : [];
$rows['active_facet_items'] = $this->getActiveFacetItems($rows['facets']);
$rows['pager'] = $this->getPagerDetails();
$rows['exposed_filters'] = $this->getExposedHandlers('filter');
$rows['exposed_sorts'] = $this->getExposedHandlers('sort');
// 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($rows, $content_type, ['views_style_plugin' => $this]);
}
/**
* Get an array of active facet items.
*
* @param array $facets
*
* @return array
*/
protected function getActiveFacetItems(array $facets): array {
$active_items = [];
foreach ($facets as $facet) {
if (!empty($facet['items'])) {
foreach ($facet['items'] as $item) {
if (!empty($item['values']['active'])) {
$item['facet_config'] = $facet['config'];
// We need to do some trickier to correctly set static values
// display item values.
if (!empty($item['facet_config']['static_values'])) {
// Break apart the config string in value pairs.
$static_values = \preg_split('/\R+/', $item['facet_config']['static_values']);
foreach ($static_values as $value) {
// Break apart key value pairs.
$key_values = \explode('|', $value);
// If value equates to the active item value, set display.
if ($key_values[0] === $item['values']['value']) {
$item['values']['display_value'] = $key_values[1];
}
}
}
$active_items[] = $item;
}
}
}
}
return $active_items;
}
/**
* Get the search results and process facets.
*
* @see FacetsSerializer::render();
*
* @return array
*/
protected function getSearchResults() {
$rows = [];
// 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) {
// Keep track of the current rendered row, like every style plugin has to
// do.
// @see \Drupal\views\Plugin\views\style\StylePluginBase::renderFields
$this->view->row_index = $row_index;
$rows[] = $this->view->rowPlugin->render($row);
}
// Remove native row index which was throwing off something in the rendering
// and avoid confusion. This is not relevant as the actually rows are being
// stored and rendered elsewhere. Can ask Jonathan to help clarify if need
// be.
unset($this->view->row_index);
return $rows;
}
/**
* Process and return.
*
* @see FacetsSerializer::render();
*
* @return array
*
* @throws \Drupal\facets\Exception\InvalidProcessorException
*/
protected function getFacets() {
// Processing facets.
$facetsource_id = "search_api:views_rest__{$this->view->id()}__{$this->view->getDisplay()->display['id']}";
$facets = $this->facetsManager->getFacetsByFacetSourceId($facetsource_id);
$this->facetsManager->updateResults($facetsource_id);
// Sort facets by weight.
\usort($facets, function ($a, $b) {
return $a->getWeight() > $b->getWeight();
});
$processed_facets = [];
foreach ($facets as $facet) {
$processed = $this->facetsManager->build($facet);
// Ensure facet is prepared and flatten it.
if (isset($processed[0], $processed[0]['config'])) {
$processed_facets[] = $processed[0];
}
}
return $processed_facets;
}
/**
* Get pager and page details.
*
* @link https://www.drupal.org/project/drupal/issues/2982729
*
* @return array
*/
protected function getPagerDetails() {
$details = ['active' => FALSE];
$pager = $this->view->pager;
if ($pager) {
$class = \get_class($pager);
$total_items = $pager->getTotalItems();
$items_per_page = $pager->getItemsPerPage();
$current_page = $pager->getCurrentPage();
$total_pages = 0;
if (!in_array($class, ['Drupal\views\Plugin\views\pager\None', 'Drupal\views\Plugin\views\pager\Some'])) {
$total_pages = $pager->getPagerTotal();
}
$details = [
'active' => TRUE,
'current_page' => $current_page,
'total_items' => $total_items,
'total_pages' => $total_pages,
'items_per_page' => $items_per_page,
'options' => $pager->usesOptions() ? $pager->options : FALSE,
];
}
return $details;
}
/**
* Get exposed handler information and option values.
*
* @param string $type
* ID for a handler type.
*
* @return array
*/
protected function getExposedHandlers(string $type) {
$exposed = [];
$handlers = $this->view->getHandlers($type);
$exposed_input = $this->view->getExposedInput();
foreach ($handlers as $id => $item) {
if ($item['exposed']) {
$info = ['id' => $id];
if (isset($item['expose']['identifier'])) {
$info['submitted_values'] = $exposed_input[$item['expose']['identifier']] ?? [];
}
$info += $item['expose'];
$exposed[] = $info;
}
}
return $exposed;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment