You are here

class EntityDrupalWrapper in Entity API 7

Provides a wrapper for entities registrered in hook_entity_info().

The wrapper eases applying getter and setter callbacks of entity properties specified in hook_entity_property_info().

Hierarchy

Expanded class hierarchy of EntityDrupalWrapper

File

includes/entity.wrapper.inc, line 590
Provides wrappers allowing easy usage of the entity metadata.

View source
class EntityDrupalWrapper extends EntityStructureWrapper {

  /**
   * Contains the entity id.
   */
  protected $id = FALSE;
  protected $bundle;
  protected $entityInfo;

  /**
   * Construct a new EntityDrupalWrapper object.
   *
   * @param $type
   *   The type of the passed data.
   * @param $data
   *   Optional. The entity to wrap or its identifier.
   * @param $info
   *   Optional. Used internally to pass info about properties down the tree.
   */
  public function __construct($type, $data = NULL, $info = array()) {
    parent::__construct($type, $data, $info);
    $this
      ->setUp();
  }
  protected function setUp() {
    $this->propertyInfo = entity_get_property_info($this->type) + array(
      'properties' => array(),
    );
    $info = $this->info + array(
      'property info' => array(),
      'bundle' => NULL,
    );
    $this->propertyInfo['properties'] += $info['property info'];
    $this->bundle = $info['bundle'];
    $this->entityInfo = entity_get_info($this->type);
    if (isset($this->bundle)) {
      $this
        ->spotBundleInfo(FALSE);
    }
  }

  /**
   * Sets the entity internally accepting both the entity id and object.
   */
  protected function setEntity($data) {

    // For entities we allow getter callbacks to return FALSE, which we
    // interpret like NULL values as unset properties.
    if (isset($data) && $data !== FALSE && !is_object($data)) {
      $this->id = $data;
      $this->data = FALSE;
    }
    elseif (is_object($data) && $data instanceof EntityDrupalWrapper) {

      // We got a wrapped entity passed, so take over its values.
      $this->id = $data->id;
      $this->data = $data->data;

      // For generic entity references, also update the entity type accordingly.
      if ($this->info['type'] == 'entity') {
        $this->type = $data->type;
      }
    }
    elseif (is_object($data)) {

      // We got the entity object passed.
      $this->data = $data;
      $id = entity_id($this->type, $data);
      $this->id = isset($id) ? $id : FALSE;
    }
    else {
      $this->id = FALSE;
      $this->data = NULL;
    }
  }

  /**
   * Used to lazy-load bundle info. So the wrapper can be loaded e.g. just
   * for setting without the data being loaded.
   */
  protected function spotInfo() {
    if (!$this->propertyInfoAltered) {
      if ($this->info['type'] == 'entity' && $this
        ->dataAvailable() && $this
        ->value()) {

        // Add in entity-type specific details.
        $this
          ->setUp();
      }
      $this
        ->spotBundleInfo(TRUE);
      parent::spotInfo();
      $this->propertyInfoAltered = TRUE;
    }
  }

  /**
   * Tries to determine the bundle and adds in the according property info.
   *
   * @param $load
   *   Whether the entity should be loaded to spot the info if necessary.
   */
  protected function spotBundleInfo($load = TRUE) {

    // Like entity_extract_ids() assume the entity type if no key is given.
    if (empty($this->entityInfo['entity keys']['bundle']) && $this->type != 'entity') {
      $this->bundle = $this->type;
    }
    elseif (!$this->bundle && $load && $this
      ->dataAvailable()) {
      try {
        if ($entity = $this
          ->value()) {
          list($id, $vid, $bundle) = entity_extract_ids($this->type, $entity);
          $this->bundle = $bundle;
        }
      } catch (EntityMetadataWrapperException $e) {

        // Loading data failed, so we cannot derive the used bundle.
      }
    }
    if ($this->bundle && isset($this->propertyInfo['bundles'][$this->bundle])) {
      $bundle_info = (array) $this->propertyInfo['bundles'][$this->bundle] + array(
        'properties' => array(),
      );

      // Allow bundles to re-define existing properties, such that the bundle
      // can add in more bundle-specific details like the bundle of a referenced
      // entity.
      $this->propertyInfo['properties'] = $bundle_info['properties'] + $this->propertyInfo['properties'];
    }
  }

