You are here

class CerFieldHandler in Corresponding Entity References 7.3

@class Handles low-level operations for a single field on a single entity. Exposes methods to add, delete and check for references. This will also iterate over the references, returning each one as an EntityDrupalWrapper object.

Hierarchy

Expanded class hierarchy of CerFieldHandler

1 string reference to 'CerFieldHandler'
CerField::getPluginInfo in includes/CerField.inc
Returns information about a particular field plugin by its identifier, or all available plugins (i.e., defined by hook_cer_fields()) if no identifier is given. The aggregated result of hook_cer_fields() is statically cached.

File

includes/CerFieldHandler.inc, line 14
Contains CerFieldHandler.

View source
class CerFieldHandler implements Countable, SeekableIterator {

  /**
   * @var CerField
   */
  protected $field;

  /**
   * @var EntityDrupalWrapper
   */
  protected $entity;

  /**
   * @var EntityMetadataWrapper
   */
  protected $value;

  /**
   * @var integer
   */
  protected $delta = 0;

  /**
   * @var boolean
   */
  protected $isMultiValue;
  public function __construct(CerField $field, EntityDrupalWrapper $entity) {
    $this->field = $field;
    $this->entity = $entity;
    $this->value = $entity->{$field->name};
    $this->isMultiValue = $this->value instanceof EntityListWrapper;
    $this
      ->rewind();
  }

  /**
   * Adds a reference to $entity, validating it first.
   *
   * @param EntityDrupalWrapper $entity
   *  The wrapped entity to reference.
   */
  public function add(EntityDrupalWrapper $entity) {
    if ($this
      ->validate($entity)) {
      $this
        ->write();
    }
  }

  /**
   * Deletes all references to $entity.
   *
   * @param EntityDrupalWrapper $entity
   *  The wrapped entity to dereference.
   */
  public function delete(EntityDrupalWrapper $entity) {
    $entityID = $entity
      ->getIdentifier();
    if ($this->isMultiValue) {
      foreach ($this->value as $delta => $ref) {
        if ($entityID == $ref
          ->getIdentifier()) {
          $this->value[$delta]
            ->set(NULL);
        }
      }
    }
    elseif ($entityID == $this->value
      ->getIdentifier()) {
      $this->value
        ->set(NULL);
    }
    $this
      ->write();
  }

  /**
   * Validates a potential reference. After doing a cardinality check, the
   * reference is validated through the Field Attach API, allowing the module
   * which owns the field to do its normal validation logic. If validation
   * fails, the error(s) are logged.
   *
   * @param EntityDrupalWrapper $entity
   *  The wrapped entity to validate.
   *
   * @return boolean
   */
  protected function validate(EntityDrupalWrapper $entity) {

    // Before we do anything else, check that the field has enough space to add the
    // reference. If there isn't, bail out so we don't blindly overwrite existing
    // field data.
    if ($this
      ->checkCardinality()) {

      // Keep the previous value so we can restore it if validation fails.
      $prev_value = $this->value
        ->value();
      if ($this->isMultiValue) {
        $value = $this->value
          ->value();
        $value[] = $entity
          ->value();
        $this->value
          ->set($value);
      }
      else {
        $this->value
          ->set($entity
          ->value());
      }

      // Leverage the Field Attach API to validate the reference. If errors occur,
      // field_attach_validate() throws FieldValidationException, containing an array
      // of every validation error.
      try {

        // Only validate this field.
        field_attach_validate($this->entity
          ->type(), $this->entity
          ->value(), array(
          'field_name' => $this->field->name,
        ));
        return TRUE;
      } catch (FieldValidationException $e) {
        foreach ($e->errors as $field) {
          foreach ($field as $language) {
            foreach ($language as $errors) {
              foreach ($errors as $error) {
                $this
                  ->logError($error['message'], $entity);
              }
            }
          }
        }
        $this->value
          ->set($prev_value);
      }
    }
    else {
      $this
        ->logError('Cannot add reference to !that_link from !field_label on !this_link because there are no more slots available.', $entity);
    }
    return FALSE;
  }

  /**
   * Checks that there are enough slots in the field to add a reference.
   *
   * @return boolean
   */
  protected function checkCardinality() {
    return $this->field->cardinality == FIELD_CARDINALITY_UNLIMITED ? TRUE : $this->field->cardinality > $this
      ->count();
  }

  /**
   * Saves changes to the entity and resets the iterator.
   */
  protected function write() {
    $entity_type = $this->entity
      ->type();
    $entityID = $this->entity
      ->getIdentifier();
    $entity = $this->entity
      ->value();
    $entity->cer_processed = TRUE;
    entity_save($entity_type, $entity);

    // Reload the entity we just saved and cleared from the static cache.
    $entities = entity_load($entity_type, (array) $entityID);
    $this->entity
      ->set($entities[$entityID]);
    $this
      ->__construct($this->field, $this->entity);
  }

