Files
Clameurs/sites/all/modules/contrib/fields/cer/includes/CerFieldHandler.inc
T

306 lines
8.0 KiB
PHP

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