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());
- }
- }
-
|