3 namespace Drupal\Tests\field\Kernel;
5 use Drupal\Core\Entity\EntityStorageException;
6 use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
7 use Drupal\Core\Field\FieldException;
8 use Drupal\entity_test\Entity\EntityTest;
9 use Drupal\field\Entity\FieldConfig;
10 use Drupal\field\Entity\FieldStorageConfig;
13 * Tests field storage create, read, update, and delete.
17 class FieldStorageCrudTest extends FieldKernelTestBase {
24 public static $modules = [];
26 // TODO : test creation with
27 // - a full fledged $field structure, check that all the values are there
28 // - a minimal $field structure, check all default values are set
29 // defer actual $field comparison to a helper function, used for the two cases above
32 * Test the creation of a field storage.
34 public function testCreate() {
35 $field_storage_definition = [
36 'field_name' => 'field_2',
37 'entity_type' => 'entity_test',
38 'type' => 'test_field',
40 field_test_memorize();
41 $field_storage = FieldStorageConfig::create($field_storage_definition);
42 $field_storage->save();
44 $field_storage = FieldStorageConfig::load($field_storage->id());
45 $this->assertTrue($field_storage->getSetting('storage_setting_from_config_data'));
46 $this->assertNull($field_storage->getSetting('config_data_from_storage_setting'));
48 $mem = field_test_memorize();
49 $this->assertIdentical($mem['field_test_field_storage_config_create'][0][0]->getName(), $field_storage_definition['field_name'], 'hook_entity_create() called with correct arguments.');
50 $this->assertIdentical($mem['field_test_field_storage_config_create'][0][0]->getType(), $field_storage_definition['type'], 'hook_entity_create() called with correct arguments.');
52 // Read the configuration. Check against raw configuration data rather than
53 // the loaded ConfigEntity, to be sure we check that the defaults are
55 $field_storage_config = $this->config('field.storage.' . $field_storage->id())->get();
57 $this->assertTrue($field_storage_config['settings']['config_data_from_storage_setting']);
58 $this->assertTrue(!isset($field_storage_config['settings']['storage_setting_from_config_data']));
60 // Since we are working with raw configuration, this needs to be unset
62 // @see Drupal\field_test\Plugin\Field\FieldType\TestItem::storageSettingsFromConfigData()
63 unset($field_storage_config['settings']['config_data_from_storage_setting']);
65 // Ensure that basic properties are preserved.
66 $this->assertEqual($field_storage_config['field_name'], $field_storage_definition['field_name'], 'The field name is properly saved.');
67 $this->assertEqual($field_storage_config['entity_type'], $field_storage_definition['entity_type'], 'The field entity type is properly saved.');
68 $this->assertEqual($field_storage_config['id'], $field_storage_definition['entity_type'] . '.' . $field_storage_definition['field_name'], 'The field id is properly saved.');
69 $this->assertEqual($field_storage_config['type'], $field_storage_definition['type'], 'The field type is properly saved.');
71 // Ensure that cardinality defaults to 1.
72 $this->assertEqual($field_storage_config['cardinality'], 1, 'Cardinality defaults to 1.');
74 // Ensure that default settings are present.
75 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
76 $this->assertEqual($field_storage_config['settings'], $field_type_manager->getDefaultStorageSettings($field_storage_definition['type']), 'Default storage settings have been written.');
78 // Guarantee that the name is unique.
80 FieldStorageConfig::create($field_storage_definition)->save();
81 $this->fail(t('Cannot create two fields with the same name.'));
83 catch (EntityStorageException $e) {
84 $this->pass(t('Cannot create two fields with the same name.'));
87 // Check that field type is required.
89 $field_storage_definition = [
90 'field_name' => 'field_1',
91 'entity_type' => 'entity_type',
93 FieldStorageConfig::create($field_storage_definition)->save();
94 $this->fail(t('Cannot create a field with no type.'));
96 catch (FieldException $e) {
97 $this->pass(t('Cannot create a field with no type.'));
100 // Check that field name is required.
102 $field_storage_definition = [
103 'type' => 'test_field',
104 'entity_type' => 'entity_test',
106 FieldStorageConfig::create($field_storage_definition)->save();
107 $this->fail(t('Cannot create an unnamed field.'));
109 catch (FieldException $e) {
110 $this->pass(t('Cannot create an unnamed field.'));
112 // Check that entity type is required.
114 $field_storage_definition = [
115 'field_name' => 'test_field',
116 'type' => 'test_field',
118 FieldStorageConfig::create($field_storage_definition)->save();
119 $this->fail('Cannot create a field without an entity type.');
121 catch (FieldException $e) {
122 $this->pass('Cannot create a field without an entity type.');
125 // Check that field name must start with a letter or _.
127 $field_storage_definition = [
128 'field_name' => '2field_2',
129 'entity_type' => 'entity_test',
130 'type' => 'test_field',
132 FieldStorageConfig::create($field_storage_definition)->save();
133 $this->fail(t('Cannot create a field with a name starting with a digit.'));
135 catch (FieldException $e) {
136 $this->pass(t('Cannot create a field with a name starting with a digit.'));
139 // Check that field name must only contain lowercase alphanumeric or _.
141 $field_storage_definition = [
142 'field_name' => 'field#_3',
143 'entity_type' => 'entity_test',
144 'type' => 'test_field',
146 FieldStorageConfig::create($field_storage_definition)->save();
147 $this->fail(t('Cannot create a field with a name containing an illegal character.'));
149 catch (FieldException $e) {
150 $this->pass(t('Cannot create a field with a name containing an illegal character.'));
153 // Check that field name cannot be longer than 32 characters long.
155 $field_storage_definition = [
156 'field_name' => '_12345678901234567890123456789012',
157 'entity_type' => 'entity_test',
158 'type' => 'test_field',
160 FieldStorageConfig::create($field_storage_definition)->save();
161 $this->fail(t('Cannot create a field with a name longer than 32 characters.'));
163 catch (FieldException $e) {
164 $this->pass(t('Cannot create a field with a name longer than 32 characters.'));
167 // Check that field name can not be an entity key.
168 // "id" is known as an entity key from the "entity_test" type.
170 $field_storage_definition = [
171 'type' => 'test_field',
172 'field_name' => 'id',
173 'entity_type' => 'entity_test',
175 FieldStorageConfig::create($field_storage_definition)->save();
176 $this->fail(t('Cannot create a field bearing the name of an entity key.'));
178 catch (FieldException $e) {
179 $this->pass(t('Cannot create a field bearing the name of an entity key.'));
184 * Tests that an explicit schema can be provided on creation.
186 * This behavior is needed to allow field storage creation within updates,
187 * since plugin classes (and thus the field type schema) cannot be accessed.
189 public function testCreateWithExplicitSchema() {
193 $field_storage = FieldStorageConfig::create([
194 'field_name' => 'field_2',
195 'entity_type' => 'entity_test',
196 'type' => 'test_field',
199 $this->assertEqual($field_storage->getSchema(), $schema);
203 * Tests reading field storage definitions.
205 public function testRead() {
206 $field_storage_definition = [
207 'field_name' => 'field_1',
208 'entity_type' => 'entity_test',
209 'type' => 'test_field',
211 $field_storage = FieldStorageConfig::create($field_storage_definition);
212 $field_storage->save();
213 $id = $field_storage->id();
215 // Check that 'single column' criteria works.
216 $fields = entity_load_multiple_by_properties('field_storage_config', ['field_name' => $field_storage_definition['field_name']]);
217 $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
219 // Check that 'multi column' criteria works.
220 $fields = entity_load_multiple_by_properties('field_storage_config', ['field_name' => $field_storage_definition['field_name'], 'type' => $field_storage_definition['type']]);
221 $this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
222 $fields = entity_load_multiple_by_properties('field_storage_config', ['field_name' => $field_storage_definition['field_name'], 'type' => 'foo']);
223 $this->assertTrue(empty($fields), 'No field was found.');
225 // Create a field from the field storage.
226 $field_definition = [
227 'field_name' => $field_storage_definition['field_name'],
228 'entity_type' => 'entity_test',
229 'bundle' => 'entity_test',
231 FieldConfig::create($field_definition)->save();
235 * Test creation of indexes on data column.
237 public function testIndexes() {
238 // Check that indexes specified by the field type are used by default.
239 $field_storage = FieldStorageConfig::create([
240 'field_name' => 'field_1',
241 'entity_type' => 'entity_test',
242 'type' => 'test_field',
244 $field_storage->save();
245 $field_storage = FieldStorageConfig::load($field_storage->id());
246 $schema = $field_storage->getSchema();
247 $expected_indexes = ['value' => ['value']];
248 $this->assertEqual($schema['indexes'], $expected_indexes, 'Field type indexes saved by default');
250 // Check that indexes specified by the field definition override the field
252 $field_storage = FieldStorageConfig::create([
253 'field_name' => 'field_2',
254 'entity_type' => 'entity_test',
255 'type' => 'test_field',
260 $field_storage->save();
261 $field_storage = FieldStorageConfig::load($field_storage->id());
262 $schema = $field_storage->getSchema();
263 $expected_indexes = ['value' => []];
264 $this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes override field type indexes');
266 // Check that indexes specified by the field definition add to the field
268 $field_storage = FieldStorageConfig::create([
269 'field_name' => 'field_3',
270 'entity_type' => 'entity_test',
271 'type' => 'test_field',
273 'value_2' => ['value'],
276 $field_storage->save();
277 $id = $field_storage->id();
278 $field_storage = FieldStorageConfig::load($id);
279 $schema = $field_storage->getSchema();
280 $expected_indexes = ['value' => ['value'], 'value_2' => ['value']];
281 $this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes are merged with field type indexes');
285 * Test the deletion of a field storage.
287 public function testDeleteNoData() {
288 // Deleting and purging field storages with data is tested in
289 // \Drupal\Tests\field\Kernel\BulkDeleteTest.
291 // Create two fields (so we can test that only one is deleted).
292 $field_storage_definition = [
293 'field_name' => 'field_1',
294 'type' => 'test_field',
295 'entity_type' => 'entity_test',
297 FieldStorageConfig::create($field_storage_definition)->save();
298 $another_field_storage_definition = [
299 'field_name' => 'field_2',
300 'type' => 'test_field',
301 'entity_type' => 'entity_test',
303 FieldStorageConfig::create($another_field_storage_definition)->save();
305 // Create fields for each.
306 $field_definition = [
307 'field_name' => $field_storage_definition['field_name'],
308 'entity_type' => 'entity_test',
309 'bundle' => 'entity_test',
311 FieldConfig::create($field_definition)->save();
312 $another_field_definition = $field_definition;
313 $another_field_definition['field_name'] = $another_field_storage_definition['field_name'];
314 FieldConfig::create($another_field_definition)->save();
316 // Test that the first field is not deleted, and then delete it.
317 $field_storage = current(entity_load_multiple_by_properties('field_storage_config', ['field_name' => $field_storage_definition['field_name'], 'include_deleted' => TRUE]));
318 $this->assertTrue(!empty($field_storage) && !$field_storage->isDeleted(), 'A new storage is not marked for deletion.');
319 FieldStorageConfig::loadByName('entity_test', $field_storage_definition['field_name'])->delete();
321 // Make sure that the field storage is deleted as it had no data.
322 $field_storages = entity_load_multiple_by_properties('field_storage_config', ['field_name' => $field_storage_definition['field_name'], 'include_deleted' => TRUE]);
323 $this->assertEquals(0, count($field_storages), 'Field storage was deleted');
325 // Make sure that this field is marked as deleted when it is
326 // specifically loaded.
327 $fields = entity_load_multiple_by_properties('field_config', ['entity_type' => 'entity_test', 'field_name' => $field_definition['field_name'], 'bundle' => $field_definition['bundle'], 'include_deleted' => TRUE]);
328 $this->assertEquals(0, count($fields), 'Field storage was deleted');
330 // Try to load the storage normally and make sure it does not show up.
331 $field_storage = FieldStorageConfig::load('entity_test.' . $field_storage_definition['field_name']);
332 $this->assertTrue(empty($field_storage), 'Field storage was deleted');
334 // Try to load the field normally and make sure it does not show up.
335 $field = FieldConfig::load('entity_test.' . '.' . $field_definition['bundle'] . '.' . $field_definition['field_name']);
336 $this->assertTrue(empty($field), 'Field was deleted');
338 // Make sure the other field and its storage are not deleted.
339 $another_field_storage = FieldStorageConfig::load('entity_test.' . $another_field_storage_definition['field_name']);
340 $this->assertTrue(!empty($another_field_storage) && !$another_field_storage->isDeleted(), 'A non-deleted storage is not marked for deletion.');
341 $another_field = FieldConfig::load('entity_test.' . $another_field_definition['bundle'] . '.' . $another_field_definition['field_name']);
342 $this->assertTrue(!empty($another_field) && !$another_field->isDeleted(), 'A field whose storage was not deleted is not marked for deletion.');
344 // Try to create a new field the same name as a deleted field and
345 // write data into it.
346 FieldStorageConfig::create($field_storage_definition)->save();
347 FieldConfig::create($field_definition)->save();
348 $field_storage = FieldStorageConfig::load('entity_test.' . $field_storage_definition['field_name']);
349 $this->assertTrue(!empty($field_storage) && !$field_storage->isDeleted(), 'A new storage with a previously used name is created.');
350 $field = FieldConfig::load('entity_test.' . $field_definition['bundle'] . '.' . $field_definition['field_name']);
351 $this->assertTrue(!empty($field) && !$field->isDeleted(), 'A new field for a previously used field name is created.');
353 // Save an entity with data for the field
354 $entity = EntityTest::create();
355 $values[0]['value'] = mt_rand(1, 127);
356 $entity->{$field_storage->getName()}->value = $values[0]['value'];
357 $entity = $this->entitySaveReload($entity);
359 // Verify the field is present on load
360 $this->assertIdentical(count($entity->{$field_storage->getName()}), count($values), "Data in previously deleted field saves and loads correctly");
361 foreach ($values as $delta => $value) {
362 $this->assertEqual($entity->{$field_storage->getName()}[$delta]->value, $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
366 public function testUpdateFieldType() {
367 $field_storage = FieldStorageConfig::create([
368 'field_name' => 'field_type',
369 'entity_type' => 'entity_test',
372 $field_storage->save();
375 $field_storage->set('type', 'integer');
376 $field_storage->save();
377 $this->fail(t('Cannot update a field to a different type.'));
379 catch (FieldException $e) {
380 $this->pass(t('Cannot update a field to a different type.'));
385 * Test updating a field storage.
387 public function testUpdate() {
388 // Create a field with a defined cardinality, so that we can ensure it's
389 // respected. Since cardinality enforcement is consistent across database
390 // systems, it makes a good test case.
392 $field_storage = FieldStorageConfig::create([
393 'field_name' => 'field_update',
394 'entity_type' => 'entity_test',
395 'type' => 'test_field',
396 'cardinality' => $cardinality,
398 $field_storage->save();
399 $field = FieldConfig::create([
400 'field_storage' => $field_storage,
401 'entity_type' => 'entity_test',
402 'bundle' => 'entity_test',
407 $entity = EntityTest::create();
408 // Fill in the entity with more values than $cardinality.
409 for ($i = 0; $i < 20; $i++) {
410 // We can not use $i here because 0 values are filtered out.
411 $entity->field_update[] = $i + 1;
413 // Load back and assert there are $cardinality number of values.
414 $entity = $this->entitySaveReload($entity);
415 $this->assertEqual(count($entity->field_update), $field_storage->getCardinality());
416 // Now check the values themselves.
417 for ($delta = 0; $delta < $cardinality; $delta++) {
418 $this->assertEqual($entity->field_update[$delta]->value, $delta + 1);
420 // Increase $cardinality and set the field cardinality to the new value.
421 $field_storage->setCardinality(++$cardinality);
422 $field_storage->save();
423 } while ($cardinality < 6);
427 * Test field type modules forbidding an update.
429 public function testUpdateForbid() {
430 $field_storage = FieldStorageConfig::create([
431 'field_name' => 'forbidden',
432 'entity_type' => 'entity_test',
433 'type' => 'test_field',
439 $field_storage->save();
440 $field_storage->setSetting('changeable', $field_storage->getSetting('changeable') + 1);
442 $field_storage->save();
443 $this->pass(t("A changeable setting can be updated."));
445 catch (FieldStorageDefinitionUpdateForbiddenException $e) {
446 $this->fail(t("An unchangeable setting cannot be updated."));
448 $field_storage->setSetting('unchangeable', $field_storage->getSetting('unchangeable') + 1);
450 $field_storage->save();
451 $this->fail(t("An unchangeable setting can be updated."));
453 catch (FieldStorageDefinitionUpdateForbiddenException $e) {
454 $this->pass(t("An unchangeable setting cannot be updated."));