View source
<?php
declare (strict_types=1);
namespace Drupal\Tests\field\Kernel;
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
class BulkDeleteTest extends FieldKernelTestBase {
protected $fieldStorages;
protected $entities;
protected $entitiesByBundles;
protected $bundles;
protected $entityTypeId = 'entity_test';
public function checkHooksInvocations($expected_hooks, $actual_hooks) {
foreach ($expected_hooks as $hook => $invocations) {
$actual_invocations = $actual_hooks[$hook];
$this
->assertSameSize($invocations, $actual_invocations, "{$hook}() was called the expected number of times.");
foreach ($invocations as $argument) {
$found = FALSE;
foreach ($actual_invocations as $actual_arguments) {
if ($argument instanceof EntityInterface && $actual_arguments[0]
->id() == $argument
->id()) {
$found = TRUE;
break;
}
elseif (is_array($argument) && count($actual_arguments[1]) == count($argument) && count(array_diff_key($actual_arguments[1], $argument)) == 0) {
$found = TRUE;
break;
}
}
$this
->assertTrue($found, "{$hook}() was called on expected argument");
}
}
}
protected function setUp() : void {
parent::setUp();
$this->fieldStorages = [];
$this->entities = [];
$this->entitiesByBundles = [];
$this->bundles = [
'bb_1' => 'bb_1',
'bb_2' => 'bb_2',
];
foreach ($this->bundles as $name => $desc) {
entity_test_create_bundle($name, $desc);
}
$field_storage = FieldStorageConfig::create([
'field_name' => 'bf_1',
'entity_type' => $this->entityTypeId,
'type' => 'test_field',
'cardinality' => 1,
]);
$field_storage
->save();
$this->fieldStorages[] = $field_storage;
$field_storage = FieldStorageConfig::create([
'field_name' => 'bf_2',
'entity_type' => $this->entityTypeId,
'type' => 'test_field',
'cardinality' => 4,
]);
$field_storage
->save();
$this->fieldStorages[] = $field_storage;
foreach ($this->bundles as $bundle) {
foreach ($this->fieldStorages as $field_storage) {
FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $bundle,
])
->save();
}
for ($i = 0; $i < 10; $i++) {
$entity = $this->container
->get('entity_type.manager')
->getStorage($this->entityTypeId)
->create([
'type' => $bundle,
]);
foreach ($this->fieldStorages as $field_storage) {
$entity->{$field_storage
->getName()}
->setValue($this
->_generateTestFieldValues($field_storage
->getCardinality()));
}
$entity
->save();
}
}
$this->entities = $this->container
->get('entity_type.manager')
->getStorage($this->entityTypeId)
->loadMultiple();
foreach ($this->entities as $entity) {
$entity->bf_1->value;
$this->entitiesByBundles[$entity
->bundle()][$entity
->id()] = $entity;
}
}
public function testDeleteField() {
$bundle = reset($this->bundles);
$field_storage = reset($this->fieldStorages);
$field_name = $field_storage
->getName();
$storage = \Drupal::entityTypeManager()
->getStorage('entity_test');
$found = $storage
->getQuery()
->accessCheck(FALSE)
->condition('type', $bundle)
->execute();
$this
->assertCount(10, $found, 'Correct number of entities found before deleting');
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
$field
->delete();
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'field_storage_uuid' => $field_storage
->uuid(),
'deleted' => TRUE,
'include_deleted' => TRUE,
]);
$this
->assertCount(1, $fields, 'There is one deleted field');
$field = $fields[$field
->uuid()];
$this
->assertEquals($bundle, $field
->getTargetBundle(), 'The deleted field is for the correct bundle');
$table_mapping = $storage
->getTableMapping();
$table = $table_mapping
->getDedicatedDataTableName($field_storage);
$column = $table_mapping
->getFieldColumnName($field_storage, 'value');
$result = Database::getConnection()
->select($table, 't')
->fields('t')
->execute();
foreach ($result as $row) {
$this
->assertEquals($row->{$column}, $this->entities[$row->entity_id]->{$field_name}->value);
}
$found = $storage
->getQuery()
->accessCheck(FALSE)
->condition('type', $bundle)
->condition("{$field_name}.deleted", 0)
->execute();
$this
->assertEmpty($found, 'No entities found after deleting');
$found = $storage
->getQuery()
->accessCheck(FALSE)
->condition('type', $bundle)
->condition("{$field_name}.deleted", 1)
->sort('id')
->execute();
$this
->assertCount(10, $found, 'Correct number of entities found after deleting');
$this
->assertEmpty(array_diff($found, array_keys($this->entities)));
}
public function testPurgeWithDeletedAndActiveField() {
$bundle = reset($this->bundles);
$field_name = 'bf_3';
$deleted_field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => $this->entityTypeId,
'type' => 'test_field',
'cardinality' => 1,
]);
$deleted_field_storage
->save();
FieldConfig::create([
'field_storage' => $deleted_field_storage,
'bundle' => $bundle,
])
->save();
for ($i = 0; $i < 20; $i++) {
$entity = $this->container
->get('entity_type.manager')
->getStorage($this->entityTypeId)
->create([
'type' => $bundle,
]);
$entity->{$field_name}
->setValue($this
->_generateTestFieldValues(1));
$entity
->save();
}
$deleted_field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
$deleted_field
->delete();
$deleted_field_uuid = $deleted_field
->uuid();
$field_storages = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->loadByProperties([
'uuid' => $deleted_field_storage
->uuid(),
'include_deleted' => TRUE,
]);
$deleted_field_storage = reset($field_storages);
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => $this->entityTypeId,
'type' => 'test_field',
'cardinality' => 1,
]);
$field_storage
->save();
FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $bundle,
])
->save();
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'uuid' => $deleted_field_uuid,
'include_deleted' => TRUE,
]);
$this
->assertArrayHasKey($deleted_field_uuid, $fields);
$this
->assertTrue($fields[$deleted_field_uuid]
->isDeleted());
$this
->assertSame($field_name, $fields[$deleted_field_uuid]
->getName());
for ($i = 0; $i < 10; $i++) {
$entity = $this->container
->get('entity_type.manager')
->getStorage($this->entityTypeId)
->create([
'type' => $bundle,
]);
$entity->{$field_name}
->setValue($this
->_generateTestFieldValues(1));
$entity
->save();
}
$storage = \Drupal::entityTypeManager()
->getStorage($this->entityTypeId);
$table_mapping = $storage
->getTableMapping();
$deleted_table_name = $table_mapping
->getDedicatedDataTableName($deleted_field_storage, TRUE);
$active_table_name = $table_mapping
->getDedicatedDataTableName($field_storage);
field_purge_batch(50);
$this
->assertTrue(\Drupal::database()
->schema()
->tableExists($active_table_name));
$this
->assertFalse(\Drupal::database()
->schema()
->tableExists($deleted_table_name));
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'field_storage_uuid' => $deleted_field_storage
->uuid(),
'deleted' => TRUE,
'include_deleted' => TRUE,
]);
$this
->assertCount(0, $fields, 'The field is gone');
$count = \Drupal::database()
->select('entity_test__' . $field_name, 'f')
->fields('f', [
'entity_id',
])
->condition('bundle', $bundle)
->countQuery()
->execute()
->fetchField();
$this
->assertEquals(10, $count);
}
public function testPurgeField() {
field_test_memorize();
$bundle = reset($this->bundles);
$field_storage = reset($this->fieldStorages);
$field_name = $field_storage
->getName();
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
$field
->delete();
$mem = field_test_memorize();
$this
->assertCount(0, $mem, 'No field hooks were called');
$batch_size = 2;
for ($count = 8; $count >= 0; $count -= $batch_size) {
field_purge_batch($batch_size);
$found = \Drupal::entityQuery('entity_test')
->accessCheck(FALSE)
->condition('type', $bundle)
->condition($field_name . '.deleted', 1)
->execute();
$this
->assertCount($count, $found, 'Correct number of entities found after purging 2');
}
$actual_hooks = field_test_memorize();
$hooks = [];
$hooks['field_test_field_delete'] = $this->entitiesByBundles[$bundle];
$this
->checkHooksInvocations($hooks, $actual_hooks);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'field_storage_uuid' => $field_storage
->uuid(),
'deleted' => TRUE,
'include_deleted' => TRUE,
]);
$this
->assertCount(1, $fields, 'There is one deleted field');
field_purge_batch($batch_size);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'field_storage_uuid' => $field_storage
->uuid(),
'deleted' => TRUE,
'include_deleted' => TRUE,
]);
$this
->assertCount(0, $fields, 'The field is gone');
$storages = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->loadByProperties([
'uuid' => $field_storage
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertTrue(isset($storages[$field_storage
->uuid()]), 'The field storage exists and is not deleted');
}
public function testPurgeFieldStorage() {
field_test_memorize();
$field_storage = reset($this->fieldStorages);
$field_name = $field_storage
->getName();
$bundle = reset($this->bundles);
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
$field
->delete();
$mem = field_test_memorize();
$this
->assertCount(0, $mem, 'No field hooks were called.');
field_purge_batch(10);
$actual_hooks = field_test_memorize();
$hooks = [];
$hooks['field_test_field_delete'] = $this->entitiesByBundles[$bundle];
$this
->checkHooksInvocations($hooks, $actual_hooks);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'uuid' => $field
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertArrayHasKey($field
->uuid(), $fields);
$this
->assertTrue($fields[$field
->uuid()]
->isDeleted());
field_purge_batch(0);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'uuid' => $field
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertCount(0, $fields, 'The field is purged.');
$storages = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->loadByProperties([
'uuid' => $field_storage
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertArrayHasKey($field_storage
->uuid(), $storages);
$this
->assertFalse($storages[$field_storage
->uuid()]
->isDeleted());
$bundle = next($this->bundles);
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
$field
->delete();
$mem = field_test_memorize();
$this
->assertCount(0, $mem, 'No field hooks were called.');
field_purge_batch(10);
$actual_hooks = field_test_memorize();
$hooks = [];
$hooks['field_test_field_delete'] = $this->entitiesByBundles[$bundle];
$this
->checkHooksInvocations($hooks, $actual_hooks);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'uuid' => $field
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertArrayHasKey($field
->uuid(), $fields);
$this
->assertTrue($fields[$field
->uuid()]
->isDeleted());
$storages = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->loadByProperties([
'uuid' => $field_storage
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertArrayHasKey($field_storage
->uuid(), $storages);
$this
->assertTrue($storages[$field_storage
->uuid()]
->isDeleted());
field_purge_batch(0);
$fields = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'uuid' => $field
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertCount(0, $fields, 'The field is purged.');
$storages = \Drupal::entityTypeManager()
->getStorage('field_storage_config')
->loadByProperties([
'uuid' => $field_storage
->uuid(),
'include_deleted' => TRUE,
]);
$this
->assertCount(0, $storages, 'The field storage is purged.');
}
}