View source
<?php
class CerException extends Exception {
}
interface CerHandlerInterface {
public function __construct($preset, $entity);
public function insert();
public function update();
public function delete();
public function references($entity);
public function referencedBy($entity);
public function referenceable($entity);
public function reference($entity);
public function dereference($entity);
}
abstract class CerHandlerBase {
protected $local;
protected $remote;
public function __construct($preset) {
$keys = explode('*', $preset);
if (sizeof($keys) != 6) {
throw new CerException(t('Invalid configuration: @preset', array(
'@preset' => $preset,
)));
}
$this->local = field_info_instance($keys[0], $keys[2], $keys[1]);
if ($this->local) {
$this->local['field'] = field_info_field($keys[2]);
}
else {
throw new CerException(t('Local field instance does not exist.'));
}
$this->remote = field_info_instance($keys[3], $keys[5], $keys[4]);
if ($this->remote) {
$this->remote['field'] = field_info_field($keys[5]);
}
else {
throw new CerException(t('Remote field instance does not exist.'));
}
}
}
class CerHandler extends CerHandlerBase implements CerHandlerInterface {
protected $entity;
protected $id;
public function __construct($preset, $entity) {
parent::__construct($preset);
$extract_ids = entity_extract_IDs($this->local['entity_type'], $entity);
$this->id = array_shift($extract_ids);
$this->entity = $entity;
}
public function insert($ids = NULL) {
if (empty($ids)) {
$entities = $this
->getReferencedEntities();
}
else {
$entities = entity_load($this->remote['entity_type'], $ids);
}
foreach ($entities as $referenced_entity) {
$this
->reference($referenced_entity);
_cer_update($this->remote['entity_type'], $referenced_entity);
}
}
public function update() {
$original = isset($this->entity->original) ? $this->entity->original : $this->entity;
$deleted = array_diff($this
->getReferenceIDs($original, $this->local), $this
->getLocalReferenceIDs());
if ($deleted) {
$entities = entity_load($this->remote['entity_type'], $deleted);
foreach ($entities as $referenced_entity) {
$this
->dereference($referenced_entity);
_cer_update($this->remote['entity_type'], $referenced_entity);
}
}
$added = array_diff($this
->getLocalReferenceIDs(), $this
->getReferenceIDs($original, $this->local));
if (!empty($added)) {
$this
->insert($added);
}
}
public function delete() {
foreach ($this
->getReferencedEntities() as $referenced_entity) {
$this
->dereference($referenced_entity);
_cer_update($this->remote['entity_type'], $referenced_entity);
}
}
public function references($entity) {
return in_array($this
->getRemoteEntityID($entity), $this
->getLocalReferenceIDs());
}
public function referencedBy($entity) {
return in_array($this->id, $this
->getRemoteReferenceIDs($entity));
}
public function referenceable($entity) {
$id = $this
->getRemoteEntityID($entity);
$allowed = array(
entityreference_get_selection_handler($this->local['field'], $this->local, $this->local['entity_type'], $this->entity)
->validateReferencableEntities(array(
$id,
)),
entityreference_get_selection_handler($this->remote['field'], $this->remote, $this->remote['entity_type'], $entity)
->validateReferencableEntities(array(
$this->id,
)),
);
return in_array($id, $allowed[0]) && in_array($this->id, $allowed[1]);
}
public function reference($entity) {
if ($this
->referenceable($entity)) {
try {
$this
->addReferenceTo($entity);
} catch (CerException $e) {
}
try {
$this
->addReferenceFrom($entity);
} catch (CerException $e) {
}
}
else {
$variables = array(
'!local_field' => $this->local['field_name'],
'!local_type' => $this->local['entity_type'],
'!local_id' => $this->id,
'!remote_field' => $this->remote['field_name'],
'!remote_type' => $this->remote['entity_type'],
'!remote_id' => $this
->getRemoteEntityID($entity),
);
watchdog('cer', 'Failed to reference !remote_field on !remote_type !remote_id from !local_field on !local_type !local_id.', $variables, WATCHDOG_ERROR);
}
}
public function dereference($entity) {
if ($this
->references($entity)) {
$id = $this
->getRemoteEntityID($entity);
foreach ($this->entity->{$this->local['field_name']} as $language => $references) {
foreach ($references as $delta => $reference) {
if ($reference['target_id'] == $id) {
unset($this->entity->{$this->local['field_name']}[$language][$delta]);
}
}
}
}
if ($this
->referencedBy($entity)) {
foreach ($entity->{$this->remote['field_name']} as $language => $references) {
foreach ($references as $delta => $reference) {
if ($reference['target_id'] == $this->id) {
unset($entity->{$this->remote['field_name']}[$language][$delta]);
}
}
}
}
}
protected function addReferenceFrom($entity) {
if ($this
->referencedBy($entity)) {
throw new CerException(t('Cannot create duplicate reference from remote entity.'));
}
elseif ($this
->filled($this
->getRemoteReferenceIDs($entity), $this->remote['field'])) {
throw new CerException(t('Remote field cannot support any more references.'));
}
else {
$languages = field_available_languages($this->remote['entity_type'], $this->remote['field']);
foreach ($languages as $language) {
$entity->{$this->remote['field_name']}[$language][] = array(
'target_id' => $this->id,
);
}
}
}
protected function addReferenceTo($entity) {
$id = $this
->getRemoteEntityID($entity);
if ($this
->references($entity)) {
throw new CerException(t('Cannot create duplicate reference to remote entity.'));
}
elseif ($this
->filled($this
->getLocalReferenceIDs(), $this->local['field'])) {
throw new CerException(t('Local field cannot support any more references.'));
}
else {
$languages = field_available_languages($this->local['entity_type'], $this->local['field']);
foreach ($languages as $language) {
$this->entity->{$this->local['field_name']}[$language][] = array(
'target_id' => $id,
);
}
}
}
protected function getRemoteEntityID($entity) {
$extract_ids = entity_extract_IDs($this->remote['entity_type'], $entity);
return array_shift($extract_ids);
}
protected function getReferencedEntities() {
$IDs = $this
->getLocalReferenceIDs();
return $IDs ? entity_load($this->remote['entity_type'], $IDs) : array();
}
protected function getLocalReferenceIDs() {
return $this
->getReferenceIDs($this->entity, $this->local);
}
protected function getRemoteReferenceIDs($entity) {
return $this
->getReferenceIDs($entity, $this->remote);
}
private function filled($references, $field) {
return $field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && sizeof($references) >= $field['cardinality'];
}
private function getReferenceIDs($entity, $field) {
$IDs = array();
if (isset($entity->{$field['field_name']})) {
foreach ($entity->{$field['field_name']} as $references) {
foreach ($references as $reference) {
$IDs[] = $reference['target_id'];
}
}
}
return array_unique(array_filter($IDs));
}
}