You are here

class CiviEntityStorage in CiviCRM Entity 8.3

Defines entity class for external CiviCRM entities.

Hierarchy

Expanded class hierarchy of CiviEntityStorage

3 files declare their use of CiviEntityStorage
civicrm_entity.module in ./civicrm_entity.module
Module file for the CiviCRM Entity module.
civicrm_entity.views.inc in ./civicrm_entity.views.inc
UserCreate.php in src/Plugin/RulesAction/UserCreate.php

File

src/CiviEntityStorage.php, line 23

Namespace

Drupal\civicrm_entity
View source
class CiviEntityStorage extends SqlContentEntityStorage {

  /**
   * The CiviCRM API.
   *
   * @var \Drupal\civicrm_entity\CiviCrmApi
   */
  protected $civicrmApi;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Gets the CiviCRM API
   *
   * @return \Drupal\civicrm_entity\CiviCrmApiInterface
   *   The CiviCRM APi.
   */
  private function getCiviCrmApi() {
    if (!$this->civicrmApi) {
      $this->civicrmApi = \Drupal::service('civicrm_entity.api');
    }
    return $this->civicrmApi;
  }

  /**
   * Gets the config factory.
   *
   * @return \Drupal\Core\Config\ConfigFactoryInterface
   *   The configuration factory service.
   */
  private function getConfigFactory() {
    if (!$this->configFactory) {
      $this->configFactory = \Drupal::configFactory();
    }
    return $this->configFactory;
  }

  /**
   * Get the entity field manager.
   *
   * This is a BC layer for Drupal 8.6 entity.manager and
   * Drupal 8.7 entity_field.manager properties.
   *
   * @return \Drupal\Core\Entity\EntityFieldManagerInterface
   *   The entity field manager.
   */
  private function getEntityFieldManager() {
    if (property_exists(static::class, 'entityManager')) {
      return $this->entityManager;
    }
    return $this->entityFieldManager;
  }

  /**
   * Initializes table name variables.
   */
  protected function initTableLayout() {
    $this->tableMapping = NULL;
    $this->revisionKey = NULL;
    $this->revisionTable = NULL;
    $this->dataTable = NULL;
    $this->revisionDataTable = NULL;
  }

