dvf-2.x-dev/dvf_ckan/src/Plugin/Visualisation/Source/CkanResource.php

dvf_ckan/src/Plugin/Visualisation/Source/CkanResource.php
<?php

namespace Drupal\dvf_ckan\Plugin\Visualisation\Source;

use Drupal\ckan_connect\Client\CkanClientInterface;
use Drupal\ckan_connect\Parser\CkanResourceUrlParserInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\dvf\DvfHelpers;
use Drupal\dvf\Plugin\VisualisationInterface;
use Drupal\dvf\Plugin\Visualisation\Source\VisualisationSourceBase;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the 'dvf_ckan_resource' visualisation source.
 *
 * @VisualisationSource(
 *   id = "dvf_ckan_resource",
 *   label = @Translation("CKAN resource"),
 *   category = @Translation("CKAN"),
 *   visualisation_types = {
 *     "dvf_file",
 *     "dvf_url"
 *   }
 * )
 */
class CkanResource extends VisualisationSourceBase implements ContainerFactoryPluginInterface {

  /**
   * The cache backend.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cache;

  /**
   * The CKAN client.
   *
   * @var \Drupal\ckan_connect\Client\CkanClientInterface
   */
  protected $ckanClient;

  /**
   * The CKAN resource URL parser.
   *
   * @var \Drupal\ckan_connect\Parser\CkanResourceUrlParserInterface
   */
  protected $ckanResourceUrlParser;

  /**
   * The CKAN resource fields.
   *
   * @var array
   */
  protected $fields;

  /**
   * The CKAN resource records.
   *
   * @var array
   */
  protected $records;

  /**
   * DVF Helpers.
   *
   * @var \Drupal\dvf\DvfHelpers
   */
  protected $dvfHelpers;

  /**
   * Constructs a new CkanResource.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\dvf\Plugin\VisualisationInterface $visualisation
   *   The visualisation context in which the plugin will run.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Psr\Log\LoggerInterface $logger
   *   Instance of the logger object.
   * @param \GuzzleHttp\Client $http_client
   *   The HTTP client.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache backend.
   * @param \Drupal\ckan_connect\Client\CkanClientInterface $ckan_client
   *   The CKAN client.
   * @param \Drupal\ckan_connect\Parser\CkanResourceUrlParserInterface $ckan_resource_url_parser
   *   The CKAN resource URL parser.
   * @param \Drupal\dvf\DvfHelpers $dvf_helpers
   *   The DVF helpers.
   * @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
   *   The file URL generator.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    VisualisationInterface $visualisation = NULL,
    ModuleHandlerInterface $module_handler,
    LoggerInterface $logger,
    Client $http_client,
    CacheBackendInterface $cache,
    CkanClientInterface $ckan_client,
    CkanResourceUrlParserInterface $ckan_resource_url_parser,
    DvfHelpers $dvf_helpers,
    FileUrlGeneratorInterface $file_url_generator
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $visualisation, $module_handler, $logger, $http_client, $file_url_generator);
    $this->cache = $cache;
    $this->ckanClient = $ckan_client;
    $this->ckanResourceUrlParser = $ckan_resource_url_parser;
    $this->dvfHelpers = $dvf_helpers;
  }

  /**
   * Creates an instance of the plugin.
   *
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   *   The container to pull out services used in the plugin.
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin ID for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\dvf\Plugin\VisualisationInterface $visualisation
   *   The visualisation context in which the plugin will run.
   *
   * @return static
   *   Returns an instance of this plugin.
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
    VisualisationInterface $visualisation = NULL
  ) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $visualisation,
      $container->get('module_handler'),
      $container->get('logger.channel.dvf'),
      $container->get('http_client'),
      $container->get('cache.dvf_ckan'),
      $container->get('ckan_connect.client'),
      $container->get('ckan_connect.resource_url_parser'),
      $container->get('dvf.helpers'),
      $container->get('file_url_generator')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFields() {
    // Generate a cache key for fields.
    $cache_key = $this->getCacheKey('fields');
    $cache_object = $this->cache->get($cache_key);

    // Use cache data if it exists, otherwise fetch fields and store it in cache.
    if (is_object($cache_object)) {
      $raw_fields = $cache_object->data;
    }
    else {
      $raw_fields = $this->fetchFields();
      $this->cache->set($cache_key, $raw_fields, $this->getCacheExpiry());
    }

    $fields = [];

    foreach ($raw_fields as $field) {
      $fields[$field->id] = $field->id;
    }

    return $fields;
  }

  /**
   * Fetches the fields.
   *
   * @return array
   *   An array of CKAN resource fields.
   */
  protected function fetchFields() {
    // Construct a CKAN API query to retrieve fields for the specified resource ID.
    $query = [
      'id' => $this->getResourceId(),
      'limit' => 1,
    ];

    // Merge additional data filters into the CKAN API query.
    $query = array_merge($query, $this->getDataFilters());

    // Use the CKAN client to make a request to 'action/datastore_search'.
    try {
      $response = $this->ckanClient->get('action/datastore_search', $query);
      $result = ($response->success === TRUE) ? $response->result : NULL;
    }
    catch (\Exception $e) {
      $this->ckanResourceError('fields', $query['id'], $e->getMessage());
      $result = NULL;
    }

    // Return the fields obtained from the CKAN datastore or an empty array if there are issues.
    return ($result) ? $result->fields : [];
  }

  /**
   * {@inheritdoc}
   */
  public function getRecords() {
    $cache_key = $this->getCacheKey('records');
    $cache_object = $this->cache->get($cache_key);

    if (is_object($cache_object)) {
      $raw_records = $cache_object->data;
    }
    else {
      $raw_records = $this->fetchRecords();
      $this->cache->set($cache_key, $raw_records, $this->getCacheExpiry());
    }

    $records = [];

    foreach ($this->getFields() as $field_id => $field_label) {
      foreach ($raw_records as $record) {
        if (!isset($records[$record->_id])) {
          $records[$record->_id] = new \stdClass();
        }
        $records[$record->_id]->{$field_id} = $record->{$field_id};
      }
    }

    return $records;
  }

  /**
   * Fetches the records.
   *
   * @param array $records
   *   (optional) The records.
   * @param int $limit
   *   (optional) The maximum number of records per request.
   * @param int $offset
   *   (optional) The number of records to skip.
   *
   * @return array
   *   An array of records.
   */
  protected function fetchRecords(array $records = [], $limit = 100, $offset = 0) {
    $query = [
      'id' => $this->getResourceId(),
      'limit' => $limit,
      'offset' => $offset,
    ];

    $query = array_merge($query, $this->getDataFilters());

    // Try to fetch records from the CKAN datastore using the CKAN client.
    try {
      $response = $this->ckanClient->get('action/datastore_search', $query);
      $result = ($response->success === TRUE) ? $response->result : NULL;
    }
    catch (\Exception $e) {
      $this->ckanResourceError('records', $query['id'], $e->getMessage());
      $result = NULL;
    }

    // Update records, total, and offset based on the fetched result.
    $records = ($result) ? array_merge($records, $result->records) : [];
    $total = ($result) ? $result->total : 0;
    $offset = count($records);

    // If there are more records to fetch (total is greater than the current offset),
    // recursively call fetchRecords with updated parameters.
    if ($total > $offset) {
      return $this->fetchRecords($records, $limit, $offset);
    }
    else {
      return $records;
    }
  }

  /**
   * Gets the CKAN resource ID for this plugin.
   *
   * @return string
   *   The CKAN resource ID.
   */
  protected function getResourceId() {
    return $this->ckanResourceUrlParser->getResourceId($this->config('uri'));
  }

  /**
   * Gets a cache key for a given type.
   *
   * @param string $object_type
   *   The object type.
   *
   * @return string
   *   The cache key.
   */
  protected function getCacheKey($object_type) {
    $plugin_id = hash('sha256', $this->getPluginId());
    $resource_id = $this->getResourceId();

    return $plugin_id . ':' . $resource_id . ':' . $object_type;
  }

  /**
   * Gets the optional data filters and returns them in the correct format.
   *
   * @return array
   *   An array of data filters.
   */
  protected function getDataFilters() {
    // Retrieve configuration options for the visualisation's style.
    $configuration_options = $this->visualisation->getConfiguration('style');
    $data_filters = [];

    // We bail early if data filters are not specified in the visualisation configuration.
    if (empty($configuration_options['style']['options']['data']['data_filters'])) {
      return $data_filters;
    }

    // Extract data filters from the visualisation configuration.
    $data_filters = $configuration_options['style']['options']['data']['data_filters'];

    // Check and validate if 'filters' key exists and contains valid JSON.
    if (empty($data_filters['filters']) || !$this->dvfHelpers->validateJson($data_filters['filters'])) {
      unset($data_filters['filters']);
    }

    if (empty($data_filters['q'])) {
      unset($data_filters['q']);
    }

    return array_map('trim', $data_filters);
  }

  /**
   * Generic error message for errors accessing resources.
   *
   * @param string $type
   *   Error type, eg "records" or "fields".
   * @param string $resource_id
   *   The resource ID that we attempted to access.
   * @param string $exception_message
   *   The exception message.
   */
  protected function ckanResourceError($type, $resource_id, $exception_message) {
    $this->messenger()->addError($this->t('Error displaying CKAN data'));
    $message = $this->t(
      'There was an error getting CKAN :type data for :id. Error :message',
      [
        ':type' => $type,
        ':id' => $resource_id,
        ':message' => $exception_message,
      ]
    );
    $this->logger->error($message);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc