You are here

class ResourceFieldEntity in RESTful 7.2

Class ResourceFieldEntity.

@package Drupal\restful\Plugin\resource\Field

Hierarchy

Expanded class hierarchy of ResourceFieldEntity

2 files declare their use of ResourceFieldEntity
DataProviderEntity.php in src/Plugin/resource/DataProvider/DataProviderEntity.php
Contains \Drupal\restful\Plugin\resource\DataProvider\DataProviderEntity.
ResourceEntity.php in src/Plugin/resource/ResourceEntity.php
Contains \Drupal\restful\Plugin\resource\ResourceEntity.

File

src/Plugin/resource/Field/ResourceFieldEntity.php, line 27
Contains \Drupal\restful\Plugin\resource\Field\ResourceFieldEntity

Namespace

Drupal\restful\Plugin\resource\Field
View source
class ResourceFieldEntity implements ResourceFieldEntityInterface {

  /**
   * Decorated resource field.
   *
   * @var ResourceFieldInterface
   */
  protected $decorated;

  /**
   * A copy of the underlying property.
   *
   * This is duplicated here for performance reasons.
   *
   * @var string
   */
  protected $property;

  /**
   * A sub property name of a property to take from it the content.
   *
   * This can be used for example on a text field with filtered text input
   * format where we would need to do $wrapper->body->value->value().
   *
   * @var string
   */
  protected $subProperty;

  /**
   * Used for rendering the value of a configurable field using Drupal field
   * API's formatter. The value is the $display value that is passed to
   * field_view_field().
   *
   * @var array
   */
  protected $formatter;

  /**
   * The wrapper's method name to perform on the field. This can be used for
   * example to get the entity label, by setting the value to "label". Defaults
   * to "value".
   *
   * @var string
   */
  protected $wrapperMethod = 'value';

  /**
   * A Boolean to indicate on what to perform the wrapper method. If TRUE the
   * method will perform on the entity (e.g. $wrapper->label()) and FALSE on the
   * property or sub property (e.g. $wrapper->field_reference->label()).
   *
   * @var bool
   */
  protected $wrapperMethodOnEntity = FALSE;

  /**
   * If the property is a field, set the column that would be used in queries.
   * For example, the default column for a text field would be "value". Defaults
   * to the first column returned by field_info_field(), otherwise FALSE.
   *
   * @var string
   */
  protected $column;

  /**
   * Array of image styles to apply to this resource field maps to an image
   * field.
   *
   * @var array
   */
  protected $imageStyles = array();

  /**
   * The entity type.
   *
   * @var string
   */
  protected $entityType;

  /**
   * The bundle name.
   *
   * @var string
   */
  protected $bundle;