  /**
   * {@inheritdoc}
   */
  protected function doDelete($entities) {

    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    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);
  }

  /**
   * {@inheritdoc}
   */
  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();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function doSave($id, EntityInterface $entity) {

    /** @var \Drupal\civicrm_entity\Entity\CivicrmEntity $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;
  }

  /**
   * {@inheritdoc}
   *
   * @throws \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
   */
  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;
      }
    }

    // get all the fields
    $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') {

          // Massage the values.
          $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;
  }

  /**
   * Prepares a loaded entity.
   *
   * @param array $civicrm_entity
   *   The entity data.
   *
   * @return \Drupal\civicrm_entity\Entity\CivicrmEntity
   *   The prepared entity.
   *
   * @throws \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
   */
  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);

    // Use initFieldValues to fix CiviCRM data array to Drupal.
    $this
      ->initFieldValues($entity, $civicrm_entity);
    return $entity;
  }

  /**
   * {@inheritdoc}
   */
  protected function getQueryServiceName() {
    return 'entity.query.civicrm_entity';
  }

  /**
   * {@inheritdoc}
   *
   * @throws \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
   */
  public function countFieldData($storage_definition, $as_bool = FALSE) {

    // The table mapping contains stale data during a request when a field
    // storage definition is added, so bypass the internal storage definitions
    // and fetch the table mapping using the passed in storage definition.
    // @todo Fix this in https://www.drupal.org/node/2705205.
    $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);
      }
    }

    // @todo Find a way to count field data also for fields having custom
    //   storage. See https://www.drupal.org/node/2337753.
    $count = 0;
    if (isset($query)) {

      // If we are performing the query just to check if the field has data
      // limit the number of rows.
      if ($as_bool) {
        $query
          ->range(0, 1)
          ->addExpression('1');
      }
      else {

        // Otherwise count the number of rows.
        $query = $query
          ->countQuery();
      }
      $count = $query
        ->execute()
        ->fetchField();
    }
    return $as_bool ? (bool) $count : (int) $count;
  }

  /**
   * {@inheritdoc}
   */
  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;
  }

  /**
   * {@inheritdoc}
   */
  protected function doLoadRevisionFieldItems($revision_id) {
  }

  /**
   * {@inheritdoc}
   */
  protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) {
    $update = !$entity
      ->isNew();
    $table_mapping = $this
      ->getTableMapping();
    $storage_definitions = $this
      ->getEntityFieldManager()
      ->getFieldStorageDefinitions($this->entityTypeId);
    $dedicated_table_fields = [];

    // Collect the name of fields to be written in dedicated tables and check
    // whether shared table records need to be updated.
    foreach ($names as $name) {
      $storage_definition = $storage_definitions[$name];
      if ($table_mapping
        ->requiresDedicatedTableStorage($storage_definition)) {
        $dedicated_table_fields[] = $name;
      }
    }

    // Update dedicated table records if necessary.
    if ($dedicated_table_fields) {
      $names = is_array($dedicated_table_fields) ? $dedicated_table_fields : [];
      $this
        ->saveToDedicatedTables($entity, $update, $names);
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function doDeleteRevisionFieldItems(ContentEntityInterface $revision) {
  }

  /**
   * {@inheritdoc}
   */
  public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
    $this
      ->wrapSchemaException(function () use ($storage_definition) {
      $this
        ->getStorageSchema()
        ->onFieldStorageDefinitionCreate($storage_definition);
    });
  }

  /**
   * {@inheritdoc}
   */
  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)) {

      // Mark all data associated with the field for deletion.
      $table = $table_mapping
        ->getDedicatedDataTableName($storage_definition);
      $this->database
        ->update($table)
        ->fields([
        'deleted' => 1,
      ])
        ->execute();
    }

    // Update the field schema.
    $this
      ->wrapSchemaException(function () use ($storage_definition) {
      $this
        ->getStorageSchema()
        ->onFieldStorageDefinitionDelete($storage_definition);
    });
  }

  /**
   * {@inheritdoc}
   *
   * Provide any additional processing of values from CiviCRM API.
   */
  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();

      // Set a default format for text fields.
      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) {

          // On Contribution entities, there are dates sometimes set to the
          // string value of 'null'.
          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;

            // CiviCRM gives us the datetime in the users timezone (or no
            // timezone at all) but Drupal expects it in UTC. So, we need to
            // convert from the users timezone into UTC.
            $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);
      }
    }

    // Handle special cases for field definitions.
    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);
          }
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  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);

    /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping|\Drupal\Core\Entity\Sql\TemporaryTableMapping $table_mapping */
    $table_mapping = new $table_mapping_class($this->entityType, $definitions);

    // Add dedicated tables.
    $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;
  }

  /**
   * Loads values of fields stored in dedicated tables for a group of entities.
   *
   * @param array &$values
   *   An array of values keyed by entity ID.
   *   defaults to FALSE.
   *
   * @throws \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
   */
  protected function loadFromDedicatedTables(array &$values, $load_from_revision = FALSE) {
    if (empty($values)) {
      return;
    }

    // Collect impacted fields.
    $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;
      }
    }

    // Load field data.
    $langcodes = array_keys($this->languageManager
      ->getLanguages(LanguageInterface::STATE_ALL));
    foreach ($storage_definitions as $field_name => $storage_definition) {
      $table = $table_mapping
        ->getDedicatedDataTableName($storage_definition);

      // Ensure that only values having valid languages are retrieved. Since we
      // are loading values for multiple entities, we cannot limit the query to
      // the available translations.
      $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 = [];

          // For each column declared by the field, populate the item from the
          // prefixed database column.
          foreach ($storage_definition
            ->getColumns() as $column => $attributes) {
            $column_name = $table_mapping
              ->getFieldColumnName($storage_definition, $column);

            // Unserialize the value if specified in the column schema.
            $item[$column] = !empty($attributes['serialize']) ? unserialize($row->{$column_name}) : $row->{$column_name};
          }

          // Add the item to the field values for the entity.
          $values[$field_name][] = $item;
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {

    // The entity base table is managed by CiviCRM.
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) {

    // The entity base table is managed by CiviCRM.
    return FALSE;
  }

  /**
   * Saves values of fields that use dedicated tables.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity.
   * @param bool $update
   *   TRUE if the entity is being updated, FALSE if it is being inserted.
   * @param string[] $names
   *   (optional) The names of the fields to be stored. Defaults to all the
   *   available fields.
   *
   * @throws \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
   */
  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;

    // Determine which fields should be actually stored.
    $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;
      }

      // When updating an existing revision, keep the existing records if the
      // field values did not change.
      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);

      // Delete and insert, rather than update, in case a value was added.
      if ($update) {

        // Only overwrite the field's base table if saving the default revision
        // of an entity.
        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();
        }
      }

      // Prepare the multi-insert query.
      $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) {

          // We now know we have something to insert.
          $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);

            // Serialize the value if specified in the column schema.
            $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;
          }
        }
      }

      // Execute the query if we have values to insert.
      if ($do_insert) {

        // Only overwrite the field's base table if saving the default revision
        // of an entity.
        if ($entity
          ->isDefaultRevision()) {
          $query
            ->execute();
        }
        if ($this->entityType
          ->isRevisionable()) {
          $revision_query
            ->execute();
        }
      }
    }
  }

  /**
   * Allows CiviCRM hook to invoke presave.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to save.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   *   If the entity identifier is invalid.
   *
   * @see \Drupal\Core\Entity\ContentEntityStorageBase::doPreSave
   */
  public function civiPreSave(EntityInterface $entity) {
    if (!empty($entity->drupal_crud)) {
      return;
    }
    $this
      ->doPreSave($entity);
  }

  /**
   * Allows CiviCRM hook to invoke postsave.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The saved entity.
   * @param $update
   *   Specifies whether the entity is being updated or created.
   *
   * @see \Drupal\Core\Entity\ContentEntityStorageBase::doPostSave
   */
  public function civiPostSave(EntityInterface $entity, $update) {
    if (!empty($entity->drupal_crud)) {
      return;
    }
    $this
      ->doPostSave($entity, $update);
  }

  /**
   * Allows CiviCRM hook to invoke predelete.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to be deleted.
   *
   * @see \Drupal\Core\Entity\EntityStorageInterface::delete
   */
  public function civiPreDelete(EntityInterface $entity) {
    if (!empty($entity->drupal_crud)) {
      return;
    }
    CivicrmEntity::preDelete($this, [
      $entity,
    ]);
    $this
      ->invokeHook('predelete', $entity);
  }

  /**
   * Allows CiviCRM hook to invoke delete.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity deleted.
   *
   * @see \Drupal\Core\Entity\EntityStorageInterface::delete
   */
  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);
  }

  /**
   * Loads the EntityTag ID.
   *
   * When saving EntityTag objects, the 'id' that's passed to CiviCRM hooks is
   * not the ID of the EntityTag, but rather the object to which the EntityTag
   * applies. This provides the lookup to determing the ID of the EntityTag
   * object itself.
   *
   * @param $entityId
   *   The entity ID.
   * @param $entityTable
   *   The entity table.
   *
   * @return int|null
   *   The EntityTag object's ID, or NULL if not found.
   */
  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'];
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function onEntityTypeDelete(EntityTypeInterface $entity_type) {

    // Don't do anything.
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CiviEntityStorage::$civicrmApi protected property The CiviCRM API.
CiviEntityStorage::$configFactory protected property The config factory.
CiviEntityStorage::civiPostDelete public function Allows CiviCRM hook to invoke delete.
CiviEntityStorage::civiPostSave public function Allows CiviCRM hook to invoke postsave.
CiviEntityStorage::civiPreDelete public function Allows CiviCRM hook to invoke predelete.
CiviEntityStorage::civiPreSave public function Allows CiviCRM hook to invoke presave.
CiviEntityStorage::countFieldData public function Overrides SqlContentEntityStorage::countFieldData
CiviEntityStorage::doDelete protected function Performs storage-specific entity deletion. Overrides ContentEntityStorageBase::doDelete
CiviEntityStorage::doDeleteFieldItems protected function Deletes entity field values from the storage. Overrides SqlContentEntityStorage::doDeleteFieldItems
CiviEntityStorage::doDeleteRevisionFieldItems protected function Deletes field values of an entity revision from the storage. Overrides SqlContentEntityStorage::doDeleteRevisionFieldItems
CiviEntityStorage::doLoadMultiple protected function Overrides SqlContentEntityStorage::doLoadMultiple
CiviEntityStorage::doLoadRevisionFieldItems protected function Actually loads revision field item values from the storage. Overrides SqlContentEntityStorage::doLoadRevisionFieldItems
CiviEntityStorage::doSave protected function Performs storage-specific saving of the entity. Overrides ContentEntityStorageBase::doSave
CiviEntityStorage::doSaveFieldItems protected function Writes entity field values to the storage. Overrides SqlContentEntityStorage::doSaveFieldItems
CiviEntityStorage::getCiviCrmApi private function Gets the CiviCRM API
CiviEntityStorage::getConfigFactory private function Gets the config factory.
CiviEntityStorage::getEntityFieldManager private function Get the entity field manager.
CiviEntityStorage::getEntityTagEntityId public function Loads the EntityTag ID.
CiviEntityStorage::getQueryServiceName protected function Gets the name of the service for the query for this entity storage. Overrides SqlContentEntityStorage::getQueryServiceName
CiviEntityStorage::getTableMapping public function Gets a table mapping for the entity's SQL tables. Overrides SqlContentEntityStorage::getTableMapping
CiviEntityStorage::hasData public function Determines if the storage contains any data. Overrides EntityStorageBase::hasData
CiviEntityStorage::initFieldValues protected function Provide any additional processing of values from CiviCRM API. Overrides ContentEntityStorageBase::initFieldValues
CiviEntityStorage::initTableLayout protected function Initializes table name variables. Overrides SqlContentEntityStorage::initTableLayout
CiviEntityStorage::loadFromDedicatedTables protected function Loads values of fields stored in dedicated tables for a group of entities. Overrides SqlContentEntityStorage::loadFromDedicatedTables
CiviEntityStorage::onEntityTypeDelete public function Reacts to the deletion of the entity type. Overrides SqlContentEntityStorage::onEntityTypeDelete
CiviEntityStorage::onFieldStorageDefinitionCreate public function Reacts to the creation of a field storage definition. Overrides SqlContentEntityStorage::onFieldStorageDefinitionCreate
CiviEntityStorage::onFieldStorageDefinitionDelete public function Reacts to the deletion of a field storage definition. Overrides SqlContentEntityStorage::onFieldStorageDefinitionDelete
CiviEntityStorage::prepareLoadedEntity protected function Prepares a loaded entity.
CiviEntityStorage::requiresEntityDataMigration public function Checks if existing data would be lost if the schema changes were applied. Overrides SqlContentEntityStorage::requiresEntityDataMigration
CiviEntityStorage::requiresEntityStorageSchemaChanges public function Checks if the changes to the entity type requires storage schema changes. Overrides SqlContentEntityStorage::requiresEntityStorageSchemaChanges
CiviEntityStorage::saveToDedicatedTables protected function Saves values of fields that use dedicated tables. Overrides SqlContentEntityStorage::saveToDedicatedTables
ContentEntityStorageBase::$bundleKey protected property The entity bundle key.
ContentEntityStorageBase::$cacheBackend protected property Cache backend.
ContentEntityStorageBase::$deprecatedProperties protected property
ContentEntityStorageBase::$entityFieldManager protected property The entity field manager service.
ContentEntityStorageBase::$entityTypeBundleInfo protected property The entity bundle info.
ContentEntityStorageBase::$latestRevisionIds protected property Stores the latest revision IDs for entities.
ContentEntityStorageBase::cleanIds protected function Ensures integer entity key values are valid.
ContentEntityStorageBase::createRevision public function Creates a new revision starting off from the specified entity object. Overrides TranslatableRevisionableStorageInterface::createRevision
ContentEntityStorageBase::createTranslation public function Constructs a new entity translation object, without permanently saving it. Overrides TranslatableStorageInterface::createTranslation
ContentEntityStorageBase::createWithSampleValues public function Creates an entity with sample field values. Overrides ContentEntityStorageInterface::createWithSampleValues 1
ContentEntityStorageBase::deleteRevision public function Delete a specific entity revision. Overrides EntityStorageInterface::deleteRevision 1
ContentEntityStorageBase::doCreate protected function Performs storage-specific creation of entities. Overrides EntityStorageBase::doCreate
ContentEntityStorageBase::doPostSave protected function Performs post save entity processing. Overrides EntityStorageBase::doPostSave
ContentEntityStorageBase::doPreSave protected function Performs presave entity processing. Overrides EntityStorageBase::doPreSave
ContentEntityStorageBase::getFromPersistentCache protected function Gets entities from the persistent cache backend.
ContentEntityStorageBase::getLatestRevisionId public function Returns the latest revision identifier for an entity. Overrides RevisionableStorageInterface::getLatestRevisionId
ContentEntityStorageBase::getLatestTranslationAffectedRevisionId public function Returns the latest revision affecting the specified translation. Overrides TranslatableRevisionableStorageInterface::getLatestTranslationAffectedRevisionId
ContentEntityStorageBase::getRevisionTranslationMergeSkippedFieldNames protected function Returns an array of field names to skip when merging revision translations.
ContentEntityStorageBase::hasFieldValueChanged protected function Checks whether the field values changed compared to the original entity.
ContentEntityStorageBase::invokeFieldMethod protected function Invokes a method on the Field objects within an entity.
ContentEntityStorageBase::invokeFieldPostSave protected function Invokes the post save method on the Field objects within an entity.
ContentEntityStorageBase::invokeHook protected function Invokes a hook on behalf of the entity. Overrides EntityStorageBase::invokeHook 1
ContentEntityStorageBase::invokeStorageLoadHook protected function Invokes hook_entity_storage_load().
ContentEntityStorageBase::invokeTranslationHooks protected function Checks translation statuses and invoke the related hooks if needed.
ContentEntityStorageBase::isAnyRevisionTranslated protected function Checks whether any entity revision is translated.
ContentEntityStorageBase::isAnyStoredRevisionTranslated protected function Checks whether any stored entity revision is translated.
ContentEntityStorageBase::loadMultipleRevisions public function Loads multiple entity revisions. Overrides RevisionableStorageInterface::loadMultipleRevisions 1
ContentEntityStorageBase::loadRevision public function Load a specific entity revision. Overrides EntityStorageInterface::loadRevision 1
ContentEntityStorageBase::loadUnchanged public function Loads an unchanged entity from the database. Overrides EntityStorageBase::loadUnchanged
ContentEntityStorageBase::onFieldDefinitionCreate public function Reacts to the creation of a field. Overrides FieldDefinitionListenerInterface::onFieldDefinitionCreate
ContentEntityStorageBase::onFieldDefinitionUpdate public function Reacts to the update of a field. Overrides FieldDefinitionListenerInterface::onFieldDefinitionUpdate
ContentEntityStorageBase::populateAffectedRevisionTranslations protected function Populates the affected flag for all the revision translations.
ContentEntityStorageBase::preLoad protected function Gathers entities from a 'preload' step. Overrides EntityStorageBase::preLoad
ContentEntityStorageBase::purgeFieldData public function Purges a batch of field data. Overrides FieldableEntityStorageInterface::purgeFieldData
ContentEntityStorageBase::resetCache public function Resets the internal, static entity cache. Overrides EntityStorageBase::resetCache 1
ContentEntityStorageBase::setPersistentCache protected function Stores entities in the persistent cache backend.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DeprecatedServicePropertyTrait::__get public function Allows to access deprecated/removed properties.
EntityHandlerBase::$moduleHandler protected property The module handler to invoke hooks on. 2
EntityHandlerBase::moduleHandler protected function Gets the module handler. 2
EntityHandlerBase::setModuleHandler public function Sets the module handler for this handler.
EntityStorageBase::$entityClass protected property Name of the entity class.
EntityStorageBase::$entityType protected property Information about the entity type.
EntityStorageBase::$entityTypeId protected property Entity type ID for this storage.
EntityStorageBase::$idKey protected property Name of the entity's ID field in the entity database table.
EntityStorageBase::$memoryCache protected property The memory cache.
EntityStorageBase::$memoryCacheTag protected property The memory cache cache tag.
EntityStorageBase::$uuidKey protected property Name of entity's UUID database table field, if it supports UUIDs. 1
EntityStorageBase::$uuidService protected property The UUID service. 1
EntityStorageBase::buildCacheId protected function Builds the cache ID for the passed in entity ID. 1
EntityStorageBase::create public function Constructs a new entity object, without permanently saving it. Overrides EntityStorageInterface::create 1
EntityStorageBase::getAggregateQuery public function Gets an aggregated query instance. Overrides EntityStorageInterface::getAggregateQuery
EntityStorageBase::getEntityType public function Gets the entity type definition. Overrides EntityStorageInterface::getEntityType
EntityStorageBase::getEntityTypeId public function Gets the entity type ID. Overrides EntityStorageInterface::getEntityTypeId
EntityStorageBase::getFromStaticCache protected function Gets entities from the static cache.
EntityStorageBase::getQuery public function Gets an entity query instance. Overrides EntityStorageInterface::getQuery
EntityStorageBase::load public function Loads one entity. Overrides EntityStorageInterface::load 2
EntityStorageBase::loadByProperties public function Load entities by their property values. Overrides EntityStorageInterface::loadByProperties 3
EntityStorageBase::loadMultiple public function Loads one or more entities. Overrides EntityStorageInterface::loadMultiple 1
EntityStorageBase::postLoad protected function Attaches data to entities upon loading.
EntityStorageBase::setStaticCache protected function Stores entities in the static entity cache.
EntityStorageInterface::FIELD_LOAD_CURRENT constant Load the most recent version of an entity's field data.
EntityStorageInterface::FIELD_LOAD_REVISION constant Load the version of an entity's field data specified in the entity.
SqlContentEntityStorage::$baseTable protected property The base table of the entity.
SqlContentEntityStorage::$database protected property Active database connection.
SqlContentEntityStorage::$dataTable protected property The table that stores properties, if the entity has multilingual support.
SqlContentEntityStorage::$defaultLangcodeKey protected property The default language entity key.
SqlContentEntityStorage::$entityTypeManager protected property The entity type manager.
SqlContentEntityStorage::$fieldStorageDefinitions protected property The entity type's field storage definitions.
SqlContentEntityStorage::$langcodeKey protected property The entity langcode key. Overrides EntityStorageBase::$langcodeKey
SqlContentEntityStorage::$languageManager protected property The language manager.
SqlContentEntityStorage::$revisionDataTable protected property The table that stores revision field data if the entity supports revisions.
SqlContentEntityStorage::$revisionKey protected property Name of entity's revision database table field, if it supports revisions.
SqlContentEntityStorage::$revisionTable protected property The table that stores revisions, if the entity supports revisions.
SqlContentEntityStorage::$storageSchema protected property The entity type's storage schema object.
SqlContentEntityStorage::$tableMapping protected property The mapping of field columns to SQL tables.
SqlContentEntityStorage::$temporary protected property Whether this storage should use the temporary table mapping.
SqlContentEntityStorage::buildPropertyQuery protected function Builds an entity query. Overrides EntityStorageBase::buildPropertyQuery
SqlContentEntityStorage::buildQuery protected function Builds the query to load the entity.
SqlContentEntityStorage::createInstance public static function Instantiates a new instance of this entity handler. Overrides ContentEntityStorageBase::createInstance 1
SqlContentEntityStorage::delete public function Deletes permanently saved entities. Overrides EntityStorageBase::delete
SqlContentEntityStorage::deleteFromDedicatedTables protected function Deletes values of fields in dedicated tables for all revisions.
SqlContentEntityStorage::deleteRevisionFromDedicatedTables protected function Deletes values of fields in dedicated tables for all revisions.
SqlContentEntityStorage::doLoadMultipleRevisionsFieldItems protected function Actually loads revision field item values from the storage. Overrides ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems
SqlContentEntityStorage::finalizePurge public function Performs final cleanup after all data of a field has been purged. Overrides ContentEntityStorageBase::finalizePurge
SqlContentEntityStorage::getBaseTable public function Gets the base table name.
SqlContentEntityStorage::getCustomTableMapping public function Gets a table mapping for the specified entity type and storage definitions.
SqlContentEntityStorage::getDataTable public function Gets the data table name.
SqlContentEntityStorage::getFieldStorageDefinitions Deprecated public function Gets the base field definitions for a content entity type.
SqlContentEntityStorage::getFromStorage protected function Gets entities from the storage.
SqlContentEntityStorage::getRevisionDataTable public function Gets the revision data table name.
SqlContentEntityStorage::getRevisionTable public function Gets the revision table name.
SqlContentEntityStorage::getStorageSchema protected function Gets the entity type's storage schema object.
SqlContentEntityStorage::has protected function Determines if this entity already exists in storage. Overrides EntityStorageBase::has
SqlContentEntityStorage::isColumnSerial protected function Checks whether a field column should be treated as serial. 1
SqlContentEntityStorage::loadFromSharedTables protected function Loads values for fields stored in the shared data tables.
SqlContentEntityStorage::mapFromStorageRecords protected function Maps from storage records to entity objects, and attaches fields. Overrides EntityStorageBase::mapFromStorageRecords
SqlContentEntityStorage::mapToDataStorageRecord protected function Maps from an entity object to the storage record of the field data.
SqlContentEntityStorage::mapToStorageRecord protected function Maps from an entity object to the storage record.
SqlContentEntityStorage::onBundleCreate public function Reacts to a bundle being created. Overrides EntityBundleListenerInterface::onBundleCreate
SqlContentEntityStorage::onBundleDelete public function Reacts to a bundle being deleted. Overrides EntityBundleListenerInterface::onBundleDelete
SqlContentEntityStorage::onEntityTypeCreate public function Reacts to the creation of the entity type. Overrides EntityTypeListenerInterface::onEntityTypeCreate
SqlContentEntityStorage::onEntityTypeUpdate public function Reacts to the update of the entity type. Overrides EntityTypeListenerInterface::onEntityTypeUpdate
SqlContentEntityStorage::onFieldableEntityTypeCreate public function Reacts to the creation of the fieldable entity type. Overrides EntityTypeListenerInterface::onFieldableEntityTypeCreate
SqlContentEntityStorage::onFieldableEntityTypeUpdate public function Reacts to the update of a fieldable entity type. Overrides EntityTypeListenerInterface::onFieldableEntityTypeUpdate
SqlContentEntityStorage::onFieldDefinitionDelete public function Reacts to the deletion of a field. Overrides ContentEntityStorageBase::onFieldDefinitionDelete
SqlContentEntityStorage::onFieldStorageDefinitionUpdate public function Reacts to the update of a field storage definition. Overrides ContentEntityStorageBase::onFieldStorageDefinitionUpdate
SqlContentEntityStorage::purgeFieldItems protected function Removes field items from storage per entity during purge. Overrides ContentEntityStorageBase::purgeFieldItems
SqlContentEntityStorage::readFieldItemsToPurge protected function Reads values to be purged for a single field. Overrides ContentEntityStorageBase::readFieldItemsToPurge
SqlContentEntityStorage::requiresFieldDataMigration public function Checks if existing data would be lost if the schema changes were applied. Overrides DynamicallyFieldableEntityStorageSchemaInterface::requiresFieldDataMigration
SqlContentEntityStorage::requiresFieldStorageSchemaChanges public function Checks if the changes to the storage definition requires schema changes. Overrides DynamicallyFieldableEntityStorageSchemaInterface::requiresFieldStorageSchemaChanges
SqlContentEntityStorage::restore public function Restores a previously saved entity. Overrides EntityStorageBase::restore
SqlContentEntityStorage::save public function Saves the entity permanently. Overrides EntityStorageBase::save 1
SqlContentEntityStorage::saveRevision protected function Saves an entity revision.
SqlContentEntityStorage::saveToSharedTables protected function Saves fields that use the shared tables.
SqlContentEntityStorage::setEntityType public function Updates the wrapped entity type definition.
SqlContentEntityStorage::setFieldStorageDefinitions public function Updates the internal list of field storage definitions.
SqlContentEntityStorage::setTableMapping public function Sets the wrapped table mapping definition.
SqlContentEntityStorage::setTemporary public function Changes the temporary state of the storage.
SqlContentEntityStorage::storageDefinitionIsDeleted Deprecated protected function Determines whether the passed field has been already deleted.
SqlContentEntityStorage::wrapSchemaException protected function Wraps a database schema exception into an entity storage exception.
SqlContentEntityStorage::__construct public function Constructs a SqlContentEntityStorage object. Overrides ContentEntityStorageBase::__construct 1
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.