You are here

abstract class Formatter in RESTful 7.2

Same name in this branch
  1. 7.2 src/Annotation/Formatter.php \Drupal\restful\Annotation\Formatter
  2. 7.2 src/Plugin/formatter/Formatter.php \Drupal\restful\Plugin\formatter\Formatter

Class Formatter.

@package Drupal\restful\Plugin\formatter

Hierarchy

Expanded class hierarchy of Formatter

File

src/Plugin/formatter/Formatter.php, line 25
Contains \Drupal\restful\Plugin\formatter\Formatter

Namespace

Drupal\restful\Plugin\formatter
View source
abstract class Formatter extends PluginBase implements FormatterInterface {
  use ConfigurablePluginTrait;

  /**
   * The resource handler containing more info about the request.
   *
   * @var ResourceInterface
   */
  protected $resource;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public function format(array $data) {
    return $this
      ->render($this
      ->prepare($data));
  }

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

    // Default to the most generic content type.
    return 'application/hal+json; charset=utf-8';
  }

  /**
   * {@inheritdoc}
   */
  public function getResource() {
    if (isset($this->resource)) {
      return $this->resource;
    }

    // Get the resource from the instance configuration.
    $instance_configuration = $this
      ->getConfiguration();
    if (empty($instance_configuration['resource'])) {
      return NULL;
    }
    $this->resource = $instance_configuration['resource'] instanceof ResourceInterface ? $instance_configuration['resource'] : NULL;
    return $this->resource;
  }

  /**
   * {@inheritdoc}
   */
  public function setResource(ResourceInterface $resource) {
    $this->resource = $resource;
    $this
      ->setConfiguration(array(
      'resource' => $resource,
    ));
  }

  /**
   * {@inheritdoc}
   */
  public function parseBody($body) {
    throw new ServerConfigurationException(sprintf('Invalid body parser for: %s.', $body));
  }

  /**
   * Helper function to know if a variable is iterable or not.
   *
   * @param mixed $input
   *   The variable to test.
   *
   * @return bool
   *   TRUE if the variable is iterable.
   */
  protected static function isIterable($input) {
    return is_array($input) || $input instanceof \Traversable || $input instanceof \stdClass;
  }

  /**
   * Checks if the passed in data to be rendered can be cached.
   *
   * @param mixed $data
   *   The data to be prepared and rendered.
   *
   * @return bool
   *   TRUE if the data can be cached.
   */
  protected function isCacheEnabled($data) {

    // We are only caching field collections, but you could cache at different
    // layers too.
    if (!$data instanceof ResourceFieldCollectionInterface) {
      return FALSE;
    }
    if (!($context = $data
      ->getContext())) {
      return FALSE;
    }
    return !empty($context['cache_fragments']);
  }

  /**
   * Gets the cached computed value for the fields to be rendered.
   *
   * @param mixed $data
   *   The data to be rendered.
   *
   * @return mixed
   *   The cached data.
   */
  protected function getCachedData($data) {
    if (!($render_cache = $this
      ->createCacheController($data))) {
      return NULL;
    }
    return $render_cache
      ->get();
  }

  /**
   * Gets the cached computed value for the fields to be rendered.
   *
   * @param mixed $data
   *   The data to be rendered.
   *
   * @return string
   *   The cache hash.
   */
  protected function getCacheHash($data) {
    if (!($render_cache = $this
      ->createCacheController($data))) {
      return NULL;
    }
    return $render_cache
      ->getCid();
  }

  /**
   * Gets the cached computed value for the fields to be rendered.
   *
   * @param mixed $data
   *   The data to be rendered.
   * @param mixed $output
   *   The rendered data to output.
   * @param string[] $parent_hashes
   *   An array that holds the name of the parent cache hashes that lead to the
   *   current data structure.
   */
  protected function setCachedData($data, $output, array $parent_hashes = array()) {
    if (!($render_cache = $this
      ->createCacheController($data))) {
      return;
    }
    $render_cache
      ->set($output);

    // After setting the cache for the current object, mark all parent hashes
    // with the current cache fragments. That will have the effect of allowing
    // to clear the parent caches based on the children fragments.
    $fragments = $this
      ->cacheFragments($data);
    foreach ($parent_hashes as $parent_hash) {
      foreach ($fragments as $tag_type => $tag_value) {

        // Check if the fragment already exists.
        $query = new \EntityFieldQuery();
        $duplicate = (bool) $query
          ->entityCondition('entity_type', 'cache_fragment')
          ->propertyCondition('value', $tag_value)
          ->propertyCondition('type', $tag_type)
          ->propertyCondition('hash', $parent_hash)
          ->count()
          ->execute();
        if ($duplicate) {
          continue;
        }
        $cache_fragment = new CacheFragment(array(
          'value' => $tag_value,
          'type' => $tag_type,
          'hash' => $parent_hash,
        ), 'cache_fragment');
        try {
          $cache_fragment
            ->save();
        } catch (\Exception $e) {
          watchdog_exception('restful', $e);
        }
      }
    }
  }

  /**
   * Gets a cache controller based on the data to be rendered.
   *
   * @param mixed $data
   *   The data to be rendered.
   *
   * @return \Drupal\restful\RenderCache\RenderCacheInterface;
   *   The cache controller.
   */
  protected function createCacheController($data) {
    if (!($cache_fragments = $this
      ->cacheFragments($data))) {
      return NULL;
    }

    // Add the formatter fragment because every formatter may prepare the data
    // differently.

    /* @var \Doctrine\Common\Collections\ArrayCollection $cache_fragments */
    $cache_fragments
      ->set('formatter', $this
      ->getPluginId());

    /* @var \Drupal\restful\Plugin\resource\Decorators\CacheDecoratedResource $cached_resource */
    if (!($cached_resource = $this
      ->getResource())) {
      return NULL;
    }
    if (!$cached_resource instanceof CacheDecoratedResourceInterface) {
      return NULL;
    }
    return RenderCache::create($cache_fragments, $cached_resource
      ->getCacheController());
  }

  /**
   * Gets a cache fragments based on the data to be rendered.
   *
   * @param mixed $data
   *   The data to be rendered.
   *
   * @return \Doctrine\Common\Collections\ArrayCollection;
   *   The cache controller.
   */
  protected static function cacheFragments($data) {
    $context = $data
      ->getContext();
    if (!($cache_fragments = $context['cache_fragments'])) {
      return NULL;
    }
    return $cache_fragments;
  }

  /**
   * Returns only the allowed fields by filtering out the other ones.
   *
   * @param mixed $output
   *   The data structure to filter.
   * @param bool|string[] $allowed_fields
   *   FALSE to allow all fields. An array of allowed values otherwise.
   *
   * @return mixed
   *   The filtered output.
   */
  protected function limitFields($output, $allowed_fields = NULL) {
    if (!isset($allowed_fields)) {
      $request = ($resource = $this
        ->getResource()) ? $resource
        ->getRequest() : restful()
        ->getRequest();
      $input = $request
        ->getParsedInput();

      // Set the field limits to false if there are no limits.
      $allowed_fields = empty($input['fields']) ? FALSE : explode(',', $input['fields']);
    }
    if (!is_array($output)) {

      // $output is a simple value.
      return $output;
    }
    $result = array();
    if (ResourceFieldBase::isArrayNumeric($output)) {
      foreach ($output as $item) {
        $result[] = $this
          ->limitFields($item, $allowed_fields);
      }
      return $result;
    }
    foreach ($output as $field_name => $field_contents) {
      if ($allowed_fields !== FALSE && !in_array($field_name, $allowed_fields)) {
        continue;
      }
      $result[$field_name] = $this
        ->limitFields($field_contents, $this
        ->unprefixInputOptions($allowed_fields, $field_name));
    }
    return $result;
  }

  /**
   * Given a prefix, return the allowed fields that apply removing the prefix.
   *
   * @param bool|string[] $allowed_fields
   *   The list of allowed fields in dot notation.
   * @param string $prefix
   *   The prefix used to select the fields and to remove from the front.
   *
   * @return bool|string[]
   *   The new allowed fields for the nested sub-request.
   */
  protected static function unprefixInputOptions($allowed_fields, $prefix) {
    if ($allowed_fields === FALSE) {
      return FALSE;
    }
    $closure_unprefix = function ($field_limit) use ($prefix) {
      if ($field_limit == $prefix) {
        return NULL;
      }
      $pos = strpos($field_limit, $prefix . '.');

      // Remove the prefix from the $field_limit.
      return $pos === 0 ? substr($field_limit, strlen($prefix . '.')) : NULL;
    };
    return array_filter(array_map($closure_unprefix, $allowed_fields));
  }

  /**
   * Helper function that calculates the number of items per page.
   *
   * @param ResourceInterface $resource
   *   The associated resource.
   *
   * @return int
   *   The items per page.
   */
  protected function calculateItemsPerPage(ResourceInterface $resource) {
    $data_provider = $resource
      ->getDataProvider();
    $max_range = $data_provider
      ->getRange();
    $original_input = $resource
      ->getRequest()
      ->getPagerInput();
    $items_per_page = empty($original_input['size']) ? $max_range : $original_input['size'];
    return $items_per_page > $max_range ? $max_range : $items_per_page;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigurablePluginTrait::$instanceConfiguration protected property Plugin instance configuration.
ConfigurablePluginTrait::calculateDependencies public function
ConfigurablePluginTrait::defaultConfiguration public function 1
ConfigurablePluginTrait::getConfiguration public function
ConfigurablePluginTrait::setConfiguration public function
Formatter::$resource protected property The resource handler containing more info about the request.
Formatter::cacheFragments protected static function Gets a cache fragments based on the data to be rendered.
Formatter::calculateItemsPerPage protected function Helper function that calculates the number of items per page.
Formatter::createCacheController protected function Gets a cache controller based on the data to be rendered.
Formatter::format public function Formats the un-structured data into the output format. Overrides FormatterInterface::format
Formatter::getCachedData protected function Gets the cached computed value for the fields to be rendered.
Formatter::getCacheHash protected function Gets the cached computed value for the fields to be rendered.
Formatter::getContentTypeHeader public function Returns the content type for the selected output format. Overrides FormatterInterface::getContentTypeHeader 3
Formatter::getResource public function Gets the underlying resource. Overrides FormatterInterface::getResource
Formatter::isCacheEnabled protected function Checks if the passed in data to be rendered can be cached.
Formatter::isIterable protected static function Helper function to know if a variable is iterable or not.
Formatter::limitFields protected function Returns only the allowed fields by filtering out the other ones.
Formatter::parseBody public function Parses the body string into the common format. Overrides FormatterInterface::parseBody 2
Formatter::setCachedData protected function Gets the cached computed value for the fields to be rendered.
Formatter::setResource public function Sets the underlying resource. Overrides FormatterInterface::setResource
Formatter::unprefixInputOptions protected static function Given a prefix, return the allowed fields that apply removing the prefix.
Formatter::__construct public function
FormatterInterface::prepare public function Massages the raw data to create a structured array to pass to the renderer. 3
FormatterInterface::render public function Renders an array in the selected format. 3