| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 | <?php/** * @file * Contains CerFieldHandler. */ /** * @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. */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());  }} 
 |