Commit 24c3ee1a authored by bojanz's avatar bojanz

Issue #2985782: The product category conditions should store UUIDs instead of...

Issue #2985782: The product category conditions should store UUIDs instead of IDs to allow exporting
parent 6fc1ccb7
......@@ -46,6 +46,10 @@ services:
class: Drupal\commerce\Config\ConfigUpdater
arguments: ['@entity_type.manager', '@config.storage', '@config.factory']
commerce.entity_uuid_mapper:
class: Drupal\commerce\EntityUuidMapper
arguments: ['@database', '@entity_type.manager']
commerce.twig_extension:
class: Drupal\commerce\TwigExtension\CommerceTwigExtension
tags:
......
......@@ -2,6 +2,7 @@
namespace Drupal\commerce_product\Plugin\Commerce\Condition;
use Drupal\commerce\EntityUuidMapperInterface;
use Drupal\commerce\Plugin\Commerce\Condition\ConditionBase;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
......@@ -38,12 +39,15 @@ class OrderItemProductCategory extends ConditionBase implements ContainerFactory
* The entity field manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\commerce\EntityUuidMapperInterface $entity_uuid_mapper
* The entity UUID mapper.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, EntityUuidMapperInterface $entity_uuid_mapper) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityFieldManager = $entity_field_manager;
$this->entityTypeManager = $entity_type_manager;
$this->entityUuidMapper = $entity_uuid_mapper;
}
/**
......@@ -55,7 +59,8 @@ class OrderItemProductCategory extends ConditionBase implements ContainerFactory
$plugin_id,
$plugin_definition,
$container->get('entity_field.manager'),
$container->get('entity_type.manager')
$container->get('entity_type.manager'),
$container->get('commerce.entity_uuid_mapper')
);
}
......@@ -71,9 +76,10 @@ class OrderItemProductCategory extends ConditionBase implements ContainerFactory
if (!$purchased_entity || $purchased_entity->getEntityTypeId() != 'commerce_product_variation') {
return FALSE;
}
$term_ids = $this->getTermIds();
$referenced_ids = $this->getReferencedIds($purchased_entity->getProduct());
return (bool) array_intersect($this->configuration['terms'], $referenced_ids);
return (bool) array_intersect($term_ids, $referenced_ids);
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\commerce_product\Plugin\Commerce\Condition;
use Drupal\commerce\EntityUuidMapperInterface;
use Drupal\commerce\Plugin\Commerce\Condition\ConditionBase;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
......@@ -38,12 +39,15 @@ class OrderProductCategory extends ConditionBase implements ContainerFactoryPlug
* The entity field manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\commerce\EntityUuidMapperInterface $entity_uuid_mapper
* The entity UUID mapper.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, EntityUuidMapperInterface $entity_uuid_mapper) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityFieldManager = $entity_field_manager;
$this->entityTypeManager = $entity_type_manager;
$this->entityUuidMapper = $entity_uuid_mapper;
}
/**
......@@ -55,7 +59,8 @@ class OrderProductCategory extends ConditionBase implements ContainerFactoryPlug
$plugin_id,
$plugin_definition,
$container->get('entity_field.manager'),
$container->get('entity_type.manager')
$container->get('entity_type.manager'),
$container->get('commerce.entity_uuid_mapper')
);
}
......@@ -66,6 +71,7 @@ class OrderProductCategory extends ConditionBase implements ContainerFactoryPlug
$this->assertEntity($entity);
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $entity;
$term_ids = $this->getTermIds();
foreach ($order->getItems() as $order_item) {
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $purchased_entity */
$purchased_entity = $order_item->getPurchasedEntity();
......@@ -73,7 +79,7 @@ class OrderProductCategory extends ConditionBase implements ContainerFactoryPlug
continue;
}
$referenced_ids = $this->getReferencedIds($purchased_entity->getProduct());
if (array_intersect($this->configuration['terms'], $referenced_ids)) {
if (array_intersect($term_ids, $referenced_ids)) {
return TRUE;
}
}
......
......@@ -24,6 +24,13 @@ trait ProductCategoryTrait {
*/
protected $entityTypeManager;
/**
* The entity UUID mapper.
*
* @var \Drupal\commerce\EntityUuidMapperInterface
*/
protected $entityUuidMapper;
/**
* {@inheritdoc}
*/
......@@ -40,7 +47,7 @@ trait ProductCategoryTrait {
$form = parent::buildConfigurationForm($form, $form_state);
$terms = NULL;
$ids = $this->configuration['terms'];
$ids = $this->getTermIds();
if (!empty($ids)) {
$term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
$terms = $term_storage->loadMultiple($ids);
......@@ -67,11 +74,21 @@ trait ProductCategoryTrait {
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
// Convert selected IDs into UUIDs, and store them.
$values = $form_state->getValue($form['#parents']);
$this->configuration['terms'] = [];
foreach ($values['terms'] as $value) {
$this->configuration['terms'][] = $value['target_id'];
}
$term_ids = array_column($values['terms'], 'target_id');
$this->configuration['terms'] = $this->entityUuidMapper->mapFromIds('taxonomy_term', $term_ids);
$this->configuration['terms'] = array_values($this->configuration['terms']);
}
/**
* Gets the configured term IDs.
*
* @return array
* The term IDs.
*/
protected function getTermIds() {
return $this->entityUuidMapper->mapToIds('taxonomy_term', $this->configuration['terms']);
}
/**
......
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\commerce_product\Unit\Plugin\Commerce\Condition;
use Drupal\commerce\EntityUuidMapperInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_product\Entity\ProductInterface;
use Drupal\commerce_product\Entity\ProductVariationInterface;
......@@ -33,9 +34,20 @@ class OrderItemProductCategoryTest extends UnitTestCase {
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager = $entity_type_manager->reveal();
$uuid_map = [
'2' => '62e428e1-88a6-478c-a8c6-a554ca2332ae',
'3' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'4' => 'a019d89b-c4d9-4ed4-b859-894e4e2e93cf',
];
$entity_uuid_mapper = $this->prophesize(EntityUuidMapperInterface::class);
$entity_uuid_mapper->mapToIds('taxonomy_term', [$uuid_map['3']])->willReturn(['3']);
$entity_uuid_mapper->mapToIds('taxonomy_term', [$uuid_map['4']])->willReturn(['4']);
$entity_uuid_mapper = $entity_uuid_mapper->reveal();
$configuration = [];
$configuration['terms'] = [3];
$condition = new OrderItemProductCategory($configuration, 'order_item_product_category', ['entity_type' => 'commerce_order_item'], $entity_field_manager, $entity_type_manager);
$configuration['terms'] = ['30df59bd-7b03-4cf7-bb35-d42fc49f0651'];
$condition = new OrderItemProductCategory($configuration, 'order_item_product_category', ['entity_type' => 'commerce_order_item'], $entity_field_manager, $entity_type_manager, $entity_uuid_mapper);
// Order item with no purchasable entity.
$order_item = $this->prophesize(OrderItemInterface::class);
......@@ -70,7 +82,7 @@ class OrderItemProductCategoryTest extends UnitTestCase {
$this->assertFalse($condition->evaluate($order_item));
// Matching product category.
$configuration['terms'] = ['4'];
$configuration['terms'] = ['a019d89b-c4d9-4ed4-b859-894e4e2e93cf'];
$condition->setConfiguration($configuration);
$this->assertTrue($condition->evaluate($order_item));
}
......
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\commerce_product\Unit\Plugin\Commerce\Condition;
use Drupal\commerce\EntityUuidMapperInterface;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_product\Entity\ProductInterface;
......@@ -34,9 +35,20 @@ class OrderProductCategoryTest extends UnitTestCase {
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager = $entity_type_manager->reveal();
$uuid_map = [
'2' => '62e428e1-88a6-478c-a8c6-a554ca2332ae',
'3' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'4' => 'a019d89b-c4d9-4ed4-b859-894e4e2e93cf',
];
$entity_uuid_mapper = $this->prophesize(EntityUuidMapperInterface::class);
$entity_uuid_mapper->mapToIds('taxonomy_term', [$uuid_map['3']])->willReturn(['3']);
$entity_uuid_mapper->mapToIds('taxonomy_term', [$uuid_map['4']])->willReturn(['4']);
$entity_uuid_mapper = $entity_uuid_mapper->reveal();
$configuration = [];
$configuration['terms'] = [3];
$condition = new OrderProductCategory($configuration, 'order_product_category', ['entity_type' => 'commerce_order'], $entity_field_manager, $entity_type_manager);
$configuration['terms'] = ['30df59bd-7b03-4cf7-bb35-d42fc49f0651'];
$condition = new OrderProductCategory($configuration, 'order_product_category', ['entity_type' => 'commerce_order'], $entity_field_manager, $entity_type_manager, $entity_uuid_mapper);
// Order item with no purchasable entity.
$first_order_item = $this->prophesize(OrderItemInterface::class);
......@@ -77,7 +89,7 @@ class OrderProductCategoryTest extends UnitTestCase {
$order = $this->buildOrder([$first_order_item, $second_order_item]);
$this->assertFalse($condition->evaluate($order));
$configuration['terms'] = [4];
$configuration['terms'] = ['a019d89b-c4d9-4ed4-b859-894e4e2e93cf'];
$condition->setConfiguration($configuration);
$order = $this->buildOrder([$first_order_item, $second_order_item]);
......
<?php
namespace Drupal\commerce;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class EntityUuidMapper implements EntityUuidMapperInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* UUID to ID map, grouped by entity type ID.
*
* @var array
*/
protected $map = [];
/**
* Constructs a new EntityUuidMapper object.
*
* @param \Drupal\Core\Database\Connection $database
* The database connection.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(Connection $database, EntityTypeManagerInterface $entity_type_manager) {
$this->database = $database;
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public function mapToIds($entity_type_id, array $uuids) {
// Fetch known UUIDs from the static cache.
$ids = [];
foreach ($uuids as $index => $uuid) {
if (isset($this->map[$entity_type_id][$uuid])) {
$ids[$uuid] = $this->map[$entity_type_id][$uuid];
unset($uuids[$index]);
}
}
// Map the remaining UUIDs from the database.
if (!empty($uuids)) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$id_key = $entity_type->getKey('id');
$uuid_key = $entity_type->getKey('uuid');
// Query the storage directly to avoid the performance impact of loading
// the full entities.
$loaded_uuids = $this->database->select($entity_type->getBaseTable(), 't')
->fields('t', [$uuid_key, $id_key])
->condition($uuid_key, $uuids, 'IN')
->execute()
->fetchAllKeyed(1, 0);
foreach ($loaded_uuids as $id => $uuid) {
$ids[$uuid] = $id;
$this->map[$entity_type_id][$uuid] = $id;
}
}
return $ids;
}
/**
* {@inheritdoc}
*/
public function mapFromIds($entity_type_id, array $ids) {
// Fetch known IDs from the static cache.
$uuids = [];
foreach ($ids as $index => $id) {
if (isset($this->map[$entity_type_id])) {
$uuid = array_search($id, $this->map[$entity_type_id]);
if ($uuid) {
$uuids[$id] = $uuid;
unset($ids[$index]);
}
}
}
// Map the remaining IDs from the database.
if (!empty($ids)) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$id_key = $entity_type->getKey('id');
$uuid_key = $entity_type->getKey('uuid');
// Query the storage directly to avoid the performance impact of loading
// the full entities.
$loaded_ids = $this->database->select($entity_type->getBaseTable(), 't')
->fields('t', [$uuid_key, $id_key])
->condition($id_key, $ids, 'IN')
->execute()
->fetchAllKeyed(0, 1);
foreach ($loaded_ids as $uuid => $id) {
$uuids[$id] = $uuid;
$this->map[$entity_type_id][$uuid] = $id;
}
}
return $uuids;
}
}
<?php
namespace Drupal\commerce;
/**
* Maps entity UUIDs to entity IDs, and vice-versa.
*/
interface EntityUuidMapperInterface {
/**
* Maps the given entity UUIDs to entity IDs.
*
* @param string $entity_type_id
* The entity type ID.
* @param array $uuids
* THe entity UUIDs.
*
* @return array
* The entity IDs.
*/
public function mapToIds($entity_type_id, array $uuids);
/**
* Maps the given entity IDs to entity UUIDs.
*
* @param string $entity_type_id
* The entity type ID.
* @param array $ids
* THe entity IDs.
*
* @return array
* The entity UUIDs.
*/
public function mapFromIds($entity_type_id, array $ids);
}
<?php
namespace Drupal\Tests\commerce\Kernel;
/**
* Tests the EntityUuidMapper class.
*
* @coversDefaultClass \Drupal\commerce\EntityUuidMapper
* @group commerce
*/
class EntityUuidMapperTest extends CommerceKernelTestBase {
/**
* The entity UUID mapper.
*
* @var \Drupal\commerce\EntityUuidMapperInterface
*/
protected $entityUuidMapper;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->entityUuidMapper = \Drupal::service('commerce.entity_uuid_mapper');
}
/**
* Tests the mapper.
*
* @covers ::mapToIds
* @covers ::mapFromIds
*/
public function testMapper() {
$another_store = $this->createStore('Second store', 'second@example.com');
$map = [
$this->store->id() => $this->store->uuid(),
$another_store->id() => $another_store->uuid(),
];
$uuid_map = $this->entityUuidMapper->mapToIds('commerce_store', array_values($map));
$this->assertEquals($uuid_map, array_flip($map));
$id_map = $this->entityUuidMapper->mapFromIds('commerce_store', array_keys($map));
$this->assertEquals($id_map, $map);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment