You are here

class DeveloperStorage in Apigee Edge 8

Entity storage implementation for developers.

Hierarchy

Expanded class hierarchy of DeveloperStorage

File

src/Entity/Storage/DeveloperStorage.php, line 42

Namespace

Drupal\apigee_edge\Entity\Storage
View source
class DeveloperStorage extends EdgeEntityStorageBase implements DeveloperStorageInterface {

  /**
   * The developer controller service.
   *
   * @var \Drupal\apigee_edge\Entity\Controller\DeveloperControllerInterface
   */
  private $developerController;

  /**
   * Developer company membership cache.
   *
   * @var \Drupal\apigee_edge\Entity\DeveloperCompaniesCacheInterface
   */
  private $developerCompanies;

  /**
   * Constructs an DeveloperStorage instance.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   The cache backend to be used.
   * @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface $memory_cache
   *   The memory cache.
   * @param \Drupal\Component\Datetime\TimeInterface $system_time
   *   The system time.
   * @param \Apigee\Edge\Api\Management\Controller\DeveloperControllerInterface $developer_controller
   *   The developer controller service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
   *   Configuration factory.
   * @param \Drupal\apigee_edge\Entity\DeveloperCompaniesCacheInterface $developer_companies_cache
   *   Developer company membership cache.
   */
  public function __construct(EntityTypeInterface $entity_type, CacheBackendInterface $cache_backend, MemoryCacheInterface $memory_cache, TimeInterface $system_time, DeveloperControllerInterface $developer_controller, ConfigFactoryInterface $config, DeveloperCompaniesCacheInterface $developer_companies_cache) {
    parent::__construct($entity_type, $cache_backend, $memory_cache, $system_time);
    $this->cacheExpiration = $config
      ->get('apigee_edge.developer_settings')
      ->get('cache_expiration');
    $this->developerController = $developer_controller;
    $this->developerCompanies = $developer_companies_cache;
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    return new static($entity_type, $container
      ->get('cache.apigee_edge_entity'), $container
      ->get('entity.memory_cache'), $container
      ->get('datetime.time'), $container
      ->get('apigee_edge.controller.developer'), $container
      ->get('config.factory'), $container
      ->get('apigee_edge.controller.cache.developer_companies'));
  }

  /**
   * {@inheritdoc}
   */
  protected function entityController() : EdgeEntityControllerInterface {
    if ($this->developerController instanceof EntityCacheAwareControllerInterface) {
      return new CachedManagementApiEdgeEntityControllerProxy($this->developerController);
    }
    return new ManagementApiEdgeEntityControllerProxy($this->developerController);
  }

  /**
   * {@inheritdoc}
   *
   * We had to override this function because a developer can be referenced
   * by email or developer id (UUID) on Apigee Edge. In Drupal we use the email
   * as primary and because of that if we try to load a developer by UUID then
   * we get back an integer because EntityStorageBase::loadMultiple() returns
   * an array where entities keyed by their Drupal ids.
   *
   * @see \Drupal\Core\Entity\EntityStorageBase::loadMultiple()
   */
  public function loadMultiple(array $ids = NULL) {
    $entities = parent::loadMultiple($ids);
    if ($ids) {
      $entities_by_developer_id = array_reduce($entities, function ($carry, $item) {

        // It could be an integer if developer UUID has been used as as an id
        // instead of the email address.
        if (is_object($item)) {

          /** @var \Drupal\apigee_edge\Entity\DeveloperInterface $item */
          $carry[$item
            ->getDeveloperId()] = $item;
        }
        return $carry;
      }, []);
      $entities = array_merge($entities, $entities_by_developer_id);
      $requested_entities = [];

      // Ensure that the returned array is ordered the same as the original
      // $ids array if this was passed in and remove any invalid ids.
      $passed_ids = array_flip(array_intersect_key(array_flip($ids), $entities));
      foreach ($passed_ids as $id) {
        $requested_entities[$id] = $entities[$id];
      }
      $entities = $requested_entities;
    }
    return $entities;
  }

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

    /** @var \Drupal\apigee_edge\Entity\DeveloperInterface $entity */
    $developer_status = $entity
      ->getStatus();
    $result = parent::doSave($id, $entity);

    // In case of entity update, the original email must be
    // cleared before a new API call.
    if ($result === SAVED_UPDATED) {
      $entity
        ->resetOriginalEmail();
    }

    // Change the status of the developer in Apigee Edge.
    // TODO Only change it if it has changed.
    try {
      $this->developerController
        ->setStatus($entity
        ->id(), $developer_status);
    } catch (ApiException $exception) {
      throw new EntityStorageException($exception
        ->getMessage(), $exception
        ->getCode(), $exception);
    }