  /**
   * Returns the identifier of the wrapped entity.
   *
   * @see entity_id()
   */
  public function getIdentifier() {
    return $this
      ->dataAvailable() ? $this
      ->value(array(
      'identifier' => TRUE,
    )) : NULL;
  }

  /**
   * Returns the bundle of an entity, or FALSE if it has no bundles.
   */
  public function getBundle() {
    if ($this
      ->dataAvailable()) {
      $this
        ->spotInfo();
      return $this->bundle;
    }
  }

  /**
   * Overridden.
   *
   * @param $options
   *   An array of options. Known keys:
   *   - identifier: If set to TRUE, the entity identifier is returned.
   */
  public function value(array $options = array()) {

    // Try loading the data via the getter callback if there is none yet.
    if (!isset($this->data)) {
      $this
        ->setEntity(parent::value());
    }
    if (!empty($options['identifier'])) {
      return $this->id;
    }
    elseif (!$this->data && !empty($this->id)) {

      // Lazy load the entity if necessary.
      $return = entity_load($this->type, array(
        $this->id,
      ));

      // In case the entity cannot be loaded, we return NULL just as for empty
      // properties.
      $this->data = $return ? reset($return) : NULL;
    }
    return $this->data;
  }

  /**
   * Returns the entity prepared for rendering.
   *
   * @see entity_view()
   */
  public function view($view_mode = 'full', $langcode = NULL, $page = NULL) {
    return entity_view($this
      ->type(), array(
      $this
        ->value(),
    ), $view_mode, $langcode, $page);
  }

