cache_entity_type-1.x-dev/src/Entity/Cache/IdToCacheIdMap.php
src/Entity/Cache/IdToCacheIdMap.php
<?php
declare(strict_types=1);
namespace Drupal\cache_entity_type\Entity\Cache;
use Drupal\cache_entity_type\Exception\CacheEntityIdGenerationException;
use Drupal\cache_entity_type\Exception\InvalidEntityIdException;
/**
* A mapping of integer IDs to cache IDs.
*
* @package Drupal\cache_entity_type\Entity\Cache
*/
class IdToCacheIdMap {
/**
* Map of integer ids to cache ids.
*
* We consciously do not use an internal getter for this property to increase performance.
*
* @var string[]
*/
protected array $map;
/**
* IdToCacheIdMap constructor.
*
* @param string[] $map
* Map of integer ids to cache ids.
*/
protected function __construct(array $map) {
$this->setMap($map);
}
/**
* Creates a new object instance.
*
* @param string[] $map
* Map of integer ids to cache ids.
*
* @return self
* The created object.
*/
public static function create(array $map = []): self {
return new self($map);
}
/**
* Sets the map.
*
* @param string[] $map
* Map of integer ids to cache ids.
*/
protected function setMap(array $map): void {
$this->map = $map;
}
/**
* Returns the cache ID for given entity ID.
*
* @param int $entityId
* Entity ID.
*
* @return string|null
* The cache ID or NULL.
*/
protected function getCacheId(int $entityId): ?string {
if ($this->has($entityId)) {
return $this->map[$entityId];
}
return NULL;
}
/**
* Returns the entity ID for given cache ID.
*
* @param string $cacheId
* Cache ID.
*
* @return int|null
* The entity ID or NULL.
*/
public function getEntityId(string $cacheId): ?int {
/* TODO: Maybe consider creating an inverse map that has cacheIds as keys.
To speed up access times. */
$entityId = array_search($cacheId, $this->map, TRUE);
if (is_int($entityId)) {
return $entityId;
}
return NULL;
}
/**
* Returns multiple cache IDs.
*
* @param int[] $entityIds
* Array of entity IDs.
*
* @return string[]
* The cache IDs.
*/
public function getMultipleCacheIds(array $entityIds): array {
$cacheIds = [];
foreach ($entityIds as $entityId) {
$cacheId = $this->getCacheId((int) $entityId);
if ($cacheId !== NULL) {
$cacheIds[$entityId] = $cacheId;
}
}
return $cacheIds;
}
/**
* Returns all items.
*
* @return string[]
* The map.
*/
public function getAll(): array {
return $this->map;
}
/**
* Checks if map item type is correct.
*
* @param int $itemIndex
* The index of the item (entity ID).
*
* @return bool
* TRUE or FALSE.
*/
protected function hasMapItemCorrectType(int $itemIndex): bool {
return is_string($this->map[$itemIndex]);
}
/**
* Checks if given item exists inside map.
*
* @param int $entityId
* Entity ID.
*
* @return bool
* TRUE or FALSE.
*/
public function has(int $entityId): bool {
return isset($this->map[$entityId]) && $this->hasMapItemCorrectType($entityId);
}
/**
* Checks if the map is empty.
*
* @return bool
* TRUE or FALSE.
*/
public function isEmpty(): bool {
return empty($this->map);
}
/**
* Sets item in the map.
*
* Overwrites if item with same entity ID already exists.
*
* @param int $entityId
* The entity ID.
* @param string $cacheId
* The cache ID.
*/
public function set(int $entityId, string $cacheId): void {
$this->map[$entityId] = $cacheId;
}
/**
* Removes given item from the map.
*
* @param int $entityId
* The entity ID.
*/
public function delete(int $entityId): void {
unset($this->map[$entityId]);
}
/**
* Deletes multiple items from the map.
*
* @param int[] $entityIds
* Array containing entity IDs as values.
*
* @throws \Drupal\cache_entity_type\Exception\InvalidEntityIdException
* If an entity ID is not an integer.
*/
public function deleteMultiple(array $entityIds): void {
foreach ($entityIds as $entityId) {
if (!is_int($entityId)) {
throw new InvalidEntityIdException($entityId);
}
$this->delete($entityId);
}
}
/**
* Creates a new ID.
*
* @return int
* The generated ID.
*
* @throws \Drupal\cache_entity_type\Exception\CacheEntityIdGenerationException
* On unexpected error.
*/
public function generateNextId(): int {
if ($this->isEmpty()) {
return 1;
}
/* We do not use the highest ID + 1.
Because we want to fill in gaps caused by deleted entities.
The next cache ID should be 2 for the following example:
@code
[1] => 'cache_id.1',
[3] => 'cache_id.3',
[4] => 'cache_id.4',
@endcode
If no gaps exist, the next highest number will be used.
Also see \Drupal\Tests\cache_entity_type\Unit\Entity\Cache\IdToCacheIdMapTest::testGenerateNextId() */
if (!isset($this->map[1])) {
return 1;
}
for ($entityId = 1; $entityId < count($this->map) + 1; $entityId++) {
$nextEntityId = $entityId + 1;
if (!isset($this->map[$nextEntityId])) {
return $nextEntityId;
}
}
throw new CacheEntityIdGenerationException();
}
}