  /**
   * Constructor.
   *
   * @param array $field
   *   Contains the field values.
   * @param RequestInterface $request
   *   The request.
   *
   * @throws ServerConfigurationException
   *   If the entity type is empty.
   */
  public function __construct(array $field, RequestInterface $request) {
    if ($this->decorated) {
      $this
        ->setRequest($request);
    }
    if (empty($field['entityType'])) {
      throw new ServerConfigurationException(sprintf('Unknown entity type for %s resource field.', __CLASS__));
    }
    $this
      ->setEntityType($field['entityType']);
    $this->wrapperMethod = isset($field['wrapper_method']) ? $field['wrapper_method'] : $this->wrapperMethod;
    $this->subProperty = isset($field['sub_property']) ? $field['sub_property'] : $this->subProperty;
    $this->formatter = isset($field['formatter']) ? $field['formatter'] : $this->formatter;
    $this->wrapperMethodOnEntity = isset($field['wrapper_method_on_entity']) ? $field['wrapper_method_on_entity'] : $this->wrapperMethodOnEntity;
    $this->column = isset($field['column']) ? $field['column'] : $this->column;
    $this->imageStyles = isset($field['image_styles']) ? $field['image_styles'] : $this->imageStyles;
    if (!empty($field['bundle'])) {

      // TODO: Document this usage.
      $this
        ->setBundle($field['bundle']);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function create(array $field, RequestInterface $request = NULL, ResourceFieldInterface $decorated = NULL) {
    $request = $request ?: restful()
      ->getRequest();
    $resource_field = NULL;
    $class_name = static::fieldClassName($field);

    // If the class exists and is a ResourceFieldEntityInterface use that one.
    if ($class_name && class_exists($class_name) && in_array('Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityInterface', class_implements($class_name))) {
      $resource_field = new $class_name($field, $request);
    }

    // If no specific class was found then use the current one.
    if (!$resource_field) {

      // Create the current object.
      $resource_field = new static($field, $request);
    }
    if (!$resource_field) {
      throw new ServerConfigurationException('Unable to create resource field');
    }

    // Set the basic object to the decorated property.
    $resource_field
      ->decorate($decorated ? $decorated : new ResourceField($field, $request));
    $resource_field->decorated
      ->addDefaults();

    // Add the default specifics for the current object.
    $resource_field
      ->addDefaults();
    return $resource_field;
  }

  /**
   * {@inheritdoc}
   */
  public function value(DataInterpreterInterface $interpreter) {
    $value = $this->decorated
      ->value($interpreter);
    if (isset($value)) {

      // Let the decorated resolve callbacks.
      return $value;
    }

    // Check user has access to the property.
    if (!$this
      ->access('view', $interpreter)) {
      return NULL;
    }
    $property_wrapper = $this
      ->propertyWrapper($interpreter);
    $wrapper = $interpreter
      ->getWrapper();
    if ($property_wrapper instanceof \EntityListWrapper) {
      $values = array();

      // Multiple values.
      foreach ($property_wrapper
        ->getIterator() as $item_wrapper) {
        $values[] = $this
          ->singleValue($item_wrapper, $wrapper, $interpreter
          ->getAccount());
      }
      return $values;
    }
    return $this
      ->singleValue($property_wrapper, $wrapper, $interpreter
      ->getAccount());
  }

  /**
   * {@inheritdoc}
   */
  public function compoundDocumentId(DataInterpreterInterface $interpreter) {
    $collections = $this
      ->render($interpreter);

    // Extract the document ID from the field resource collection.
    $process = function ($collection) {
      if (!$collection instanceof ResourceFieldCollectionInterface) {
        return $collection;
      }
      $id_field = $collection
        ->getIdField();
      return $id_field
        ->render($collection
        ->getInterpreter());
    };

    // If cardinality is 1, then we don't have an array.
    return $this
      ->getCardinality() == 1 ? $process($collections) : array_map($process, array_filter($collections));
  }

  /**
   * Helper function to get the identifier from a property wrapper.
   *
   * @param \EntityMetadataWrapper $property_wrapper
   *   The property wrapper to get the ID from.
   *
   * @return string
   *   An identifier.
   */
  protected function propertyIdentifier(\EntityMetadataWrapper $property_wrapper) {
    if ($property_wrapper instanceof \EntityDrupalWrapper) {

      // The property wrapper is a reference to another entity get the entity
      // ID.
      $identifier = $this
        ->referencedId($property_wrapper);
      $resource = $this
        ->getResource();

      // TODO: Make sure we still want to support fullView.
      if (!$resource || !$identifier || isset($resource['fullView']) && $resource['fullView'] === FALSE) {
        return $identifier;
      }

      // If there is a resource that we are pointing to, we need to use the id
      // field that that particular resource has in its configuration. Trying to
      // load by the entity id in that scenario will lead to a 404.
      // We'll load the plugin to get the idField configuration.
      $instance_id = sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']);

      /* @var ResourceInterface $resource */
      $resource = restful()
        ->getResourceManager()
        ->getPluginCopy($instance_id, Request::create('', array(), RequestInterface::METHOD_GET));
      $plugin_definition = $resource
        ->getPluginDefinition();
      if (empty($plugin_definition['dataProvider']['idField'])) {
        return $identifier;
      }
      try {
        return $property_wrapper->{$plugin_definition['dataProvider']['idField']}
          ->value();
      } catch (\EntityMetadataWrapperException $e) {
        return $identifier;
      }
    }

    // The property is a regular one, get the value out of it and use it as
    // the embedded identifier.
    return $this
      ->fieldValue($property_wrapper);
  }

  /**
   * {@inheritdoc}
   */
  public function set($value, DataInterpreterInterface $interpreter) {
    try {
      $property_wrapper = $interpreter
        ->getWrapper()->{$this
        ->getProperty()};
      $property_wrapper
        ->set($value);
    } catch (\Exception $e) {
      $this->decorated
        ->set($value, $interpreter);
    }
  }

  /**
   * Returns the value for the current single field.
   *
   * This implementation will also add some metadata to the resource field
   * object about the entity it is referencing.
   *
   * @param \EntityMetadataWrapper $property_wrapper
   *   The property wrapper. Either \EntityDrupalWrapper or \EntityListWrapper.
   * @param \EntityDrupalWrapper $wrapper
   *   The entity wrapper.
   * @param object $account
   *   The user account.
   *
   * @return mixed
   *   A single value for the field.
   *
   * @throws \Drupal\restful\Exception\BadRequestException
   * @throws \Drupal\restful\Exception\ServerConfigurationException
   */
  protected function singleValue(\EntityMetadataWrapper $property_wrapper, \EntityDrupalWrapper $wrapper, $account) {
    if ($resource = $this
      ->getResource()) {

      // TODO: The resource input data in the field definition has changed.
      // Now it does not need to be keyed by bundle since you don't even need
      // an entity to use the resource based field.
      $embedded_identifier = $this
        ->propertyIdentifier($property_wrapper);

      // Allow embedding entities with ID 0, like the anon user.
      if (empty($embedded_identifier) && $embedded_identifier !== 0) {
        return NULL;
      }
      if (isset($resource['fullView']) && $resource['fullView'] === FALSE) {
        return $embedded_identifier;
      }

      // We support dot notation for the sparse fieldsets. That means that
      // clients can specify the fields to show based on the "fields" query
      // string parameter.
      $parsed_input = array(
        'fields' => implode(',', $this
          ->nestedDottedChildren('fields')),
        'include' => implode(',', $this
          ->nestedDottedChildren('include')),
        'filter' => $this
          ->nestedDottedChildren('filter'),
      );
      $request = Request::create('', array_filter($parsed_input), RequestInterface::METHOD_GET);

      // Get a plugin (that can be altered with decorators.
      $embedded_resource = restful()
        ->getResourceManager()
        ->getPluginCopy(sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']));

      // Configure the plugin copy with the sub-request and sub-path.
      $embedded_resource
        ->setPath($embedded_identifier);
      $embedded_resource
        ->setRequest($request);
      $embedded_resource
        ->setAccount($account);
      $metadata = $this
        ->getMetadata($wrapper
        ->getIdentifier());
      $metadata = $metadata ?: array();
      $metadata[] = $this
        ->buildResourceMetadataItem($property_wrapper);
      $this
        ->addMetadata($wrapper
        ->getIdentifier(), $metadata);
      try {

        // Get the contents to embed in place of the reference ID.

        /* @var ResourceFieldCollection $embedded_entity */
        $embedded_entity = $embedded_resource
          ->getDataProvider()
          ->view($embedded_identifier);
      } catch (InaccessibleRecordException $e) {

        // If you don't have access to the embedded entity is like not having
        // access to the property.
        return NULL;
      } catch (UnprocessableEntityException $e) {

        // If you access a nonexistent embedded entity.
        return NULL;
      }

      // Test if the $embedded_entity meets the filter or not.
      if (empty($parsed_input['filter'])) {
        return $embedded_entity;
      }
      foreach ($parsed_input['filter'] as $filter) {

        // Filters only apply if the target is the current field.
        if (!empty($filter['target']) && $filter['target'] == $this
          ->getPublicName() && !$embedded_entity
          ->evalFilter($filter)) {

          // This filter is not met.
          return NULL;
        }
      }
      return $embedded_entity;
    }
    if ($this
      ->getFormatter()) {

      // Get value from field formatter.
      $value = $this
        ->formatterValue($property_wrapper, $wrapper);
    }
    else {

      // Single value.
      $value = $this
        ->fieldValue($property_wrapper);
    }
    return $value;
  }

  /**
   * {@inheritdoc}
   *
   * @throws \EntityMetadataWrapperException
   */
  public function access($op, DataInterpreterInterface $interpreter) {

    // Perform basic access checks.
    if (!$this->decorated
      ->access($op, $interpreter)) {
      return FALSE;
    }
    if (!$this
      ->getProperty()) {

      // If there is no property we cannot check for property access.
      return TRUE;
    }

    // Perform field API access checks.
    if (!($property_wrapper = $this
      ->propertyWrapper($interpreter))) {
      return FALSE;
    }
    if ($this
      ->isWrapperMethodOnEntity() && $this
      ->getWrapperMethod() && $this
      ->getProperty()) {

      // Sometimes we define fields as $wrapper->getIdentifier. We need to
      // resolve that to $wrapper->nid to call $wrapper->nid->info().
      $property_wrapper = $property_wrapper->{$this
        ->getProperty()};
    }
    $account = $interpreter
      ->getAccount();

    // Check format access for text fields.
    if ($op == 'edit' && $property_wrapper
      ->type() == 'text_formatted' && $property_wrapper
      ->value() && $property_wrapper->format
      ->value()) {
      $format = (object) array(
        'format' => $property_wrapper->format
          ->value(),
      );

      // Only check filter access on write contexts.
      if (!filter_access($format, $account)) {
        return FALSE;
      }
    }
    $info = $property_wrapper
      ->info();
    if ($op == 'edit' && empty($info['setter callback'])) {

      // Property does not allow setting.
      return FALSE;
    }

    // If $interpreter->getWrapper()->value() === FALSE it means that the entity
    // could not be loaded, thus checking properties on it will result in
    // errors.
    // Ex: this happens when the embedded author is the anonymous user. Doing
    // user_load(0) returns FALSE.
    $access = $interpreter
      ->getWrapper()
      ->value() !== FALSE && $property_wrapper
      ->access($op, $account);
    return $access !== FALSE;
  }

  /**
   * Get the wrapper for the property associated to the current field.
   *
   * @param DataInterpreterInterface $interpreter
   *   The data source.
   *
   * @return \EntityMetadataWrapper
   *   Either a \EntityStructureWrapper or a \EntityListWrapper.
   *
   * @throws ServerConfigurationException
   */
  protected function propertyWrapper(DataInterpreterInterface $interpreter) {

    // This is the first method that gets called for all fields after loading
    // the entity. We'll use that opportunity to set the actual bundle of the
    // field.
    $this
      ->setBundle($interpreter
      ->getWrapper()
      ->getBundle());

    // Exposing an entity field.
    $wrapper = $interpreter
      ->getWrapper();

    // For entity fields the DataInterpreter needs to contain an EMW.
    if (!$wrapper instanceof \EntityDrupalWrapper) {
      throw new ServerConfigurationException('Cannot get a value without an entity metadata wrapper data source.');
    }
    $property = $this
      ->getProperty();
    try {
      return $property && !$this
        ->isWrapperMethodOnEntity() ? $wrapper->{$property} : $wrapper;
    } catch (\EntityMetadataWrapperException $e) {
      throw new UnprocessableEntityException(sprintf('The property %s could not be found in %s:%s.', $property, $wrapper
        ->type(), $wrapper
        ->getBundle()));
    }
  }

  /**
   * Get value from a property.
   *
   * @param \EntityMetadataWrapper $property_wrapper
   *   The property wrapper. Either \EntityDrupalWrapper or \EntityListWrapper.
   *
   * @return mixed
   *   A single or multiple values.
   */
  protected function fieldValue(\EntityMetadataWrapper $property_wrapper) {
    if ($this
      ->getSubProperty() && $property_wrapper
      ->value()) {
      $property_wrapper = $property_wrapper->{$this
        ->getSubProperty()};
    }

    // Wrapper method.
    return $property_wrapper
      ->{$this
      ->getWrapperMethod()}();
  }

  /**
   * Get value from a field rendered by Drupal field API's formatter.
   *
   * @param \EntityMetadataWrapper $property_wrapper
   *   The property wrapper. Either \EntityDrupalWrapper or \EntityListWrapper.
   * @param \EntityDrupalWrapper $wrapper
   *   The entity wrapper.
   *
   * @return mixed
   *   A single or multiple values.
   *
   * @throws \Drupal\restful\Exception\ServerConfigurationException
   */
  protected function formatterValue(\EntityMetadataWrapper $property_wrapper, \EntityDrupalWrapper $wrapper) {
    $value = NULL;
    if (!ResourceFieldEntity::propertyIsField($this
      ->getProperty())) {

      // Property is not a field.
      throw new ServerConfigurationException(format_string('@property is not a configurable field, so it cannot be processed using field API formatter', array(
        '@property' => $this
          ->getProperty(),
      )));
    }

    // Get values from the formatter.
    $output = field_view_field($this
      ->getEntityType(), $wrapper
      ->value(), $this
      ->getProperty(), $this
      ->getFormatter());

    // Unset the theme, as we just want to get the value from the formatter,
    // without the wrapping HTML.
    unset($output['#theme']);
    if ($property_wrapper instanceof \EntityListWrapper) {

      // Multiple values.
      foreach (element_children($output) as $delta) {
        $value[] = drupal_render($output[$delta]);
      }
    }
    else {

      // Single value.
      $value = drupal_render($output);
    }
    return $value;
  }

  /**
   * Get the children of a query string parameter that apply to the field.
   *
   * For instance: if the field is 'relatedArticles' and the query string is
   * '?relatedArticles.one.two,articles' it returns array('one.two').
   *
   * @param string $key
   *   The name of the key: include|fields
   *
   * @return string[]
   *   The list of fields.
   */
  protected function nestedDottedChildren($key) {

    // Filters are dealt with differently.
    if ($key == 'filter') {
      return $this
        ->nestedDottedFilters();
    }
    $allowed_values = array(
      'include',
      'fields',
    );
    if (!in_array($key, $allowed_values)) {
      return array();
    }
    $input = $this
      ->getRequest()
      ->getParsedInput();
    $limit_values = !empty($input[$key]) ? explode(',', $input[$key]) : array();
    $limit_values = array_filter($limit_values, function ($value) {
      $parts = explode('.', $value);
      return $parts[0] == $this
        ->getPublicName() && $value != $this
        ->getPublicName();
    });
    return array_map(function ($value) {
      return substr($value, strlen($this
        ->getPublicName()) + 1);
    }, $limit_values);
  }

  /**
   * Process the filter query string for the relevant sub-query.
   *
   * Selects the filters that start with the field name.
   *
   * @return array
   *   The processed filters.
   */
  protected function nestedDottedFilters() {
    $input = $this
      ->getRequest()
      ->getParsedInput();
    if (empty($input['filter'])) {
      return array();
    }
    $output_filters = array();
    $filters = $input['filter'];
    foreach ($filters as $filter_public_name => $filter) {
      $filter = DataProvider::processFilterInput($filter, $filter_public_name);
      if (strpos($filter_public_name, $this
        ->getPublicName() . '.') === 0) {

        // Remove the prefix and add it to the filters for the next request.
        $new_name = substr($filter_public_name, strlen($this
          ->getPublicName()) + 1);
        $filter['public_field'] = $new_name;
        $output_filters[$new_name] = $filter;
      }
    }
    return $output_filters;
  }

  /**
   * {@inheritdoc}
   */
  public function addMetadata($key, $value) {
    $this->decorated
      ->addMetadata($key, $value);
  }

  /**
   * {@inheritdoc}
   */
  public function getMetadata($key) {
    return $this->decorated
      ->getMetadata($key);
  }

  /**
   * {@inheritdoc}
   */
  public function getRequest() {
    return $this->decorated
      ->getRequest();
  }

  /**
   * {@inheritdoc}
   */
  public function setRequest(RequestInterface $request) {
    $this->decorated
      ->setRequest($request);
  }

  /**
   * {@inheritdoc}
   */
  public function executeProcessCallbacks($value) {
    return $this->decorated
      ->executeProcessCallbacks($value);
  }

  /**
   * {@inheritdoc}
   */
  public function render(DataInterpreterInterface $interpreter) {
    return $this
      ->executeProcessCallbacks($this
      ->value($interpreter));
  }

  /**
   * {@inheritdoc}
   */
  public function getDefinition() {
    return $this->decorated
      ->getDefinition();
  }

  /**
   * {@inheritdoc}
   */
  public function getPublicFieldInfo() {
    return $this->decorated
      ->getPublicFieldInfo();
  }

  /**
   * {@inheritdoc}
   */
  public function setPublicFieldInfo(PublicFieldInfoInterface $public_field_info) {
    $this->decorated
      ->setPublicFieldInfo($public_field_info);
  }

  /**
   * Get value for a field based on another resource.
   *
   * @param DataInterpreterInterface $source
   *   The data source.
   *
   * @return mixed
   *   A single or multiple values.
   */
  protected function resourceValue(DataInterpreterInterface $source) {
  }

  /**
   * {@inheritdoc}
   */
  public function decorate(ResourceFieldInterface $decorated) {
    $this->decorated = $decorated;
  }

  /**
   * {@inheritdoc}
   */
  public function getSubProperty() {
    return $this->subProperty;
  }

  /**
   * {@inheritdoc}
   */
  public function setSubProperty($sub_property) {
    $this->subProperty = $sub_property;
  }

  /**
   * {@inheritdoc}
   */
  public function getFormatter() {
    return $this->formatter;
  }

  /**
   * {@inheritdoc}
   */
  public function setFormatter($formatter) {
    $this->formatter = $formatter;
  }

  /**
   * {@inheritdoc}
   */
  public function getWrapperMethod() {
    return $this->wrapperMethod;
  }

  /**
   * {@inheritdoc}
   */
  public function setWrapperMethod($wrapper_method) {
    $this->wrapperMethod = $wrapper_method;
  }

  /**
   * {@inheritdoc}
   */
  public function isWrapperMethodOnEntity() {
    return $this->wrapperMethodOnEntity;
  }

  /**
   * {@inheritdoc}
   */
  public function setWrapperMethodOnEntity($wrapper_method_on_entity) {
    $this->wrapperMethodOnEntity = $wrapper_method_on_entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getColumn() {
    if (isset($this->column)) {
      return $this->column;
    }
    if ($this
      ->getProperty() && ($field = $this::fieldInfoField($this
      ->getProperty()))) {
      if ($field['type'] == 'text_long') {

        // Do not default to format.
        $this
          ->setColumn('value');
      }
      else {

        // Set the column name.
        $this
          ->setColumn(key($field['columns']));
      }
    }
    return $this->column;
  }

  /**
   * {@inheritdoc}
   */
  public function setColumn($column) {
    $this->column = $column;
  }

  /**
   * {@inheritdoc}
   */
  public function getImageStyles() {
    return $this->imageStyles;
  }

  /**
   * {@inheritdoc}
   */
  public function setImageStyles($image_styles) {
    $this->imageStyles = $image_styles;
  }

  /**
   * {@inheritdoc}
   */
  public function getEntityType() {
    return $this->entityType;
  }

  /**
   * {@inheritdoc}
   */
  public function setEntityType($entity_type) {
    $this->entityType = $entity_type;
  }

  /**
   * Gets the \EntityStructureWrapper for the entity type.
   *
   * @return mixed
   *   The \EntityStructureWrapper if the entity type exists.
   */
  protected function entityTypeWrapper() {
    static $entity_wrappers = array();
    $key = sprintf('%s:%s', $this
      ->getEntityType(), $this
      ->getBundle());
    if (isset($entity_wrappers[$key])) {
      return $entity_wrappers[$key];
    }
    $entity_wrappers[$key] = entity_metadata_wrapper($this
      ->getEntityType(), NULL, array(
      'bundle' => $this
        ->getBundle(),
    ));
    return $entity_wrappers[$key];
  }

  /**
   * {@inheritdoc}
   */
  public function getBundle() {
    return $this->bundle;
  }

  /**
   * {@inheritdoc}
   */
  public function setBundle($bundle) {

    // Do not do pointless work if not needed.
    if (!empty($this->bundle) && $this->bundle == $bundle) {
      return;
    }
    $this->bundle = $bundle;

    // If this is an options call, then introspect Entity API to add more data
    // to the public field information.
    if ($this
      ->getRequest()
      ->getMethod() == RequestInterface::METHOD_OPTIONS) {
      $this
        ->populatePublicInfoField();
    }
  }

  /**
   * {@inheritdoc}
   *
   * Almost all the defaults come are applied by the object's property defaults.
   */
  public function addDefaults() {

    // Set the defaults from the decorated.
    $this
      ->setResource($this->decorated
      ->getResource());

    // If entity metadata wrapper methods were used, then return the appropriate
    // entity property.
    if ($this
      ->isWrapperMethodOnEntity() && $this
      ->getWrapperMethod()) {
      $this
        ->propertyOnEntity();
    }

    // Set the Entity related defaults.
    if (($this->property = $this->decorated
      ->getProperty()) && ($field = $this::fieldInfoField($this->property)) && $field['type'] == 'image' && ($image_styles = $this
      ->getImageStyles())) {

      // If it's an image check if we need to add image style processing.
      $process_callbacks = $this
        ->getProcessCallbacks();
      array_unshift($process_callbacks, array(
        array(
          $this,
          'getImageUris',
        ),
        array(
          $image_styles,
        ),
      ));
      $this
        ->setProcessCallbacks($process_callbacks);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getImageUris(array $file_array, $image_styles) {

    // Return early if there are no image styles.
    if (empty($image_styles)) {
      return $file_array;
    }

    // If $file_array is an array of file arrays. Then call recursively for each
    // item and return the result.
    if (static::isArrayNumeric($file_array)) {
      $output = array();
      foreach ($file_array as $item) {
        $output[] = static::getImageUris($item, $image_styles);
      }
      return $output;
    }
    $file_array['image_styles'] = array();
    foreach ($image_styles as $style) {
      $file_array['image_styles'][$style] = image_style_url($style, $file_array['uri']);
    }
    return $file_array;
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyIsField($name) {
    return (bool) static::fieldInfoField($name);
  }

  /**
   * {@inheritdoc}
   */
  public function preprocess($value) {

    // By default assume that there is no preprocess and allow extending classes
    // to implement this.
    return $value;
  }

  /**
   * Get the class name to use based on the field definition.
   *
   * @param array $field_definition
   *   The processed field definition with the user values.
   *
   * @return string
   *   The class name to use. If the class name is empty or does not implement
   *   ResourceFieldInterface then ResourceField will be used. NULL if nothing
   *   was found.
   */
  public static function fieldClassName(array $field_definition) {
    if (!empty($field_definition['class']) && $field_definition['class'] != '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntity') {

      // If there is a class that is not the current, return it.
      return $field_definition['class'];
    }

    // If there is an extending class for the particular field use that class
    // instead.
    if (empty($field_definition['property']) || !($field_info = static::fieldInfoField($field_definition['property']))) {
      return NULL;
    }
    switch ($field_info['type']) {
      case 'entityreference':
      case 'taxonomy_term_reference':
        return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityReference';
      case 'text':
      case 'text_long':
      case 'text_with_summary':
        return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityText';
      case 'file':
      case 'image':

        // If the field is treated as a resource, then default to the reference.
        if (!empty($field_definition['resource'])) {
          return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldFileEntityReference';
        }
        return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityFile';
      default:
        return NULL;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getPublicName() {
    return $this->decorated
      ->getPublicName();
  }

  /**
   * {@inheritdoc}
   */
  public function setPublicName($public_name) {
    $this->decorated
      ->setPublicName($public_name);
  }

  /**
   * {@inheritdoc}
   */
  public function getAccessCallbacks() {
    return $this->decorated
      ->getAccessCallbacks();
  }

  /**
   * {@inheritdoc}
   */
  public function setAccessCallbacks($access_callbacks) {
    $this->decorated
      ->setAccessCallbacks($access_callbacks);
  }

  /**
   * {@inheritdoc}
   */
  public function getProperty() {
    return $this->property;
  }

  /**
   * {@inheritdoc}
   */
  public function setProperty($property) {
    $this->property = $property;
    $this->decorated
      ->setProperty($property);
  }

  /**
   * {@inheritdoc}
   */
  public function getCallback() {
    return $this->decorated
      ->getCallback();
  }

  /**
   * {@inheritdoc}
   */
  public function setCallback($callback) {
    $this->decorated
      ->setCallback($callback);
  }

  /**
   * {@inheritdoc}
   */
  public function getProcessCallbacks() {
    return $this->decorated
      ->getProcessCallbacks();
  }

  /**
   * {@inheritdoc}
   */
  public function setProcessCallbacks($process_callbacks) {
    $this->decorated
      ->setProcessCallbacks($process_callbacks);
  }

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

  /**
   * {@inheritdoc}
   */
  public function setResource($resource) {
    $this->decorated
      ->setResource($resource);
  }

  /**
   * {@inheritdoc}
   */
  public function getMethods() {
    return $this->decorated
      ->getMethods();
  }

  /**
   * {@inheritdoc}
   */
  public function setMethods($methods) {
    $this->decorated
      ->setMethods($methods);
  }

  /**
   * {@inheritdoc}
   */
  public function id() {
    return $this->decorated
      ->id();
  }

  /**
   * {@inheritdoc}
   */
  public function isComputed() {
    return $this->decorated
      ->isComputed();
  }

  /**
   * {@inheritdoc}
   */
  public function autoDiscovery() {
    if (method_exists($this->decorated, 'autoDiscovery')) {
      return $this->decorated
        ->autoDiscovery();
    }
    return ResourceFieldBase::emptyDiscoveryInfo($this
      ->getPublicName());
  }

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

    // Default to single cardinality.
    $this->cardinality = 1;
    if ($field_info = $this::fieldInfoField($this
      ->getProperty())) {
      $this->cardinality = empty($field_info['cardinality']) ? $this->cardinality : $field_info['cardinality'];
    }
    return $this->cardinality;
  }

  /**
   * {@inheritdoc}
   */
  public function setCardinality($cardinality) {
    $this->cardinality = $cardinality;
  }

  /**
   * Helper method to determine if an array is numeric.
   *
   * @param array $input
   *   The input array.
   *
   * @return bool
   *   TRUE if the array is numeric, false otherwise.
   */
  public static function isArrayNumeric(array $input) {
    return ResourceFieldBase::isArrayNumeric($input);
  }

  /**
   * Builds a metadata item for a field value.
   *
   * It will add information about the referenced entity. NOTE: Do not type hint
   * the $wrapper argument to avoid PHP errors for the file entities. Those are
   * no true entity references, but file arrays (although they reference file
   * entities)
   *
   * @param \EntityDrupalWrapper $wrapper
   *   The wrapper to the referenced entity.
   *
   * @return array
   *   The metadata array item.
   */
  protected function buildResourceMetadataItem($wrapper) {
    if ($wrapper instanceof \EntityValueWrapper) {
      $wrapper = entity_metadata_wrapper($this
        ->getEntityType(), $wrapper
        ->value());
    }
    $id = $wrapper
      ->getIdentifier();
    $bundle = $wrapper
      ->getBundle();
    $resource = $this
      ->getResource();
    return array(
      'id' => $id,
      'entity_type' => $wrapper
        ->type(),
      'bundle' => $bundle,
      'resource_name' => $resource['name'],
    );
  }

  /**
   * Helper function to get the referenced entity ID.
   *
   * @param \EntityDrupalWrapper $property_wrapper
   *   The wrapper for the referenced file array.
   *
   * @return mixed
   *   The ID.
   */
  protected function referencedId($property_wrapper) {
    return $property_wrapper
      ->getIdentifier() ?: NULL;
  }

  /**
   * Sets the resource field property to the schema field in the entity.
   *
   * @throws \EntityMetadataWrapperException
   */
  protected function propertyOnEntity() {

    // If there is no property try to get it based on the wrapper method and
    // store the value in the decorated object.
    $property = NULL;
    $wrapper_method = $this
      ->getWrapperMethod();
    $wrapper = $this
      ->entityTypeWrapper();
    if ($wrapper_method == 'label') {

      // Store the label key.
      $property = $wrapper
        ->entityKey('label');
    }
    elseif ($wrapper_method == 'getBundle') {

      // Store the bundle key.
      $property = $wrapper
        ->entityKey('bundle');
    }
    elseif ($wrapper_method == 'getIdentifier') {

      // Store the ID key.
      $property = $wrapper
        ->entityKey('id');
    }

    // There are occasions when the wrapper property is not the schema
    // database field.
    if (!is_a($wrapper, '\\EntityStructureWrapper')) {

      // The entity type does not exist.
      return;
    }

    /* @var $wrapper \EntityStructureWrapper */
    foreach ($wrapper
      ->getPropertyInfo() as $wrapper_property => $property_info) {
      if (!empty($property_info['schema field']) && $property_info['schema field'] == $property) {
        $property = $wrapper_property;
        break;
      }
    }
    $this
      ->setProperty($property);
  }

  /**
   * Populate public info field with Property API information.
   */
  protected function populatePublicInfoField() {
    $field_definition = $this
      ->getDefinition();
    $discovery_info = empty($field_definition['discovery']) ? array() : $field_definition['discovery'];
    $public_field_info = new PublicFieldInfoEntity($this
      ->getPublicName(), $this
      ->getProperty(), $this
      ->getEntityType(), $this
      ->getBundle(), $discovery_info);
    $this
      ->setPublicFieldInfo($public_field_info);
    if ($field_instance = field_info_instance($this
      ->getEntityType(), $this
      ->getProperty(), $this
      ->getBundle())) {
      $public_field_info
        ->addSectionDefaults('info', array(
        'label' => $field_instance['label'],
        'description' => $field_instance['description'],
      ));
      $field_info = $this::fieldInfoField($this
        ->getProperty());
      $section_info = array();
      $section_info['label'] = empty($field_info['label']) ? NULL : $field_info['label'];
      $section_info['description'] = empty($field_info['description']) ? NULL : $field_info['description'];
      $public_field_info
        ->addSectionDefaults('info', $section_info);
      $type = $public_field_info instanceof PublicFieldInfoEntityInterface ? $public_field_info
        ->getFormSchemaAllowedType() : NULL;
      $public_field_info
        ->addSectionDefaults('form_element', array(
        'default_value' => isset($field_instance['default_value']) ? $field_instance['default_value'] : NULL,
        'type' => $type,
      ));

      // Loading allowed values can be a performance issue, load them only if
      // they are not provided in the field definition.
      $form_element_info = $public_field_info
        ->getSection('form_element');
      if (!isset($form_element_info['allowed_values'])) {
        $allowed_values = $public_field_info instanceof PublicFieldInfoEntityInterface ? $public_field_info
          ->getFormSchemaAllowedValues() : NULL;
        $public_field_info
          ->addSectionDefaults('form_element', array(
          'allowed_values' => $allowed_values,
        ));
      }
    }
    else {

      // Extract the discovery information from the property info.
      try {
        $property_info = $this
          ->entityTypeWrapper()
          ->getPropertyInfo($this
          ->getProperty());
      } catch (\EntityMetadataWrapperException $e) {
        return;
      }
      if (empty($property_info)) {
        return;
      }
      $public_field_info
        ->addSectionDefaults('data', array(
        'type' => $property_info['type'],
        'required' => empty($property_info['required']) ? FALSE : $property_info['required'],
      ));
      $public_field_info
        ->addSectionDefaults('info', array(
        'label' => $property_info['label'],
        'description' => $property_info['description'],
      ));
    }
  }

  /**
   * Gets statically cached information about a field.
   *
   * @param string $field_name
   *   The name of the field to retrieve. $field_name can only refer to a
   *   non-deleted, active field. For deleted fields, use
   *   field_info_field_by_id(). To retrieve information about inactive fields,
   *   use field_read_fields().
   *
   * @return array
   *   The field info.
   *
   * @see field_info_field()
   */
  protected static function fieldInfoField($field_name) {
    return field_info_field($field_name);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ResourceFieldEntity::$bundle protected property The bundle name.
ResourceFieldEntity::$column protected property If the property is a field, set the column that would be used in queries. For example, the default column for a text field would be "value". Defaults to the first column returned by field_info_field(), otherwise FALSE.
ResourceFieldEntity::$decorated protected property Decorated resource field.
ResourceFieldEntity::$entityType protected property The entity type.
ResourceFieldEntity::$formatter protected property Used for rendering the value of a configurable field using Drupal field API's formatter. The value is the $display value that is passed to field_view_field().
ResourceFieldEntity::$imageStyles protected property Array of image styles to apply to this resource field maps to an image field.
ResourceFieldEntity::$property protected property A copy of the underlying property.
ResourceFieldEntity::$subProperty protected property A sub property name of a property to take from it the content.
ResourceFieldEntity::$wrapperMethod protected property The wrapper's method name to perform on the field. This can be used for example to get the entity label, by setting the value to "label". Defaults to "value".
ResourceFieldEntity::$wrapperMethodOnEntity protected property A Boolean to indicate on what to perform the wrapper method. If TRUE the method will perform on the entity (e.g. $wrapper->label()) and FALSE on the property or sub property (e.g. $wrapper->field_reference->label()).
ResourceFieldEntity::access public function Overrides ResourceFieldInterface::access
ResourceFieldEntity::addDefaults public function Almost all the defaults come are applied by the object's property defaults. Overrides ResourceFieldInterface::addDefaults
ResourceFieldEntity::addMetadata public function Add metadata to the field. Overrides ResourceFieldInterface::addMetadata
ResourceFieldEntity::autoDiscovery public function
ResourceFieldEntity::buildResourceMetadataItem protected function Builds a metadata item for a field value. 1
ResourceFieldEntity::compoundDocumentId public function Fetches the embedded identifier(s) for the current resource field, if any. Overrides ResourceFieldInterface::compoundDocumentId
ResourceFieldEntity::create public static function Factory. Overrides ResourceFieldInterface::create
ResourceFieldEntity::decorate public function Decorate the object. Overrides ResourceFieldEntityInterface::decorate
ResourceFieldEntity::entityTypeWrapper protected function Gets the \EntityStructureWrapper for the entity type.
ResourceFieldEntity::executeProcessCallbacks public function Executes the process callbacks. Overrides ResourceFieldInterface::executeProcessCallbacks 2
ResourceFieldEntity::fieldClassName public static function Get the class name to use based on the field definition.
ResourceFieldEntity::fieldInfoField protected static function Gets statically cached information about a field.
ResourceFieldEntity::fieldValue protected function Get value from a property.
ResourceFieldEntity::formatterValue protected function Get value from a field rendered by Drupal field API's formatter.
ResourceFieldEntity::getAccessCallbacks public function Overrides ResourceFieldInterface::getAccessCallbacks
ResourceFieldEntity::getBundle public function Overrides ResourceFieldEntityInterface::getBundle
ResourceFieldEntity::getCallback public function Overrides ResourceFieldInterface::getCallback
ResourceFieldEntity::getCardinality public function Gets the cardinality of the wrapped field. Overrides ResourceFieldInterface::getCardinality
ResourceFieldEntity::getColumn public function Overrides ResourceFieldEntityInterface::getColumn
ResourceFieldEntity::getDefinition public function Gets the original field definition as declared in Resource::publicFields(). Overrides ResourceFieldInterface::getDefinition 3
ResourceFieldEntity::getEntityType public function Overrides ResourceFieldEntityInterface::getEntityType
ResourceFieldEntity::getFormatter public function Overrides ResourceFieldEntityInterface::getFormatter
ResourceFieldEntity::getImageStyles public function Overrides ResourceFieldEntityInterface::getImageStyles
ResourceFieldEntity::getImageUris public static function Get the image URLs based on the configured image styles. Overrides ResourceFieldEntityInterface::getImageUris
ResourceFieldEntity::getMetadata public function Add metadata to the field. Overrides ResourceFieldInterface::getMetadata
ResourceFieldEntity::getMethods public function Overrides ResourceFieldInterface::getMethods
ResourceFieldEntity::getProcessCallbacks public function Overrides ResourceFieldInterface::getProcessCallbacks
ResourceFieldEntity::getProperty public function Overrides ResourceFieldInterface::getProperty
ResourceFieldEntity::getPublicFieldInfo public function Gets the public field info object. Overrides ResourceFieldInterface::getPublicFieldInfo
ResourceFieldEntity::getPublicName public function Overrides ResourceFieldInterface::getPublicName
ResourceFieldEntity::getRequest public function Get the request in the data provider. Overrides ResourceFieldInterface::getRequest 3
ResourceFieldEntity::getResource public function Overrides ResourceFieldInterface::getResource
ResourceFieldEntity::getSubProperty public function Overrides ResourceFieldEntityInterface::getSubProperty
ResourceFieldEntity::getWrapperMethod public function Overrides ResourceFieldEntityInterface::getWrapperMethod
ResourceFieldEntity::id public function Gets the ID of the resource field. Overrides ResourceFieldInterface::id
ResourceFieldEntity::isArrayNumeric public static function Helper method to determine if an array is numeric. Overrides ResourceFieldInterface::isArrayNumeric
ResourceFieldEntity::isComputed public function Checks if the current field is computed. Overrides ResourceFieldInterface::isComputed
ResourceFieldEntity::isWrapperMethodOnEntity public function Overrides ResourceFieldEntityInterface::isWrapperMethodOnEntity
ResourceFieldEntity::nestedDottedChildren protected function Get the children of a query string parameter that apply to the field.
ResourceFieldEntity::nestedDottedFilters protected function Process the filter query string for the relevant sub-query.
ResourceFieldEntity::populatePublicInfoField protected function Populate public info field with Property API information.
ResourceFieldEntity::preprocess public function Massage the value to set according to the format expected by the wrapper. Overrides ResourceFieldEntityInterface::preprocess 3
ResourceFieldEntity::propertyIdentifier protected function Helper function to get the identifier from a property wrapper. 1
ResourceFieldEntity::propertyIsField public static function Checks if a given string represents a Field API field. Overrides ResourceFieldEntityInterface::propertyIsField
ResourceFieldEntity::propertyOnEntity protected function Sets the resource field property to the schema field in the entity.
ResourceFieldEntity::propertyWrapper protected function Get the wrapper for the property associated to the current field.
ResourceFieldEntity::referencedId protected function Helper function to get the referenced entity ID. 1
ResourceFieldEntity::render public function Gets the value of a field and applies all process callbacks to it. Overrides ResourceFieldInterface::render
ResourceFieldEntity::resourceValue protected function Get value for a field based on another resource.
ResourceFieldEntity::set public function Gets the value for the field given a data source. Overrides ResourceFieldInterface::set
ResourceFieldEntity::setAccessCallbacks public function Overrides ResourceFieldInterface::setAccessCallbacks
ResourceFieldEntity::setBundle public function Overrides ResourceFieldEntityInterface::setBundle
ResourceFieldEntity::setCallback public function Overrides ResourceFieldInterface::setCallback
ResourceFieldEntity::setCardinality public function Set the cardinality. Overrides ResourceFieldInterface::setCardinality
ResourceFieldEntity::setColumn public function Overrides ResourceFieldEntityInterface::setColumn
ResourceFieldEntity::setEntityType public function Overrides ResourceFieldEntityInterface::setEntityType
ResourceFieldEntity::setFormatter public function Overrides ResourceFieldEntityInterface::setFormatter
ResourceFieldEntity::setImageStyles public function Overrides ResourceFieldEntityInterface::setImageStyles
ResourceFieldEntity::setMethods public function Overrides ResourceFieldInterface::setMethods
ResourceFieldEntity::setProcessCallbacks public function Overrides ResourceFieldInterface::setProcessCallbacks
ResourceFieldEntity::setProperty public function Overrides ResourceFieldInterface::setProperty
ResourceFieldEntity::setPublicFieldInfo public function Gets the public field info object. Overrides ResourceFieldInterface::setPublicFieldInfo
ResourceFieldEntity::setPublicName public function Overrides ResourceFieldInterface::setPublicName
ResourceFieldEntity::setRequest public function Set the request. Overrides ResourceFieldInterface::setRequest 3
ResourceFieldEntity::setResource public function Overrides ResourceFieldInterface::setResource
ResourceFieldEntity::setSubProperty public function Overrides ResourceFieldEntityInterface::setSubProperty
ResourceFieldEntity::setWrapperMethod public function Overrides ResourceFieldEntityInterface::setWrapperMethod
ResourceFieldEntity::setWrapperMethodOnEntity public function Overrides ResourceFieldEntityInterface::setWrapperMethodOnEntity
ResourceFieldEntity::singleValue protected function Returns the value for the current single field.
ResourceFieldEntity::value public function Gets the value for the field given a data source. Overrides ResourceFieldInterface::value 1
ResourceFieldEntity::__construct public function Constructor. 1