View source
<?php
namespace Drupal\civicrm_entity;
use Drupal\civicrm_entity\Entity\CivicrmEntity;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Sql\DefaultTableMapping;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\field\FieldStorageConfigInterface;
class CiviEntityStorage extends SqlContentEntityStorage {
protected $civicrmApi;
protected $configFactory;
private function getCiviCrmApi() {
if (!$this->civicrmApi) {
$this->civicrmApi = \Drupal::service('civicrm_entity.api');
}
return $this->civicrmApi;
}
private function getConfigFactory() {
if (!$this->configFactory) {
$this->configFactory = \Drupal::configFactory();
}
return $this->configFactory;
}
private function getEntityFieldManager() {
if (property_exists(static::class, 'entityManager')) {
return $this->entityManager;
}
return $this->entityFieldManager;
}
protected function initTableLayout() {
$this->tableMapping = NULL;
$this->revisionKey = NULL;
$this->revisionTable = NULL;
$this->dataTable = NULL;
$this->revisionDataTable = NULL;
}
protected function doDelete($entities) {
foreach ($entities as $entity) {
try {
$params['id'] = $entity
->id();
$this
->getCiviCrmApi()
->delete($this->entityType
->get('civicrm_entity'), $params);
} catch (\Exception $e) {
throw $e;
}
}
$this
->doDeleteFieldItems($entities);
}
protected function doDeleteFieldItems($entities) {
$table_mapping = $this
->getTableMapping();
foreach ($entities as $entity) {
foreach ($this
->getEntityFieldManager()
->getFieldDefinitions($entity
->getEntityTypeId(), $entity
->bundle()) as $field_definition) {
$storage_definition = $field_definition
->getFieldStorageDefinition();
if (!$table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
continue;
}
$table_name = $table_mapping
->getDedicatedDataTableName($storage_definition);
$this->database
->delete($table_name)
->condition('entity_id', $entity
->id())
->execute();
}
}
}
protected function doSave($id, EntityInterface $entity) {
$return = $entity
->isNew() ? SAVED_NEW : SAVED_UPDATED;
$params = $entity
->civicrmApiNormalize();
$non_base_fields = array_filter($entity
->getFieldDefinitions(), function (FieldDefinitionInterface $definition) {
return !$definition
->getFieldStorageDefinition()
->isBaseField();
});
$non_base_fields = array_map(function (FieldDefinitionInterface $definition) {
return $definition
->getName();
}, $non_base_fields);
$result = $this
->getCiviCrmApi()
->save($this->entityType
->get('civicrm_entity'), $params);
if ($entity
->isNew()) {
$entity->{$this->idKey} = (string) $result['id'];
}
$this
->doSaveFieldItems($entity, $non_base_fields);
return $return;
}
protected function doLoadMultiple(array $ids = NULL) {
$entities = [];
if ($ids === NULL) {
$civicrm_entities = $this
->getCiviCrmApi()
->get($this->entityType
->get('civicrm_entity'));
foreach ($civicrm_entities as $civicrm_entity) {
$civicrm_entity = reset($civicrm_entity);
$entity = $this
->prepareLoadedEntity($civicrm_entity);
$entities[$entity
->id()] = $entity;
}
}
$fields = $this
->getCiviCrmApi()
->getFields($this->entityType
->get('civicrm_entity'));
$field_names = [];
foreach ($fields as $field) {
$field_names[] = $field['name'];
}
foreach ($ids as $id) {
$options = [
'id' => $id,
];
$options['return'] = $field_names;
if ($this->entityType
->get('civicrm_entity') === 'participant') {
unset($options['return']);
}
$civicrm_entity = $this
->getCiviCrmApi()
->get($this->entityType
->get('civicrm_entity'), $options);
$civicrm_entity = reset($civicrm_entity);
if ($civicrm_entity) {
if ($this->entityType
->get('civicrm_entity') === 'participant') {
$temporary = [];
foreach ($civicrm_entity as $key => $value) {
if (strpos($key, 'participant_') === 0) {
$temporary[str_replace('participant_', '', $key)] = $value;
}
else {
$temporary[$key] = $value;
}
}
$civicrm_entity = $temporary;
}
$entity = $this
->prepareLoadedEntity($civicrm_entity);
$entities[$entity
->id()] = $entity;
}
}
return $entities;
}
protected function prepareLoadedEntity(array $civicrm_entity) {
$this
->loadFromDedicatedTables($civicrm_entity);
$bundle = FALSE;
if ($this->bundleKey) {
$bundle_property = $this->entityType
->get('civicrm_bundle_property');
if (!isset($civicrm_entity[$bundle_property])) {
throw new EntityStorageException('Missing bundle for entity type ' . $this->entityTypeId);
}
$bundle_value = $civicrm_entity[$bundle_property];
$options = $this->civicrmApi
->getOptions($this->entityType
->get('civicrm_entity'), $bundle_property);
$bundle = $options[$bundle_value];
$transliteration = \Drupal::transliteration();
$bundle = SupportedEntities::optionToMachineName($bundle, $transliteration);
}
$entity = new $this->entityClass([], $this->entityTypeId, $bundle);
$this
->initFieldValues($entity, $civicrm_entity);
return $entity;
}
protected function getQueryServiceName() {
return 'entity.query.civicrm_entity';
}
public function countFieldData($storage_definition, $as_bool = FALSE) {
$table_mapping = $this
->getTableMapping();
if ($table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
$is_deleted = $storage_definition instanceof FieldStorageConfigInterface && $storage_definition
->isDeleted();
$table_name = $table_mapping
->getDedicatedDataTableName($storage_definition, $is_deleted);
$query = $this->database
->select($table_name, 't');
$or = $query
->orConditionGroup();
foreach ($storage_definition
->getColumns() as $column_name => $data) {
$or
->isNotNull($table_mapping
->getFieldColumnName($storage_definition, $column_name));
}
$query
->condition($or);
if (!$as_bool) {
$query
->fields('t', [
'entity_id',
])
->distinct(TRUE);
}
}
$count = 0;
if (isset($query)) {
if ($as_bool) {
$query
->range(0, 1)
->addExpression('1');
}
else {
$query = $query
->countQuery();
}
$count = $query
->execute()
->fetchField();
}
return $as_bool ? (bool) $count : (int) $count;
}
public function hasData() {
if (($component = $this->entityType
->get('component')) !== NULL) {
$components = $this
->getCiviCrmApi()
->getValue('Setting', [
'name' => 'enable_components',
]);
return !in_array($component, $components) ? FALSE : $this
->getCiviCrmApi()
->getCount($this->entityType
->get('civicrm_entity')) > 0;
}
return $this
->getCiviCrmApi()
->getCount($this->entityType
->get('civicrm_entity')) > 0;
}
protected function doLoadRevisionFieldItems($revision_id) {
}
protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) {
$update = !$entity
->isNew();
$table_mapping = $this
->getTableMapping();
$storage_definitions = $this
->getEntityFieldManager()
->getFieldStorageDefinitions($this->entityTypeId);
$dedicated_table_fields = [];
foreach ($names as $name) {
$storage_definition = $storage_definitions[$name];
if ($table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
$dedicated_table_fields[] = $name;
}
}
if ($dedicated_table_fields) {
$names = is_array($dedicated_table_fields) ? $dedicated_table_fields : [];
$this
->saveToDedicatedTables($entity, $update, $names);
}
}
protected function doDeleteRevisionFieldItems(ContentEntityInterface $revision) {
}
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
$this
->wrapSchemaException(function () use ($storage_definition) {
$this
->getStorageSchema()
->onFieldStorageDefinitionCreate($storage_definition);
});
}
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
$table_mapping = $this
->getTableMapping($this
->getEntityFieldManager()
->getActiveFieldStorageDefinitions($this->entityType
->id()));
if ($storage_definition instanceof FieldStorageConfigInterface && $table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
$table = $table_mapping
->getDedicatedDataTableName($storage_definition);
$this->database
->update($table)
->fields([
'deleted' => 1,
])
->execute();
}
$this
->wrapSchemaException(function () use ($storage_definition) {
$this
->getStorageSchema()
->onFieldStorageDefinitionDelete($storage_definition);
});
}
protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) {
parent::initFieldValues($entity, $values, $field_names);
$civicrm_entity_settings = $this
->getConfigFactory()
->get('civicrm_entity.settings');
$field_definitions = $entity
->getFieldDefinitions();
foreach ($field_definitions as $definition) {
$items = $entity
->get($definition
->getName());
if ($items
->isEmpty()) {
continue;
}
$main_property_name = $definition
->getFieldStorageDefinition()
->getMainPropertyName();
if ($definition
->getType() === 'text_long') {
$filter_format = $civicrm_entity_settings
->get('filter_format') ?: filter_fallback_format();
$item_values = $items
->getValue();
foreach ($item_values as $delta => $item) {
$item_values[$delta]['format'] = $filter_format;
}
$items
->setValue($item_values);
}
elseif ($definition
->getType() === 'datetime') {
$item_values = $items
->getValue();
foreach ($item_values as $delta => $item) {
if ($item[$main_property_name] === 'null') {
$item_values[$delta][$main_property_name] = NULL;
}
elseif (is_numeric($item[$main_property_name])) {
$item_values[$delta][$main_property_name] = (new \DateTime())
->setTimestamp($item[$main_property_name])
->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT);
}
else {
$datetime_format = $definition
->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
$datetime_value = (new \DateTime($item[$main_property_name], new \DateTimeZone(date_default_timezone_get())))
->setTimezone(new \DateTimeZone('UTC'))
->format($datetime_format);
$item_values[$delta][$main_property_name] = $datetime_value;
}
}
$items
->setValue($item_values);
}
}
foreach ($field_definitions as $definition) {
if (($field_metadata = $definition
->getSetting('civicrm_entity_field_metadata')) && isset($field_metadata['custom_group_id']) && $field_metadata['data_type'] === 'File') {
$items = $entity
->get($definition
->getName());
$item_values = $items
->getValue();
if (!empty($item_values)) {
$ret = [];
foreach ($item_values as $value) {
if (!isset($value['fid'])) {
continue;
}
$ret[] = [
'value' => $value['fid'],
];
}
if (!empty($ret)) {
$items
->setValue($ret);
}
}
}
}
}
public function getTableMapping(array $storage_definitions = NULL) {
$table_mapping = $this->tableMapping;
if ($table_mapping) {
return $table_mapping;
}
$table_mapping_class = DefaultTableMapping::class;
$definitions = $this
->getEntityFieldManager()
->getFieldStorageDefinitions($this->entityTypeId);
$table_mapping = new $table_mapping_class($this->entityType, $definitions);
$dedicated_table_definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
return $table_mapping
->requiresDedicatedTableStorage($definition);
});
$extra_columns = [
'bundle',
'deleted',
'entity_id',
'revision_id',
'langcode',
'delta',
];
foreach ($dedicated_table_definitions as $field_name => $definition) {
$tables = [
$table_mapping
->getDedicatedDataTableName($definition),
];
foreach ($tables as $table_name) {
$table_mapping
->setFieldNames($table_name, [
$field_name,
]);
$table_mapping
->setExtraColumns($table_name, $extra_columns);
}
}
$this->tableMapping = $table_mapping;
return $table_mapping;
}
protected function loadFromDedicatedTables(array &$values, $load_from_revision = FALSE) {
if (empty($values)) {
return;
}
$storage_definitions = [];
$table_mapping = $this
->getTableMapping();
$definitions = $this
->getEntityFieldManager()
->getFieldDefinitions($this->entityTypeId, $this->entityTypeId);
foreach ($definitions as $field_name => $field_definition) {
$storage_definition = $field_definition
->getFieldStorageDefinition();
if ($table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
$storage_definitions[$field_name] = $storage_definition;
}
}
$langcodes = array_keys($this->languageManager
->getLanguages(LanguageInterface::STATE_ALL));
foreach ($storage_definitions as $field_name => $storage_definition) {
$table = $table_mapping
->getDedicatedDataTableName($storage_definition);
$results = $this->database
->select($table, 't')
->fields('t')
->condition('entity_id', [
$values[$this
->getEntityType()
->getKey('id')],
], 'IN')
->condition('deleted', 0)
->condition('langcode', $langcodes, 'IN')
->orderBy('delta')
->execute();
foreach ($results as $row) {
if (!isset($values[$field_name])) {
$values[$field_name] = [];
}
if ($storage_definition
->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($values[$field_name]) < $storage_definition
->getCardinality()) {
$item = [];
foreach ($storage_definition
->getColumns() as $column => $attributes) {
$column_name = $table_mapping
->getFieldColumnName($storage_definition, $column);
$item[$column] = !empty($attributes['serialize']) ? unserialize($row->{$column_name}) : $row->{$column_name};
}
$values[$field_name][] = $item;
}
}
}
}
public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
return FALSE;
}
public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
return FALSE;
}
protected function saveToDedicatedTables(ContentEntityInterface $entity, $update = TRUE, $names = []) {
$vid = $entity
->getRevisionId();
$id = $entity
->id();
$bundle = $entity
->bundle();
$entity_type = $entity
->getEntityTypeId();
$default_langcode = $entity
->getUntranslated()
->language()
->getId();
$translation_langcodes = array_keys($entity
->getTranslationLanguages());
$table_mapping = $this
->getTableMapping();
if (!isset($vid)) {
$vid = $id;
}
$original = !empty($entity->original) ? $entity->original : NULL;
$definitions = $this
->getEntityFieldManager()
->getFieldDefinitions($entity_type, $bundle);
if ($names) {
$definitions = array_intersect_key($definitions, array_flip($names));
}
foreach ($definitions as $field_name => $field_definition) {
$storage_definition = $field_definition
->getFieldStorageDefinition();
if (!$table_mapping
->requiresDedicatedTableStorage($storage_definition)) {
continue;
}
if (!$entity
->isNewRevision() && $original && !$this
->hasFieldValueChanged($field_definition, $entity, $original)) {
continue;
}
$table_name = $table_mapping
->getDedicatedDataTableName($storage_definition);
$revision_name = $table_mapping
->getDedicatedRevisionTableName($storage_definition);
if ($update) {
if ($entity
->isDefaultRevision()) {
$this->database
->delete($table_name)
->condition('entity_id', $id)
->execute();
}
if ($this->entityType
->isRevisionable()) {
$this->database
->delete($revision_name)
->condition('entity_id', $id)
->condition('revision_id', $vid)
->execute();
}
}
$do_insert = FALSE;
$columns = [
'entity_id',
'revision_id',
'bundle',
'delta',
'langcode',
];
foreach ($storage_definition
->getColumns() as $column => $attributes) {
$columns[] = $table_mapping
->getFieldColumnName($storage_definition, $column);
}
$query = $this->database
->insert($table_name)
->fields($columns);
if ($this->entityType
->isRevisionable()) {
$revision_query = $this->database
->insert($revision_name)
->fields($columns);
}
$langcodes = $field_definition
->isTranslatable() ? $translation_langcodes : [
$default_langcode,
];
foreach ($langcodes as $langcode) {
$delta_count = 0;
$items = $entity
->getTranslation($langcode)
->get($field_name);
$items
->filterEmptyItems();
foreach ($items as $delta => $item) {
$do_insert = TRUE;
$record = [
'entity_id' => $id,
'revision_id' => $vid,
'bundle' => $bundle,
'delta' => $delta,
'langcode' => $langcode,
];
foreach ($storage_definition
->getColumns() as $column => $attributes) {
$column_name = $table_mapping
->getFieldColumnName($storage_definition, $column);
$value = $item->{$column};
if (!empty($attributes['serialize'])) {
$value = serialize($value);
}
$record[$column_name] = SqlContentEntityStorageSchema::castValue($attributes, $value);
}
$query
->values($record);
if ($this->entityType
->isRevisionable()) {
$revision_query
->values($record);
}
if ($storage_definition
->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition
->getCardinality()) {
break;
}
}
}
if ($do_insert) {
if ($entity
->isDefaultRevision()) {
$query
->execute();
}
if ($this->entityType
->isRevisionable()) {
$revision_query
->execute();
}
}
}
}
public function civiPreSave(EntityInterface $entity) {
if (!empty($entity->drupal_crud)) {
return;
}
$this
->doPreSave($entity);
}
public function civiPostSave(EntityInterface $entity, $update) {
if (!empty($entity->drupal_crud)) {
return;
}
$this
->doPostSave($entity, $update);
}
public function civiPreDelete(EntityInterface $entity) {
if (!empty($entity->drupal_crud)) {
return;
}
CivicrmEntity::preDelete($this, [
$entity,
]);
$this
->invokeHook('predelete', $entity);
}
public function civiPostDelete(EntityInterface $entity) {
if (!empty($entity->drupal_crud)) {
return;
}
$this
->doDeleteFieldItems([
$entity,
]);
$this
->resetCache([
$entity
->id(),
]);
CivicrmEntity::postDelete($this, [
$entity,
]);
$this
->invokeHook('delete', $entity);
}
public function getEntityTagEntityId($entityId, $entityTable) {
$api_params = [
'sequential' => 1,
'entity_id' => $entityId,
'entity_table' => $entityTable,
];
$api_results = civicrm_api3('EntityTag', 'get', $api_params);
if (!empty($api_results['values'])) {
foreach ($api_results['values'] as $delta => $result) {
if ($result['entity_id'] == $entityId) {
return $result['id'];
}
}
}
}
public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
}
}