Problem/Motivation
In Drupal Commerce, we wanted to retroactively apply indexes to our entity schema for commonly queried fields. These are string fields and not entity reference fields, which automatically have generated indices due to their field type definition.
The issue is tracked here:
#2907367: Add indexes to important fields β
.
We added a custom schema handler and added the following override to getSharedTableFieldSchema
to add indexes for specific fields.
+ /**
+ * {@inheritdoc}
+ */
+ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
+ $schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
+
+ $entity_type = $this->entityManager->getDefinition($storage_definition->getTargetEntityTypeId());
+ $field_indexes = $entity_type->get('field_indexes');
+ foreach ($field_indexes as $field_name) {
+ if ($field_name == $storage_definition->getName()) {
+ $this->addSharedTableFieldIndex($storage_definition, $schema);
+ }
+ }
+
+ return $schema;
+ }
This is a more generic form of what FeedStorageSchema, FileStorageSchema, etc have done. The problem, however, is that the indexes do not seem to be added on the update hook.
This was a sample code that berdir pointed me to via the Paragraphs module
+
+/**
+ * Ensure new field indexes on the product variation entity.
+ */
+function commerce_product_update_8205() {
+ $entity_type_manager = \Drupal::entityTypeManager();
+ $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+ // Get the current product variation entity type definition, ensure the
+ // storage schema class is set.
+ $entity_type = $entity_type_manager->getDefinition('commerce_product_variation')
+ ->setHandlerClass('storage_schema', CommerceContentEntityStorageSchema::class);
+
+ // Regenerate entity type indexes.
+ $definition_update_manager->updateEntityType($entity_type);
+}
However, after many test runs -- the indices were never created.
To resolve this issue, I had to manually fetch the indexes and install them if they did not exist, in an overridden onEntityTypeUpdate method.
+ /**
+ * {@inheritdoc}
+ */
+ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
+ parent::onEntityTypeUpdate($entity_type, $original);
+
+ // @todo open issue against Drupal core
+ // ::onEntityTypeUpdate will only populate new indexes if the entity has
+ // a change on being translatable, revisionable, or a field change.
+ $entity_schema = $this->getEntitySchema($entity_type, TRUE);
+ $schema_table = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
+ $schema_indexes = $entity_schema[$schema_table]['indexes'];
+ foreach ($schema_indexes as $index_name => $index_fields) {
+ if (!$this->database->schema()->indexExists($schema_table, $index_name)) {
+ $this->database->schema()->addIndex($schema_table, $index_name, $index_fields, $entity_schema[$schema_table]);
+ }
+ }
+ }
Proposed resolution
addSharedTableFieldIndex
adds an index to the table schema. Respect this as a change in requiresEntityStorageSchemaChanges
Remaining tasks
Find out why this logic ignores new indices on an entity's table schema
/**
* {@inheritdoc}
*/
public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
return
$this->hasSharedTableStructureChange($entity_type, $original) ||
// Detect changes in key or index definitions.
$this->getEntitySchemaData($entity_type, $this->getEntitySchema($entity_type, TRUE)) != $this->loadEntitySchemaData($original);
}
User interface changes
N/A
API changes
N/A
Data model changes
Adds database indices as expected.