commerce_inventory-8.x-1.0-alpha6/src/Entity/Storage/InventoryAdjustmentStorage.php
src/Entity/Storage/InventoryAdjustmentStorage.php
<?php
namespace Drupal\commerce_inventory\Entity\Storage;
use Drupal\commerce\CommerceContentEntityStorage;
use Drupal\commerce_inventory\Entity\InventoryAdjustmentInterface;
use Drupal\commerce_inventory\Entity\InventoryItemInterface;
use Drupal\commerce_inventory\InventoryAdjustmentTypeManager;
use Drupal\commerce_inventory\InventoryProviderManager;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Defines the storage handler class for Commerce Inventory Adjustment entities.
*
* This extends the base storage class, adding required special handling for
* Commerce Inventory Adjustment entities.
*
* @ingroup commerce_inventory
*/
class InventoryAdjustmentStorage extends CommerceContentEntityStorage implements InventoryAdjustmentStorageInterface {
/**
* The Inventory Adjustment type manager.
*
* @var \Drupal\commerce_inventory\InventoryAdjustmentTypeManager
*/
protected $adjustmentTypeManager;
/**
* Loaded Inventory Adjustment type plugins.
*
* @var \Drupal\commerce_inventory\Plugin\Commerce\InventoryAdjustmentType\InventoryAdjustmentTypeInterface[]
*/
protected $adjustmentTypes = [];
/**
* The inventory provider manager.
*
* @var \Drupal\commerce_inventory\InventoryProviderManager
*/
protected $inventoryProviderManager;
/**
* Loaded inventory provider instances, keyed by bundle.
*
* @var \Drupal\commerce_inventory\Plugin\Commerce\InventoryProvider\InventoryProviderInterface[]
*/
protected $inventoryProviders = [];
/**
* The Commerce Log entity storage.
*
* @var \Drupal\commerce_log\LogStorageInterface
*/
protected $logStorage;
/**
* Temporary array of adjustment back-references.
*
* The temporary array values are used to bypass adjustments with unsaved
* back references.
*
* @var array
*/
protected $tempReferences = [];
/**
* Constructs a InventoryAdjustmentStorage object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Database\Connection $database
* The database connection to be used.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend to be used.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param \Drupal\commerce_inventory\InventoryAdjustmentTypeManager $adjustment_type_manager
* The Inventory Adjustment type manager.
* @param \Drupal\commerce_inventory\InventoryProviderManager $inventory_provider_manager
* The inventory provider manager.
*/
public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache, EventDispatcherInterface $event_dispatcher, InventoryAdjustmentTypeManager $adjustment_type_manager, InventoryProviderManager $inventory_provider_manager) {
parent::__construct($entity_type, $database, $entity_manager, $cache, $language_manager, $memory_cache, $event_dispatcher);
$this->adjustmentTypeManager = $adjustment_type_manager;
$this->inventoryProviderManager = $inventory_provider_manager;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('database'),
$container->get('entity.manager'),
$container->get('cache.entity'),
$container->get('language_manager'),
$container->get('entity.memory_cache'),
$container->get('event_dispatcher'),
$container->get('plugin.manager.commerce_inventory_adjustment_type'),
$container->get('plugin.manager.commerce_inventory_provider')
);
}
/**
* Gets an Inventory Adjustment Type plugin.
*
* @param string $adjustment_type
* The Inventory Adjustment Type ID.
*
* @return \Drupal\commerce_inventory\Plugin\Commerce\InventoryAdjustmentType\InventoryAdjustmentTypeInterface
* The loaded Adjustment Type plugin instance.
*/
protected function getAdjustmentType($adjustment_type) {
if (!array_key_exists($adjustment_type, $this->adjustmentTypes)) {
$this->adjustmentTypes[$adjustment_type] = $this->adjustmentTypeManager->createInstance($adjustment_type);
}
return $this->adjustmentTypes[$adjustment_type];
}
/**
* Gets the Commerce Log entity storage.
*
* @return \Drupal\commerce_log\LogStorageInterface
* The Commerce Log entity storage instance.
*/
protected function getLogStorage() {
if (is_null($this->logStorage)) {
$this->logStorage = $this->entityManager->getStorage('commerce_log');
}
return $this->logStorage;
}
/**
* Loads a provider by bundle ID.
*
* @param string $bundle
* An entity's bundle ID.
*
* @return \Drupal\commerce_inventory\Plugin\Commerce\InventoryProvider\InventoryProviderInterface
* The bundle's inventory provider instance.
*/
protected function getProvider($bundle) {
if (!array_key_exists($bundle, $this->inventoryProviders)) {
$this->inventoryProviders[$bundle] = $this->inventoryProviderManager->createInstance($bundle);
}
return $this->inventoryProviders[$bundle];
}
/**
* {@inheritdoc}
*/
public function createAdjustment($adjustment_type_id, InventoryItemInterface $item, $quantity, array $values = [], InventoryItemInterface $related_item = NULL, $save = TRUE) {
/** @var \Drupal\commerce_inventory\Entity\InventoryAdjustmentInterface $adjustment */
/** @var \Drupal\commerce_inventory\Entity\InventoryAdjustmentInterface $related_adjustment */
// Clean values of dynamic data.
$values = array_diff_key($values, [
'commerce_inventory_adjustment_type' => NULL,
'created' => NULL,
'quantity' => NULL,
'related_adjustment' => NULL,
]);
// Create initial adjustment.
$adjustment_type = $this->getAdjustmentType($adjustment_type_id);
$adjustment_values = [
'item_id' => $item->id(),
'commerce_inventory_adjustment_type' => $adjustment_type_id,
'quantity' => $quantity,
] + $values;
$adjustment = $this->create($adjustment_values);
// Related adjustment requirement check.
if ($adjustment_type->hasRelatedAdjustmentType()) {
// Related Item required.
if (is_null($related_item)) {
throw new EntityStorageException("Related Inventory Item required for {$adjustment_type->getLabel()} adjustment.");
}
// Create related adjustment.
$related_type_id = $adjustment_type->getRelatedAdjustmentTypeId();
$related_values = [
'item_id' => $related_item->id(),
'commerce_inventory_adjustment_type' => $related_type_id,
'quantity' => $quantity,
] + $values;
$related_adjustment = $this->create($related_values);
// Relate related adjustment entity to initial adjustment.
$adjustment->setRelatedAdjustment($related_adjustment);
}
// Save adjustment (which saves related adjustment).
if ($save) {
$adjustment->save();
}
return $adjustment;
}
/**
* {@inheritdoc}
*/
public function calculateQuantity($item_id) {
$table = $this->getDataTable()?:$this->getBaseTable();
$query = $this->database->select($table, 'ia');
$query->condition('item_id', $item_id);
$query->addExpression('sum(quantity)', 'total');
$total = $query->execute()->fetchObject()->total;
return round($total, 5);
}
/**
* {@inheritdoc}
*/
public function getQuantitySelectQuery() {
$table = $this->getDataTable() ?: $this->getBaseTable();
$query = $this->database->select($table, 'ia');
$query->addField('ia', 'item_id', 'inventory_item_id');
$query->addField('ia', 'quantity');
return $query;
}
/**
* {@inheritdoc}
*/
protected function doPreSave(EntityInterface $entity) {
/** @var \Drupal\commerce_inventory\Entity\InventoryAdjustmentInterface $entity */
$related_entity = $entity->getRelatedAdjustment();
// Make sure adjustment is valid in case createAdjustment wasn't used.
if ($entity->getType()->hasRelatedAdjustmentType()) {
// Skip validation if this entity is a temp reference.
if (in_array($entity->uuid(), $this->tempReferences)) {
// Do nothing.
}
// Related adjustment isn't an adjustment entity.
elseif ($related_entity instanceof InventoryAdjustmentInterface == FALSE) {
throw new EntityStorageException("Related Adjustment required for {$entity->getType()->getLabel()} adjustments.");
}
// Related adjustment type doesn't match correct related adjustment type.
elseif ($related_entity->bundle() !== $entity->getType()->getRelatedAdjustmentTypeId()) {
throw new EntityStorageException("Invalid Related Adjustment type for {$entity->getType()->getLabel()} adjustment.");
}
}
// An entity is related but isn't supposed to be.
elseif (!is_null($related_entity)) {
throw new EntityStorageException("Related Adjustment not allowed for {$entity->getType()->getLabel()} adjustments.");
}
// Run provider presave only on initial entity and on entity creation.
if ($entity->isNew() && !in_array($entity->uuid(), $this->tempReferences) && !$entity->getData('skip_provider_adjustment_pre_save', FALSE)) {
$this->getProvider($entity->getItem()->bundle())->onAdjustmentPreSave($entity);
}
// Add related-adjustment to temporary references array. Remove
// related-adjustment's back-reference to this entity if it is set.
if ($related_entity instanceof InventoryAdjustmentInterface && $related_entity->isNew()) {
$this->tempReferences[$entity->uuid()] = $related_entity->uuid();
$related_entity->set('related_adjustment', NULL);
}
return parent::doPreSave($entity);
}
/**
* {@inheritdoc}
*/
protected function doPostSave(EntityInterface $entity, $update) {
/** @var \Drupal\commerce_inventory\Entity\InventoryAdjustmentInterface $entity */
// Add entity item to cid array for cache quantity invalidation.
$cache_tags = ['quantity:commerce_inventory_item:' . $entity->getItemId()];
if (!in_array($entity->uuid(), $this->tempReferences)) {
if ($related_entity = $entity->getRelatedAdjustment()) {
// Relate adjustment entity back to other entity.
if (array_key_exists($entity->uuid(), $this->tempReferences) && $this->tempReferences[$entity->uuid()] == $related_entity->uuid()) {
$related_entity->set('related_adjustment', $entity->id());
$related_entity->save();
unset($this->tempReferences[$entity->uuid()]);
}
// Add related entity item to cid array for cache quantity invalidation.
$cache_tags[] = 'quantity:commerce_inventory_item:' . $related_entity->getItemId();
}
}
// Run provider adjustment.
if (!$update && !$entity->getData('skip_provider_adjustment_post_save', FALSE)) {
$this->getProvider($entity->getItem()->bundle())->onAdjustmentPostSave($entity);
}
// Add to logs.
$this->getLogStorage()->generate($entity->getItem(), 'inventory_item_adjustment', [
'adjustment' => $entity->getType()->getLabel(),
'quantity' => $entity->getQuantity(),
'total' => $this->calculateQuantity($entity->getItemId()),
])->save();
// Invalidate cache.
Cache::invalidateTags($cache_tags);
parent::doPostSave($entity, $update);
}
/**
* {@inheritdoc}
*/
public function __sleep() {
$this->adjustmentTypeManager = NULL;
$this->adjustmentTypes = [];
$this->inventoryProviderManager = NULL;
$this->inventoryProviders = [];
$this->logStorage = NULL;
// $this->tempReferences = NULL;.
parent::__sleep();
}
}
