CerFieldHandler.inc in Corresponding Entity References 7.3
Contains CerFieldHandler.
File
includes/CerFieldHandler.incView source
<?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();
}
}
Classes
Name![]() |
Description |
---|---|
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. |