  /**
   * Logs an error, optionally against a specific entity. If the cer_debug
   * variable is set, the error will also be set as a message.
   *
   * @param string $message
   *  The untranslated message to log.
   *
   * @param EntityDrupalWrapper $entity
   *  The entity that has caused the error, if any.
   */
  protected function logError($message, EntityDrupalWrapper $entity = NULL) {
    $variables = array(
      '!field_name' => $this->field->name,
      '!field_type' => $this->field->fieldTypeLabel,
      '!field_label' => $this->field->label,
    );
    $variables['!this_type'] = $this->entity
      ->type();
    $variables['!this_label'] = $this->entity
      ->label();

    // If the entity has a URI, provide a link to it. Otherwise, its "link"
    // will just be an unlinked label. Entity API doesn't reliably expose a url
    // property on entities, and there doesn't appear to be a way to check for
    // it without risking an EntityMetadataWrapperException. So I need to use
    // this clunky BS instead...ugh.
    $this_uri = entity_uri($this->entity
      ->type(), $this->entity
      ->value());
    if (isset($this_uri)) {
      $variables['!this_url'] = url($this_uri['path'], $this_uri['options']);
      $variables['!this_link'] = l($this->entity
        ->label(), $this_uri['path'], $this_uri['options']);
    }
    else {
      $variables['!this_link'] = $this->entity
        ->label();
    }
    if ($entity) {
      $variables['!that_type'] = $entity
        ->type();
      $variables['!that_label'] = $entity
        ->label();

      // If the entity has a URI, link to it.
      $that_uri = entity_uri($entity
        ->type(), $entity
        ->value());
      if (isset($that_uri)) {
        $variables['!that_url'] = url($that_uri['path'], $that_uri['options']);
        $variables['!that_link'] = l($entity
          ->label(), $that_uri['path'], $that_uri['options']);
      }
      else {
        $variables['!that_link'] = $entity
          ->label();
      }
    }
    watchdog('cer', $message, $variables, WATCHDOG_ERROR);
    if (variable_get('cer_debug', FALSE)) {
      drupal_set_message(t($message, $variables), 'error');
    }
  }
  public function getIDs() {
    $IDs = array();
    if ($this->isMultiValue) {
      foreach ($this->value as $ref) {
        $IDs[] = $ref
          ->raw();
      }
    }
    else {
      $IDs[] = $this->value
        ->raw();
    }
    return array_unique(array_filter($IDs));
  }

  /**
   * Implements Countable::count().
   */
  public function count() {
    if ($this->isMultiValue) {
      return sizeof($this->value);
    }
    else {
      return $this->value
        ->value() ? 1 : 0;
    }
  }

  /**
   * Implements SeekableIterator::seek().
   */
  public function seek($position) {
    $length = $this
      ->count();
    if ($position < 0) {
      $position += $length;
    }
    if ($position >= 0 && $position < $length) {
      $this->delta = $position;
    }
    else {
      throw new OutOfBoundsException(t('Cannot seek to invalid position.'));
    }
  }

  /**
   * Implements Iterator::current().
   */
  public function current() {
    return $this->isMultiValue ? $this->value[$this->delta] : $this->value;
  }

  /**
   * Implements Iterator::key().
   */
  public function key() {
    return $this
      ->current()
      ->getIdentifier();
  }

  /**
   * Implements Iterator::next().
   */
  public function next() {
    $this->delta++;
  }

  /**
   * Implements Iterator::rewind().
   */
  public function rewind() {
    $this->delta = 0;
  }

  /**
   * Implements Iterator::valid().
   */
  public function valid() {
    return $this->delta < $this
      ->count();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CerFieldHandler::$delta protected property
CerFieldHandler::$entity protected property
CerFieldHandler::$field protected property
CerFieldHandler::$isMultiValue protected property
CerFieldHandler::$value protected property
CerFieldHandler::add public function Adds a reference to $entity, validating it first.
CerFieldHandler::checkCardinality protected function Checks that there are enough slots in the field to add a reference.
CerFieldHandler::count public function Implements Countable::count().
CerFieldHandler::current public function Implements Iterator::current().
CerFieldHandler::delete public function Deletes all references to $entity.
CerFieldHandler::getIDs public function
CerFieldHandler::key public function Implements Iterator::key().
CerFieldHandler::logError protected function Logs an error, optionally against a specific entity. If the cer_debug variable is set, the error will also be set as a message.
CerFieldHandler::next public function Implements Iterator::next().
CerFieldHandler::rewind public function Implements Iterator::rewind().
CerFieldHandler::seek public function Implements SeekableIterator::seek().
CerFieldHandler::valid public function Implements Iterator::valid().
CerFieldHandler::validate protected function Validates a potential reference. After doing a cardinality check, the reference is validated through the Field Attach API, allowing the module which owns the field to do its normal validation logic. If validation fails, the error(s) are logged.
CerFieldHandler::write protected function Saves changes to the entity and resets the iterator.
CerFieldHandler::__construct public function