You are here

class EntityFieldHandlerHelper in Entity API 7

Helper class containing static implementations of common field handler methods.

Used by the data selection entity field handlers to avoid code duplication.

Hierarchy

Expanded class hierarchy of EntityFieldHandlerHelper

See also

entity_views_table_definition()

1 string reference to 'EntityFieldHandlerHelper'
entity_views_table_definition in views/entity.views.inc
Helper function for getting data selection based entity Views table definitions.

File

views/handlers/entity_views_field_handler_helper.inc, line 15
Contains the EntityFieldHandlerHelper class.

View source
class EntityFieldHandlerHelper {

  /**
   * Provide appropriate default options for a handler.
   */
  public static function option_definition($handler) {
    if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) {
      $options['list']['contains']['mode'] = array(
        'default' => 'collapse',
      );
      $options['list']['contains']['separator'] = array(
        'default' => ', ',
      );
      $options['list']['contains']['type'] = array(
        'default' => 'ul',
      );
    }
    $options['link_to_entity'] = array(
      'default' => FALSE,
    );
    return $options;
  }

  /**
   * Provide an appropriate default option form for a handler.
   */
  public static function options_form($handler, &$form, &$form_state) {
    if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) {
      $form['list']['mode'] = array(
        '#type' => 'select',
        '#title' => t('List handling'),
        '#options' => array(
          'collapse' => t('Concatenate values using a seperator'),
          'list' => t('Output values as list'),
          'first' => t('Show first (if present)'),
          'count' => t('Show item count'),
        ),
        '#default_value' => $handler->options['list']['mode'],
      );
      $form['list']['separator'] = array(
        '#type' => 'textfield',
        '#title' => t('List seperator'),
        '#default_value' => $handler->options['list']['separator'],
        '#dependency' => array(
          'edit-options-list-mode' => array(
            'collapse',
          ),
        ),
      );
      $form['list']['type'] = array(
        '#type' => 'select',
        '#title' => t('List type'),
        '#options' => array(
          'ul' => t('Unordered'),
          'ol' => t('Ordered'),
        ),
        '#default_value' => $handler->options['list']['type'],
        '#dependency' => array(
          'edit-options-list-mode' => array(
            'list',
          ),
        ),
      );
    }
    $form['link_to_entity'] = array(
      '#type' => 'checkbox',
      '#title' => t('Link this field to its entity'),
      '#description' => t("When using this, you should not set any other link on the field."),
      '#default_value' => $handler->options['link_to_entity'],
    );
  }

  /**
   * Add the field for the entity ID (if necessary).
   */
  public static function query($handler) {

    // Copied over from views_handler_field_entity::query().
    // Sets table_alias (entity table), base_field (entity id field) and
    // field_alias (the field's alias).
    $handler->table_alias = $base_table = $handler->view->base_table;
    $handler->base_field = $handler->view->base_field;
    if (!empty($handler->relationship)) {
      foreach ($handler->view->relationship as $relationship) {
        if ($relationship->alias == $handler->relationship) {
          $base_table = $relationship->definition['base'];
          $handler->table_alias = $relationship->alias;
          $table_data = views_fetch_data($base_table);
          $handler->base_field = empty($relationship->definition['base field']) ? $table_data['table']['base']['field'] : $relationship->definition['base field'];
        }
      }
    }

    // Add the field if the query back-end implements an add_field() method,
    // just like the default back-end.
    if (method_exists($handler->query, 'add_field')) {
      $handler->field_alias = $handler->query
        ->add_field($handler->table_alias, $handler->base_field, '');
    }
    else {

      // To ensure there is an alias just set the field alias to the real field.
      $handler->field_alias = $handler->real_field;
    }
  }

  /**
   * Extracts the innermost field name from a data selector.
   *
   * @param $selector
   *   The data selector.
   *
   * @return string
   *   The last component of the data selector.
   */
  public static function get_selector_field_name($selector) {
    return ltrim(substr($selector, strrpos($selector, ':')), ':');
  }

  /**
   * Adds a click-sort to the query.
   *
   * @param $order
   *   Either 'ASC' or 'DESC'.
   */
  public static function click_sort($handler, $order) {

    // The normal orderby() method for this usually won't work here. So we need
    // query plugins to provide their own method for this.
    if (method_exists($handler->query, 'add_selector_orderby')) {
      $selector = self::construct_property_selector($handler, TRUE);
      $handler->query
        ->add_selector_orderby($selector, $order);
    }
  }

  /**
   * Load the entities for all rows that are about to be displayed.
   *
   * Automatically takes care of relationships, including data selection
   * relationships. Results are written into @code $handler->wrappers @endcode
   * and @code $handler->entity_type @endcode is set.
   */
  public static function pre_render($handler, &$values, $load_always = FALSE) {
    if (empty($values)) {
      return;
    }
    if (!$load_always && empty($handler->options['link_to_entity'])) {

      // Check whether we even need to load the entities.
      $selector = self::construct_property_selector($handler, TRUE);
      $load = FALSE;
      foreach ($values as $row) {
        if (empty($row->_entity_properties) || !array_key_exists($selector, $row->_entity_properties)) {
          $load = TRUE;
          break;
        }
      }
      if (!$load) {
        return;
      }
    }
    if (method_exists($handler->query, 'get_result_wrappers')) {
      list($handler->entity_type, $handler->wrappers) = $handler->query
        ->get_result_wrappers($values, $handler->relationship, $handler->real_field);
    }
    else {
      list($handler->entity_type, $entities) = $handler->query
        ->get_result_entities($values, $handler->relationship, $handler->real_field);
      $handler->wrappers = array();
      foreach ($entities as $id => $entity) {
        $handler->wrappers[$id] = entity_metadata_wrapper($handler->entity_type, $entity);
      }
    }
  }

  /**
   * Return an Entity API data selector for the given handler's relationship.
   *
   * A data selector is a concatenation of properties which should be followed
   * to arrive at a desired property that may be nested in related entities or
   * structures. The separate properties are herein concatenated with colons.
   *
   * For instance, a data selector of "author:roles" would mean to first
   * access the "author" property of the given wrapper, and then for this new
   * wrapper to access and return the "roles" property.
   *
   * Lists of entities are handled automatically by always returning only the
   * first entity.
   *
   * @param $handler
   *   The handler for which to construct the selector.
   * @param $complete
   *   If TRUE, the complete selector for the field is returned, not just the
   *   one for its parent. Defaults to FALSE.
   *
   * @return string
   *   An Entity API data selector for the given handler's relationship.
   */
  public static function construct_property_selector($handler, $complete = FALSE) {
    $return = '';
    if ($handler->relationship) {
      $current_handler = $handler;
      $view = $current_handler->view;
      $relationships = array();

      // Collect all relationships, keyed by alias.
      foreach ($view->relationship as $key => $relationship) {
        $key = $relationship->alias ? $relationship->alias : $key;
        $relationships[$key] = $relationship;
      }
      while (!empty($current_handler->relationship) && !empty($relationships[$current_handler->relationship])) {
        $current_handler = $relationships[$current_handler->relationship];
        $return = $current_handler->real_field . ($return ? ":{$return}" : '');
      }
    }
    if ($complete) {
      $return .= ($return ? ':' : '') . $handler->real_field;
    }
    elseif ($pos = strrpos($handler->real_field, ':')) {

      // If we have a selector as the real_field, append this to the returned
      // relationship selector.
      $return .= ($return ? ':' : '') . substr($handler->real_field, 0, $pos);
    }
    return $return;
  }

  /**
   * Extracts data from several metadata wrappers based on a data selector.
   *
   * All metadata wrappers passed to this function have to be based on the exact
   * same property information. The data will be returned wrapped by one or more
   * metadata wrappers.
   *
   * Can be used in query plugins for the get_result_entities() and
   * get_result_wrappers() methods.
   *
   * @param array $wrappers
   *   The EntityMetadataWrapper objects from which to extract data.
   * @param $selector
   *   The selector specifying the data to extract.
   *
   * @return array
   *   An array with numeric indices, containing the type of the extracted
   *   wrappers in the first element. The second element of the array contains
   *   the extracted property value(s) for each wrapper, keyed to the same key
   *   that was used for the respective wrapper in $wrappers. All extracted
   *   properties are returned as metadata wrappers.
   */
  public static function extract_property_multiple(array $wrappers, $selector) {
    $parts = explode(':', $selector, 2);
    $name = $parts[0];
    $results = array();
    $entities = array();
    $type = '';
    foreach ($wrappers as $i => $wrapper) {
      try {
        $property = $wrapper->{$name};
        $type = $property
          ->type();
        if ($property instanceof EntityDrupalWrapper) {

          // Remember the entity IDs to later load all at once (so as to
          // properly utilize multiple load functionality).
          $id = $property
            ->getIdentifier();

          // Only accept valid ids. $id can be FALSE for entity values that are
          // NULL.
          if ($id) {
            $entities[$type][$i] = $id;
          }
        }
        elseif ($property instanceof EntityStructureWrapper) {
          $results[$i] = $property;
        }
        elseif ($property instanceof EntityListWrapper) {
          foreach ($property as $item) {
            $results[$i] = $item;
            $type = $item
              ->type();
            break;
          }
        }

        // Do nothing in case it cannot be applied.
      } catch (EntityMetadataWrapperException $e) {

        // Skip single empty properties.
      }
    }
    if ($entities) {

      // Map back the loaded entities back to the results array.
      foreach ($entities as $type => $id_map) {
        $loaded = entity_load($type, $id_map);
        foreach ($id_map as $i => $id) {
          if (isset($loaded[$id])) {
            $results[$i] = entity_metadata_wrapper($type, $loaded[$id]);
          }
        }
      }
    }

    // If there are no further parts in the selector, we are done now.
    if (empty($parts[1])) {
      return array(
        $type,
        $results,
      );
    }
    return self::extract_property_multiple($results, $parts[1]);
  }

  /**
   * Get the value of a certain data selector.
   *
   * Uses $values->_entity_properties to look for already extracted properties.
   *
   * @param $handler
   *   The field handler for which to return a value.
   * @param $values
   *   The values for the current row retrieved from the Views query, as an
   *   object.
   * @param $field
   *   The field to extract. If no value is given, the field of the given
   *   handler is used instead. The special "entity object" value can be used to
   *   get the base entity instead of a special field.
   * @param $default
   *   The value to return if the entity or field are not present.
   */
  public static function get_value($handler, $values, $field = NULL, $default = NULL) {

    // There is a value cache on each handler so parent handlers rendering a
    // single field value from a list will get the single value, not the whole
    // list.
    if (!isset($field) && isset($handler->current_value)) {
      return $handler->current_value;
    }
    $field = isset($field) ? $field : self::get_selector_field_name($handler->real_field);
    $selector = self::construct_property_selector($handler);
    $selector = $selector ? "{$selector}:{$field}" : $field;
    if (!isset($values->_entity_properties)) {
      $values->_entity_properties = array();
    }
    if (!array_key_exists($selector, $values->_entity_properties)) {
      if (!isset($handler->wrappers[$handler->view->row_index])) {
        $values->_entity_properties[$selector] = $default;
      }
      elseif (is_array($handler->wrappers[$handler->view->row_index])) {
        $values->_entity_properties[$selector] = self::extract_list_wrapper_values($handler->wrappers[$handler->view->row_index], $field);
      }
      else {
        $wrapper = $handler->wrappers[$handler->view->row_index];
        try {
          if ($field === 'entity object') {
            $values->_entity_properties[$selector] = $wrapper
              ->value();
          }
          else {
            $values->_entity_properties[$selector] = isset($wrapper->{$field}) ? $wrapper->{$field}
              ->value(array(
              'identifier' => TRUE,
              'sanitize' => TRUE,
            )) : $default;
          }
        } catch (EntityMetadataWrapperException $e) {
          $values->_entity_properties[$selector] = $default;
        }
      }
    }
    return $values->_entity_properties[$selector];
  }

  /**
   * Helper method for extracting the values from an array of wrappers.
   *
   * Nested arrays of wrappers are also handled, the values are returned in a
   * flat (not nested) array.
   */
  public static function extract_list_wrapper_values(array $wrappers, $field) {
    $return = array();
    foreach ($wrappers as $wrapper) {
      if (is_array($wrapper)) {
        $values = self::extract_list_wrapper_values($wrapper, $field);
        if ($values) {
          $return = array_merge($return, $values);
        }
      }
      else {
        try {
          if ($field == 'entity object') {
            $return[] = $wrapper
              ->value();
          }
          elseif (isset($wrapper->{$field})) {
            $return[] = $wrapper->{$field}
              ->value(array(
              'identifier' => TRUE,
            ));
          }
        } catch (EntityMetadataWrapperException $e) {

          // An exception probably signifies a non-present property, so we just
          // ignore it.
        }
      }
    }
    return $return;
  }

  /**
   * Render the field.
   *
   * Implements the entity link functionality and list handling. Basic handling
   * of the single values is delegated back to the field handler.
   *
   * @param $handler
   *   The field handler whose field should be rendered.
   * @param $values
   *   The values for the current row retrieved from the Views query, as an
   *   object.
   *
   * @return string
   *   The rendered value for the field.
   */
  public static function render($handler, $values) {
    $value = $handler
      ->get_value($values);
    if (is_array($value)) {
      return self::render_list($handler, $value, $values);
    }
    return self::render_entity_link($handler, $value, $values);
  }

  /**
   * Render a list of values.
   *
   * @param $handler
   *   The field handler whose field is rendered.
   * @param $list
   *   The list of values to render.
   * @param $values
   *   The values for the current row retrieved from the Views query, as an
   *   object.
   *
   * @return string
   *   The rendered value for the given list.
   */
  public static function render_list($handler, $list, $values) {

    // Allow easy overriding of this behaviour in the specific field handler.
    if (method_exists($handler, 'render_list')) {
      return $handler
        ->render_list($list, $values);
    }
    $mode = isset($handler->options['list']['mode']) ? $handler->options['list']['mode'] : NULL;
    switch ($mode) {
      case 'first':
        $list = count($list) ? array_shift($list) : NULL;
        if (is_array($list)) {
          return self::render_list($handler, $list, $values);
        }
        elseif (isset($list)) {
          return self::render_entity_link($handler, $list, $values);
        }
        return NULL;
      case 'count':
        return count($list);

      // Handles both collapse and list output. Fallback is to collapse.
      default:
        $inner_values = array();
        foreach ($list as $value) {
          $value = is_array($value) ? self::render_list($handler, $value, $values) : self::render_entity_link($handler, $value, $values);
          if ($value) {
            $inner_values[] = $value;
          }
        }

        // Format output as list.
        if ($mode == 'list') {
          $type = isset($handler->options['list']['type']) ? $handler->options['list']['type'] : 'ul';
          return theme('item_list', array(
            'items' => $inner_values,
            'type' => $type,
          ));
        }
        $separator = isset($handler->options['list']['separator']) ? $handler->options['list']['separator'] : ', ';
        return implode($separator, $inner_values);
    }
  }

  /**
   * Render a single value as a link to the entity if applicable.
   *
   * @param $handler
   *   The field handler whose field is rendered.
   * @param $value
   *   The single value to render.
   * @param $values
   *   The values for the current row retrieved from the Views query, as an
   *   object.
   *
   * @return string
   *   The rendered value.
   */
  public static function render_entity_link($handler, $value, $values) {

    // Allow easy overriding of this behaviour in the specific field handler.
    if (method_exists($handler, 'render_entity_link')) {
      return $handler
        ->render_entity_link($value, $values);
    }
    $render = self::render_single_value($handler, $value, $values);
    if (!$handler->options['link_to_entity']) {
      return $render;
    }
    $entity = $handler
      ->get_value($values, 'entity object');
    if (is_object($entity) && ($url = entity_uri($handler->entity_type, $entity))) {
      return l($render, $url['path'], array(
        'html' => TRUE,
      ) + $url['options']);
    }
    return $render;
  }

  /**
   * Render a single value.
   *
   * @param $handler
   *   The field handler whose field is rendered.
   * @param $value
   *   The single value to render.
   * @param $values
   *   The values for the current row retrieved from the Views query, as an
   *   object.
   *
   * @return string
   *   The rendered value.
   */
  public static function render_single_value($handler, $value, $values) {

    // Try to use the method in the specific field handler.
    if (method_exists($handler, 'render_single_value')) {
      $handler->current_value = $value;
      $return = $handler
        ->render_single_value($value, $values);
      unset($handler->current_value);
      return $return;
    }

    // Default fallback in case the field handler doesn't provide the method.
    return is_scalar($value) ? check_plain($value) : nl2br(check_plain(print_r($value, TRUE)));
  }

}

Members

Namesort descending Modifiers Type Description Overrides
EntityFieldHandlerHelper::click_sort public static function Adds a click-sort to the query.
EntityFieldHandlerHelper::construct_property_selector public static function Return an Entity API data selector for the given handler's relationship.
EntityFieldHandlerHelper::extract_list_wrapper_values public static function Helper method for extracting the values from an array of wrappers.
EntityFieldHandlerHelper::extract_property_multiple public static function Extracts data from several metadata wrappers based on a data selector.
EntityFieldHandlerHelper::get_selector_field_name public static function Extracts the innermost field name from a data selector.
EntityFieldHandlerHelper::get_value public static function Get the value of a certain data selector.
EntityFieldHandlerHelper::options_form public static function Provide an appropriate default option form for a handler.
EntityFieldHandlerHelper::option_definition public static function Provide appropriate default options for a handler.
EntityFieldHandlerHelper::pre_render public static function Load the entities for all rows that are about to be displayed.
EntityFieldHandlerHelper::query public static function Add the field for the entity ID (if necessary).
EntityFieldHandlerHelper::render public static function Render the field.
EntityFieldHandlerHelper::render_entity_link public static function Render a single value as a link to the entity if applicable.
EntityFieldHandlerHelper::render_list public static function Render a list of values.
EntityFieldHandlerHelper::render_single_value public static function Render a single value.