1

I have a View with a Rest Export display returning entities. I have a custom Normalizer class for it that crafts the object the way I want it in the response.

The node entity in the response has some custom data attached to it coming from an API (think operating hours). I am being asked that if someone clicks on 'Open Now' in the filtering form (not yet created), then anything not open should not be shown in the list.

Is there some way I can add a custom filter or hook into Views and look for this URL parameter and exclude a result from the list if it is not open? hook_views_pre_render and hook_views_post_render don't seem to fire, I assume this is because I am using Serializer format.

1 Answer 1

0

So, I am not 100% confident in this approach, but I got a working solution. The base table is Node, I have no join, this data is not stored locally and I use an API call to fetch the operating hours. So I looked at how the Global filter options worked and started from there with hook_views_data:

In mymodule.module:

/**
 * Implements hook_views_data().
 */
function harlib_restful_normalizer_views_data() {
  $data = [];
  $data['libcal']['table']['group'] = t('LibCal');
  $data['libcal']['table']['join'] = [
    '#global' => [],
  ];

  $data['libcal']['open_now'] = [
    'title' => t('Open Now'),
    'filter' => [
      'id' => 'open_now',
      'title' => t('Currently Open'),
      'help' => t('Filter results by "currently_open" value on results from LibCal Hours API.'),
    ],
  ];

  return $data;
}

In my custom OpenNow filter class:

namespace Drupal\harlib_restful_normalizer\Plugin\views\filter;

use Drupal\harlib_libcal_api\Client\HoursTodayClient;
use Drupal\views\Plugin\views\filter\BooleanOperator;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Filters nodes based on libcal currently open value.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("open_now")
 */
class OpenNow extends BooleanOperator {

  /**
   * @var \Drupal\harlib_libcal_api\Client\HoursTodayClient
   */
  protected $hoursClient;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, HoursTodayClient $client) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->hoursClient = $client;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('harlib_libcal_api.hours_today_client')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function query() {}

  /**
   * {@inheritdoc}
   */
  public function postExecute(&$values) {
    if ($this->value) {
      foreach ($values as $key => $value) {
        $node = $value->_entity;
        $library_id = (int) $node->get('field_library_id')->value;

        if ($library_id === 0) {
          // No times associated with this library.
          unset($values[$key]);
          continue;
        }

        $hours = $this->hoursClient->request($library_id);

        if ($hours['currently_open'] == FALSE) {
          unset($values[$key]);
        }
      }

      $values = array_values($values);
    }

    parent::postExecute($values);
  }

}

If the filter is 'checked' or true, the postExecute method will loop the result set, check the hours returned and unset the result if the location is not currently open. I am not using pagination or anything, so, I did not test if it works in those conditions.

The better scenario is to filter the JSON list on the front end (since the data is already attached to the entity in the response), but I was asked to do it from the backend.

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.