    // Apply status change in the entity object as well.
    $entity
      ->setStatus($developer_status);
    return $result;
  }

  /**
   * {@inheritdoc}
   */
  protected function getPersistentCacheTags(EntityInterface $entity) {

    /** @var \Drupal\apigee_edge\Entity\Developer $entity */
    $cache_tags = parent::getPersistentCacheTags($entity);
    $cache_tags = $this
      ->sanitizeCacheTags($entity
      ->id(), $cache_tags);

    // Create tags by developerId (besides email address).
    $cache_tags[] = "{$this->entityTypeId}:{$entity->uuid()}";
    $cache_tags[] = "{$this->entityTypeId}:{$entity->uuid()}:values";

    // Also add a tag by developer's Drupal user id to ensure that cached
    // developer data is invalidated when the related Drupal user gets changed
    // or deleted.
    if ($entity
      ->getOwnerId()) {
      $cache_tags[] = "user:{$entity->getOwnerId()}";
    }
    return $cache_tags;
  }

  /**
   * Sanitizes accented characters from a generated cache tag.
   *
   * If $id is an email address (which the default entity id of a developer
   * entity in Drupal) and it contains accented characters it must be sanitized
   * before they could be saved to the database backend.
   *
   * @param string $id
   *   Developer UUID or email address.
   * @param string $tag
   *   Generated cache tag that may or may not contains the id.
   *
   * @return string
   *   The sanitized cache tags.
   */
  private function sanitizeCacheTag(string $id, string $tag) : string {
    return str_replace($id, filter_var($id, FILTER_SANITIZE_ENCODED), $tag);
  }

  /**
   * Sanitizes accented characters from generated cache tags.
   *
   * @param string $id
   *   Developer UUID or email address.
   * @param array $cache_tags
   *   Array of generated cache tags.
   *
   * @return array
   *   Array of sanitized cache tags.
   *
   * @see sanitizeCacheTag()
   */
  private function sanitizeCacheTags(string $id, array $cache_tags) : array {
    return array_map(function ($tag) use ($id) {
      return $this
        ->sanitizeCacheTag($id, $tag);
    }, $cache_tags);
  }

  /**
   * {@inheritdoc}
   */
  protected function setPersistentCache(array $entities) {
    parent::setPersistentCache($entities);
    if (!$this->entityType
      ->isPersistentlyCacheable()) {
      return;
    }

    // Create a separate cache entry that uses developer id in the cache id
    // instead of the email address. This way we can load a developer from
    // cache by using both ids.
    foreach ($entities as $entity) {

      /** @var \Drupal\apigee_edge\Entity\Developer $entity */
      $this->cacheBackend
        ->set($this
        ->buildCacheId($entity
        ->getDeveloperId()), $entity, $this
        ->getPersistentCacheExpiration(), $this
        ->getPersistentCacheTags($entity));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function resetCache(array $ids = NULL) {
    parent::resetCache($ids);

    // Ensure that if ids contains email addresses we also invalidate cache
    // entries that refers to the same entities by developer id and vice-versa.
    // See getPersistentCacheTags() for more insight.
    if ($ids && $this->entityType
      ->isPersistentlyCacheable()) {
      $tags = [];
      foreach ($ids as $id) {
        $tags[] = $this
          ->sanitizeCacheTag($id, "{$this->entityTypeId}:{$id}");
        $tags[] = $this
          ->sanitizeCacheTag($id, "{$this->entityTypeId}:{$id}:values");
      }
      Cache::invalidateTags($tags);
    }

    // Remove related entries in the developer company membership cache.
    if (empty($ids)) {
      $this->developerCompanies
        ->remove();
    }
    else {

      // We can not be sure whether ids are developer ids (UUIDs) or email
      // addresses so we invalidate entries by tags.
      $this->developerCompanies
        ->invalidate(array_map(function ($id) {
        return "developer:{$id}";
      }, $ids));
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
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
DeveloperStorage::$developerCompanies private property Developer company membership cache.
DeveloperStorage::$developerController private property The developer controller service.
DeveloperStorage::createInstance public static function Instantiates a new instance of this entity handler. Overrides EdgeEntityStorageBase::createInstance
DeveloperStorage::doSave protected function Performs storage-specific saving of the entity. Overrides EdgeEntityStorageBase::doSave
DeveloperStorage::entityController protected function Returns the wrapped controller instance used by this storage. Overrides EdgeEntityStorageBase::entityController
DeveloperStorage::getPersistentCacheTags protected function Generates cache tags for entities. Overrides EdgeEntityStorageBase::getPersistentCacheTags
DeveloperStorage::loadMultiple public function We had to override this function because a developer can be referenced by email or developer id (UUID) on Apigee Edge. In Drupal we use the email as primary and because of that if we try to load a developer by UUID then we get back an integer because… Overrides EntityStorageBase::loadMultiple
DeveloperStorage::resetCache public function Resets the internal, static entity cache. Overrides EdgeEntityStorageBase::resetCache
DeveloperStorage::sanitizeCacheTag private function Sanitizes accented characters from a generated cache tag.
DeveloperStorage::sanitizeCacheTags private function Sanitizes accented characters from generated cache tags.
DeveloperStorage::setPersistentCache protected function Stores entities in the persistent cache backend. Overrides EdgeEntityStorageBase::setPersistentCache
DeveloperStorage::__construct public function Constructs an DeveloperStorage instance. Overrides EdgeEntityStorageBase::__construct
EdgeEntityStorageBase::$cacheBackend protected property Cache backend.
EdgeEntityStorageBase::$cacheExpiration protected property Number of seconds until an entity can be served from cache.
EdgeEntityStorageBase::$systemTime protected property The system time.
EdgeEntityStorageBase::buildCacheId protected function Builds the cache ID for the passed in entity ID. Overrides EntityStorageBase::buildCacheId
EdgeEntityStorageBase::createNewInstance protected function Creates a new Drupal entity from an SDK entity.
EdgeEntityStorageBase::deleteRevision public function Delete a specific entity revision. Overrides EntityStorageInterface::deleteRevision
EdgeEntityStorageBase::doDelete protected function Performs storage-specific entity deletion. Overrides EntityStorageBase::doDelete 1
EdgeEntityStorageBase::doLoadMultiple protected function Performs storage-specific loading of entities. Overrides EntityStorageBase::doLoadMultiple
EdgeEntityStorageBase::getFromPersistentCache protected function Gets entities from the persistent cache backend.
EdgeEntityStorageBase::getFromStorage protected function Gets entities from the storage. 1
EdgeEntityStorageBase::getPersistentCacheExpiration protected function Number of seconds after a cache item expires.
EdgeEntityStorageBase::getQueryServiceName protected function Gets the name of the service for the query for this entity storage. Overrides EntityStorageBase::getQueryServiceName
EdgeEntityStorageBase::has protected function Determines if this entity already exists in storage. Overrides EntityStorageBase::has
EdgeEntityStorageBase::invokeStorageLoadHook protected function Invokes hook_entity_storage_load().
EdgeEntityStorageBase::loadRevision public function Load a specific entity revision. Overrides EntityStorageInterface::loadRevision
EdgeEntityStorageBase::loadUnchanged public function Loads an unchanged entity from the database. Overrides EntityStorageBase::loadUnchanged 1
EdgeEntityStorageBase::processLoadedEntities final protected function Processes loaded (SDK) entities to Drupal entities.
EdgeEntityStorageBase::resetControllerCache protected function Resets entity controller's cache if it is a cached entity controller.
EdgeEntityStorageBase::SAVED_UNKNOWN public constant Initial status for saving a item to Apigee Edge.
EdgeEntityStorageBase::withController protected function Wraps communication with Apigee Edge.
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::$langcodeKey protected property The name of the entity langcode property. 1
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::buildPropertyQuery protected function Builds an entity query. 1
EntityStorageBase::create public function Constructs a new entity object, without permanently saving it. Overrides EntityStorageInterface::create 1
EntityStorageBase::delete public function Deletes permanently saved entities. Overrides EntityStorageInterface::delete 2
EntityStorageBase::doCreate protected function Performs storage-specific creation of entities. 3
EntityStorageBase::doPostSave protected function Performs post save entity processing. 1
EntityStorageBase::doPreSave protected function Performs presave entity processing. 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::hasData public function Determines if the storage contains any data. Overrides EntityStorageInterface::hasData 3
EntityStorageBase::invokeHook protected function Invokes a hook on behalf of the entity. 2
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::mapFromStorageRecords protected function Maps from storage records to entity objects. 4
EntityStorageBase::postLoad protected function Attaches data to entities upon loading.
EntityStorageBase::preLoad protected function Gathers entities from a 'preload' step. 1
EntityStorageBase::restore public function Restores a previously saved entity. Overrides EntityStorageInterface::restore 1
EntityStorageBase::save public function Saves the entity permanently. Overrides EntityStorageInterface::save 4
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.
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.