  /**
   * Overridden to support setting the entity by either the object or the id.
   */
  public function set($value) {
    if (!$this
      ->validate($value)) {
      throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array(
        // An exception's message is output through check_plain().
        '!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value,
        '!location' => $this
          ->debugIdentifierLocation(),
      )));
    }
    if ($this->info['type'] == 'entity' && $value === $this) {

      // Nothing to do.
      return $this;
    }
    $previous_id = $this->id;
    $previous_type = $this->type;

    // Set value, so we get the identifier and pass it to the normal setter.
    $this
      ->clear();
    $this
      ->setEntity($value);

    // Generally, we have to update the parent only if the entity reference
    // has changed. In case of a generic entity reference, we pass the entity
    // wrapped. Else we just pass the id of the entity to the setter callback.
    if ($this->info['type'] == 'entity' && ($previous_id != $this->id || $previous_type != $this->type)) {

      // We need to clone the wrapper we pass through as value, so it does not
      // get cleared when the current wrapper instance gets cleared.
      $this
        ->updateParent(clone $this);
    }
    elseif ($this->id === FALSE && !$this->data) {
      $this
        ->updateParent(NULL);
    }
    elseif ($previous_id !== $this->id) {
      $this
        ->updateParent($this->id);
    }
    return $this;
  }

  /**
   * Overridden.
   */
  public function clear() {
    $this->id = NULL;
    $this->bundle = isset($this->info['bundle']) ? $this->info['bundle'] : NULL;
    if ($this->type != $this->info['type']) {

      // Reset entity info / property info based upon the info provided during
      // the creation of the wrapper.
      $this->type = $this->info['type'];
      $this
        ->setUp();
    }
    parent::clear();
  }

  /**
   * Overridden.
   */
  public function type() {

    // In case of a generic entity wrapper, load the data first to determine
    // the type of the concrete entity.
    if ($this
      ->dataAvailable() && $this->info['type'] == 'entity') {
      try {
        $this
          ->value(array(
          'identifier' => TRUE,
        ));
      } catch (EntityMetadataWrapperException $e) {

        // If loading data fails, we cannot determine the concrete entity type.
      }
    }
    return $this->type;
  }

  /**
   * {@inheritdoc}
   *
   * Note that this method checks property access, but can be used for checking
   * entity access *only* if the wrapper is not a property (i.e. has no parent
   * wrapper).
   * To be safe, better use EntityDrupalWrapper::entityAccess() for checking
   * entity access.
   */
  public function access($op, $account = NULL) {
    if (!empty($this->info['parent'])) {

      // If this is a property, make sure the user is able to view the
      // currently referenced entity also.
      if ($this
        ->entityAccess('view', $account) === FALSE) {
        return FALSE;
      }
      if (parent::access($op, $account) === FALSE) {
        return FALSE;
      }

      // If access is unknown, we return TRUE.
      return TRUE;
    }
    else {

      // This is not a property, so fallback on entity access.
      if ($op == 'edit') {

        // If the operation is "edit" determine if its actually a "create" for
        // new un-saved entities, or an "update" for existing ones.
        return $this
          ->entityAccess($this
          ->getIdentifier() ? 'update' : 'create', $account);
      }
      return $this
        ->entityAccess('view', $account);
    }
  }

  /**
   * Checks whether the operation $op is allowed on the entity.
   *
   * @see entity_access()
   */
  public function entityAccess($op, $account = NULL) {
    $entity = $this
      ->dataAvailable() ? $this
      ->value() : NULL;

    // The value() method could return FALSE on entities such as user 0, so we
    // need to use NULL instead to conform to the expectations of
    // entity_access().
    if ($entity === FALSE) {
      $entity = NULL;
    }
    return entity_access($op, $this->type, $entity, $account);
  }

  /**
   * Permanently save the wrapped entity.
   *
   * @throws EntityMetadataWrapperException
   *   If the entity type does not support saving.
   *
   * @return EntityDrupalWrapper
   */
  public function save() {
    if ($this->data) {
      if (!entity_type_supports($this->type, 'save')) {
        throw new EntityMetadataWrapperException("There is no information about how to save entities of type " . check_plain($this->type) . '.');
      }
      entity_save($this->type, $this->data);

      // On insert, update the identifier afterwards.
      if (!$this->id) {
        list($this->id, , ) = entity_extract_ids($this->type, $this->data);
      }
    }

    // If the entity hasn't been loaded yet, don't bother saving it.
    return $this;
  }

  /**
   * Permanently delete the wrapped entity.
   *
   * @return EntityDrupalWrapper
   */
  public function delete() {
    if ($this
      ->dataAvailable() && $this
      ->value()) {
      $return = entity_delete($this->type, $this->id);
      if ($return === FALSE) {
        throw new EntityMetadataWrapperException("There is no information about how to delete entities of type " . check_plain($this->type) . '.');
      }
    }
    return $this;
  }

  /**
   * Gets the info about the wrapped entity.
   */
  public function entityInfo() {
    return $this->entityInfo;
  }

  /**
   * Returns the name of the key used by the entity for given entity key.
   *
   * @param $name
   *   One of 'id', 'name', 'bundle' or 'revision'.
   *
   * @return string
   *   The name of the key used by the entity.
   */
  public function entityKey($name) {
    return isset($this->entityInfo['entity keys'][$name]) ? $this->entityInfo['entity keys'][$name] : FALSE;
  }

  /**
   * Returns the entity label.
   *
   * @see entity_label()
   */
  public function label() {
    if ($entity = $this
      ->value()) {
      return entity_label($this->type, $entity);
    }
  }

  /**
   * Returns a string to use to identify this wrapper in error messages.
   */
  public function debugIdentifierLocation() {

    // An entity wrapper can be at the top of the chain or a part of it.
    if (isset($this->info['name'])) {

      // This wrapper is part of a chain, eg in the position node->author.
      // Return the name.
      $debug = $this->info['name'];
    }
    else {

      // This is a wrapper for an actual entity: return its type and id.
      $debug = $this->info['type'] . '(' . $this
        ->getIdentifier() . ')';
    }
    if (isset($this->info['parent'])) {
      $debug = $this->info['parent']
        ->debugIdentifierLocation() . '->' . $debug;
    }
    return $debug;
  }

  /**
   * Prepare for serializiation.
   */
  public function __sleep() {
    $vars = parent::__sleep();

    // Don't serialize the loaded entity and its property info.
    unset($vars['data'], $vars['propertyInfo'], $vars['propertyInfoAltered'], $vars['entityInfo']);

    // In case the entity is not saved yet, serialize the unsaved data.
    if ($this
      ->dataAvailable() && $this->id === FALSE) {
      $vars['data'] = 'data';
    }
    return $vars;
  }
  public function __wakeup() {
    $this
      ->setUp();
    if ($this->id !== FALSE) {

      // Make sure data is set, so the entity will be loaded when needed.
      $this->data = FALSE;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
EntityDrupalWrapper::$bundle protected property
EntityDrupalWrapper::$entityInfo protected property
EntityDrupalWrapper::$id protected property Contains the entity id.
EntityDrupalWrapper::access public function Note that this method checks property access, but can be used for checking entity access *only* if the wrapper is not a property (i.e. has no parent wrapper). To be safe, better use EntityDrupalWrapper::entityAccess() for checking entity access. Overrides EntityMetadataWrapper::access
EntityDrupalWrapper::clear public function Overridden. Overrides EntityStructureWrapper::clear
EntityDrupalWrapper::debugIdentifierLocation public function Returns a string to use to identify this wrapper in error messages. Overrides EntityMetadataWrapper::debugIdentifierLocation
EntityDrupalWrapper::delete public function Permanently delete the wrapped entity.
EntityDrupalWrapper::entityAccess public function Checks whether the operation $op is allowed on the entity.
EntityDrupalWrapper::entityInfo public function Gets the info about the wrapped entity.
EntityDrupalWrapper::entityKey public function Returns the name of the key used by the entity for given entity key.
EntityDrupalWrapper::getBundle public function Returns the bundle of an entity, or FALSE if it has no bundles.
EntityDrupalWrapper::getIdentifier public function Returns the identifier of the wrapped entity. Overrides EntityStructureWrapper::getIdentifier
EntityDrupalWrapper::label public function Returns the entity label. Overrides EntityMetadataWrapper::label
EntityDrupalWrapper::save public function Permanently save the wrapped entity.
EntityDrupalWrapper::set public function Overridden to support setting the entity by either the object or the id. Overrides EntityMetadataWrapper::set
EntityDrupalWrapper::setEntity protected function Sets the entity internally accepting both the entity id and object.
EntityDrupalWrapper::setUp protected function
EntityDrupalWrapper::spotBundleInfo protected function Tries to determine the bundle and adds in the according property info.
EntityDrupalWrapper::spotInfo protected function Used to lazy-load bundle info. So the wrapper can be loaded e.g. just for setting without the data being loaded. Overrides EntityStructureWrapper::spotInfo
EntityDrupalWrapper::type public function Overridden. Overrides EntityMetadataWrapper::type
EntityDrupalWrapper::value public function Overridden. Overrides EntityMetadataWrapper::value
EntityDrupalWrapper::view public function Returns the entity prepared for rendering.
EntityDrupalWrapper::__construct public function Construct a new EntityDrupalWrapper object. Overrides EntityStructureWrapper::__construct
EntityDrupalWrapper::__sleep public function Prepare for serializiation. Overrides EntityStructureWrapper::__sleep
EntityDrupalWrapper::__wakeup public function
EntityMetadataWrapper::$cache protected property
EntityMetadataWrapper::$data protected property
EntityMetadataWrapper::$info protected property
EntityMetadataWrapper::$type protected property 1
EntityMetadataWrapper::dataAvailable protected function Returns whether data is available to work with.
EntityMetadataWrapper::info public function Gets info about the wrapped data.
EntityMetadataWrapper::optionsList public function Returns the options list specifying possible values for the property, if defined.
EntityMetadataWrapper::raw public function Returns the raw, unprocessed data. Most times this is the same as returned by value(), however for already processed and sanitized textual data, this will return the unprocessed data in contrast to value().
EntityMetadataWrapper::updateParent protected function Updates the parent data structure of a data property with the latest data value.
EntityMetadataWrapper::validate public function Returns whether $value is a valid value to set. 1
EntityMetadataWrapper::__toString public function
EntityStructureWrapper::$langcode protected property
EntityStructureWrapper::$propertyInfo protected property
EntityStructureWrapper::$propertyInfoDefaults protected property
EntityStructureWrapper::get public function Get the wrapper for a property.
EntityStructureWrapper::getIterator public function
EntityStructureWrapper::getPropertyInfo public function Gets the info about the given property.
EntityStructureWrapper::getPropertyLanguage public function Gets the language used for retrieving properties.
EntityStructureWrapper::getPropertyRaw protected function Gets the raw value of a property.
EntityStructureWrapper::getPropertyValue protected function Gets the value of a property.
EntityStructureWrapper::language public function Sets a new language to use for retrieving properties.
EntityStructureWrapper::propertyAccess protected function
EntityStructureWrapper::refPropertyInfo public function Returns a reference on the property info.
EntityStructureWrapper::setProperty protected function Sets a property.
EntityStructureWrapper::__get public function Magic method: Get a wrapper for a property.
EntityStructureWrapper::__isset public function Magic method: Can be used to check if a property is known.
EntityStructureWrapper::__set public function Magic method: Set a property.