PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/core/modules/field/tests/src/Kernel/FieldCrudTest.php

http://github.com/drupal/drupal
PHP | 344 lines | 177 code | 57 blank | 110 comment | 5 complexity | 538e565a59eca76f78d78c301ec352c1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. namespace Drupal\Tests\field\Kernel;
  3. use Drupal\Core\Entity\EntityStorageException;
  4. use Drupal\Core\Field\FieldException;
  5. use Drupal\entity_test\Entity\EntityTest;
  6. use Drupal\field\Entity\FieldStorageConfig;
  7. use Drupal\field\Entity\FieldConfig;
  8. /**
  9. * Create field entities by attaching fields to entities.
  10. *
  11. * @coversDefaultClass \Drupal\Core\Field\FieldConfigBase
  12. *
  13. * @group field
  14. */
  15. class FieldCrudTest extends FieldKernelTestBase {
  16. /**
  17. * The field storage entity.
  18. *
  19. * @var \Drupal\field\Entity\FieldStorageConfig
  20. */
  21. protected $fieldStorage;
  22. /**
  23. * The field entity definition.
  24. *
  25. * @var array
  26. */
  27. protected $fieldStorageDefinition;
  28. /**
  29. * The field entity definition.
  30. *
  31. * @var array
  32. */
  33. protected $fieldDefinition;
  34. public function setUp() {
  35. parent::setUp();
  36. $this->fieldStorageDefinition = [
  37. 'field_name' => mb_strtolower($this->randomMachineName()),
  38. 'entity_type' => 'entity_test',
  39. 'type' => 'test_field',
  40. ];
  41. $this->fieldStorage = FieldStorageConfig::create($this->fieldStorageDefinition);
  42. $this->fieldStorage->save();
  43. $this->fieldDefinition = [
  44. 'field_name' => $this->fieldStorage->getName(),
  45. 'entity_type' => 'entity_test',
  46. 'bundle' => 'entity_test',
  47. ];
  48. }
  49. // TODO : test creation with
  50. // - a full fledged $field structure, check that all the values are there
  51. // - a minimal $field structure, check all default values are set
  52. // defer actual $field comparison to a helper function, used for the two cases above,
  53. // and for testUpdateField
  54. /**
  55. * Test the creation of a field.
  56. */
  57. public function testCreateField() {
  58. $field = FieldConfig::create($this->fieldDefinition);
  59. $field->save();
  60. $field = FieldConfig::load($field->id());
  61. $this->assertEquals('TRUE', $field->getSetting('field_setting_from_config_data'));
  62. $this->assertNull($field->getSetting('config_data_from_field_setting'));
  63. // Read the configuration. Check against raw configuration data rather than
  64. // the loaded ConfigEntity, to be sure we check that the defaults are
  65. // applied on write.
  66. $config = $this->config('field.field.' . $field->id())->get();
  67. $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  68. $this->assertTrue($config['settings']['config_data_from_field_setting']);
  69. $this->assertTrue(!isset($config['settings']['field_setting_from_config_data']));
  70. // Since we are working with raw configuration, this needs to be unset
  71. // manually.
  72. // @see Drupal\field_test\Plugin\Field\FieldType\TestItem::fieldSettingsFromConfigData()
  73. unset($config['settings']['config_data_from_field_setting']);
  74. // Check that default values are set.
  75. $this->assertEqual($config['required'], FALSE, 'Required defaults to false.');
  76. $this->assertIdentical($config['label'], $this->fieldDefinition['field_name'], 'Label defaults to field name.');
  77. $this->assertIdentical($config['description'], '', 'Description defaults to empty string.');
  78. // Check that default settings are set.
  79. $this->assertEqual($config['settings'], $field_type_manager->getDefaultFieldSettings($this->fieldStorageDefinition['type']), 'Default field settings have been written.');
  80. // Check that the denormalized 'field_type' was properly written.
  81. $this->assertEqual($config['field_type'], $this->fieldStorageDefinition['type']);
  82. // Guarantee that the field/bundle combination is unique.
  83. try {
  84. FieldConfig::create($this->fieldDefinition)->save();
  85. $this->fail('Cannot create two fields with the same field / bundle combination.');
  86. }
  87. catch (EntityStorageException $e) {
  88. $this->pass('Cannot create two fields with the same field / bundle combination.');
  89. }
  90. // Check that the specified field exists.
  91. try {
  92. $this->fieldDefinition['field_name'] = $this->randomMachineName();
  93. FieldConfig::create($this->fieldDefinition)->save();
  94. $this->fail('Cannot create a field with a non-existing storage.');
  95. }
  96. catch (FieldException $e) {
  97. $this->pass('Cannot create a field with a non-existing storage.');
  98. }
  99. // TODO: test other failures.
  100. }
  101. /**
  102. * Tests setting and adding property constraints to a configurable field.
  103. *
  104. * @covers ::setPropertyConstraints
  105. * @covers ::addPropertyConstraints
  106. */
  107. public function testFieldPropertyConstraints() {
  108. $field = FieldConfig::create($this->fieldDefinition);
  109. $field->save();
  110. $field_name = $this->fieldStorage->getName();
  111. // Test that constraints are applied to configurable fields. A TestField and
  112. // a Range constraint are added dynamically to limit the field to values
  113. // between 0 and 32.
  114. // @see field_test_entity_bundle_field_info_alter()
  115. \Drupal::state()->set('field_test_constraint', $field_name);
  116. // Clear the field definitions cache so the new constraints added by
  117. // field_test_entity_bundle_field_info_alter() are taken into consideration.
  118. \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
  119. // Test the newly added property constraints in the same request as when the
  120. // caches were cleared. This will test the field definitions that are stored
  121. // in the static cache of
  122. // \Drupal\Core\Entity\EntityFieldManager::getFieldDefinitions().
  123. $this->doFieldPropertyConstraintsTests();
  124. // In order to test a real-world scenario where the property constraints are
  125. // only stored in the persistent cache of
  126. // \Drupal\Core\Entity\EntityFieldManager::getFieldDefinitions(), we need to
  127. // simulate a new request by removing the 'entity_field.manager' service,
  128. // thus forcing it to be re-initialized without static caches.
  129. \Drupal::getContainer()->set('entity_field.manager', NULL);
  130. // This will test the field definitions that are stored in the persistent
  131. // cache by \Drupal\Core\Entity\EntityFieldManager::getFieldDefinitions().
  132. $this->doFieldPropertyConstraintsTests();
  133. }
  134. /**
  135. * Tests configurable field validation.
  136. *
  137. * @see field_test_entity_bundle_field_info_alter()
  138. */
  139. protected function doFieldPropertyConstraintsTests() {
  140. $field_name = $this->fieldStorage->getName();
  141. // Check that a valid value (not -2 and between 0 and 32) doesn't trigger
  142. // any violation.
  143. $entity = EntityTest::create();
  144. $entity->set($field_name, 1);
  145. $violations = $entity->validate();
  146. $this->assertCount(0, $violations, 'No violations found when in-range value passed.');
  147. // Check that a value that is specifically restricted triggers both
  148. // violations.
  149. $entity->set($field_name, -2);
  150. $violations = $entity->validate();
  151. $this->assertCount(2, $violations, 'Two violations found when using a null and outside the range value.');
  152. $this->assertEquals($field_name . '.0.value', $violations[0]->getPropertyPath());
  153. $this->assertEquals(t('%name does not accept the value @value.', ['%name' => $field_name, '@value' => -2]), $violations[0]->getMessage());
  154. $this->assertEquals($field_name . '.0.value', $violations[1]->getPropertyPath());
  155. $this->assertEquals(t('This value should be %limit or more.', ['%limit' => 0]), $violations[1]->getMessage());
  156. // Check that a value that is not specifically restricted but outside the
  157. // range triggers the expected violation.
  158. $entity->set($field_name, 33);
  159. $violations = $entity->validate();
  160. $this->assertCount(1, $violations, 'Violations found when using value outside the range.');
  161. $this->assertEquals($field_name . '.0.value', $violations[0]->getPropertyPath());
  162. $this->assertEquals(t('This value should be %limit or less.', ['%limit' => 32]), $violations[0]->getMessage());
  163. }
  164. /**
  165. * Test creating a field with custom storage set.
  166. */
  167. public function testCreateFieldCustomStorage() {
  168. $field_name = mb_strtolower($this->randomMachineName());
  169. \Drupal::state()->set('field_test_custom_storage', $field_name);
  170. $field_storage = FieldStorageConfig::create([
  171. 'field_name' => $field_name,
  172. 'entity_type' => 'entity_test',
  173. 'type' => 'test_field',
  174. 'custom_storage' => TRUE,
  175. ]);
  176. $field_storage->save();
  177. $field = FieldConfig::create([
  178. 'field_name' => $field_storage->getName(),
  179. 'entity_type' => 'entity_test',
  180. 'bundle' => 'entity_test',
  181. ]);
  182. $field->save();
  183. \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
  184. // Check that no table has been created for the field.
  185. $this->assertFalse(\Drupal::database()->schema()->tableExists('entity_test__' . $field_storage->getName()));
  186. // Save an entity with a value in the custom storage field and verify no
  187. // data is retrieved on load.
  188. $entity = EntityTest::create(['name' => $this->randomString(), $field_name => 'Test value']);
  189. $this->assertIdentical('Test value', $entity->{$field_name}->value, 'The test value is set on the field.');
  190. $entity->save();
  191. $entity = EntityTest::load($entity->id());
  192. $this->assertNull($entity->{$field_name}->value, 'The loaded entity field value is NULL.');
  193. }
  194. /**
  195. * Test reading back a field definition.
  196. */
  197. public function testReadField() {
  198. FieldConfig::create($this->fieldDefinition)->save();
  199. // Read the field back.
  200. $field = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
  201. $this->assertTrue($this->fieldDefinition['field_name'] == $field->getName(), 'The field was properly read.');
  202. $this->assertTrue($this->fieldDefinition['entity_type'] == $field->getTargetEntityTypeId(), 'The field was properly read.');
  203. $this->assertTrue($this->fieldDefinition['bundle'] == $field->getTargetBundle(), 'The field was properly read.');
  204. }
  205. /**
  206. * Test the update of a field.
  207. */
  208. public function testUpdateField() {
  209. FieldConfig::create($this->fieldDefinition)->save();
  210. // Check that basic changes are saved.
  211. $field = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
  212. $field->setRequired(!$field->isRequired());
  213. $field->setLabel($this->randomMachineName());
  214. $field->set('description', $this->randomMachineName());
  215. $field->setSetting('test_field_setting', $this->randomMachineName());
  216. $field->save();
  217. $field_new = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
  218. $this->assertEqual($field->isRequired(), $field_new->isRequired(), '"required" change is saved');
  219. $this->assertEqual($field->getLabel(), $field_new->getLabel(), '"label" change is saved');
  220. $this->assertEqual($field->getDescription(), $field_new->getDescription(), '"description" change is saved');
  221. // TODO: test failures.
  222. }
  223. /**
  224. * Test the deletion of a field with no data.
  225. */
  226. public function testDeleteFieldNoData() {
  227. // Deleting and purging fields with data is tested in
  228. // \Drupal\Tests\field\Kernel\BulkDeleteTest.
  229. // Create two fields for the same field storage so we can test that only one
  230. // is deleted.
  231. FieldConfig::create($this->fieldDefinition)->save();
  232. $another_field_definition = $this->fieldDefinition;
  233. $another_field_definition['bundle'] .= '_another_bundle';
  234. entity_test_create_bundle($another_field_definition['bundle']);
  235. FieldConfig::create($another_field_definition)->save();
  236. // Test that the first field is not deleted, and then delete it.
  237. $field = current(\Drupal::entityTypeManager()->getStorage('field_config')->loadByProperties(['entity_type' => 'entity_test', 'field_name' => $this->fieldDefinition['field_name'], 'bundle' => $this->fieldDefinition['bundle'], 'include_deleted' => TRUE]));
  238. $this->assertTrue(!empty($field) && empty($field->deleted), 'A new field is not marked for deletion.');
  239. $field->delete();
  240. // Make sure the field was deleted without being marked for purging as there
  241. // was no data.
  242. $fields = \Drupal::entityTypeManager()->getStorage('field_config')->loadByProperties(['entity_type' => 'entity_test', 'field_name' => $this->fieldDefinition['field_name'], 'bundle' => $this->fieldDefinition['bundle'], 'include_deleted' => TRUE]);
  243. $this->assertEquals(0, count($fields), 'A deleted field is marked for deletion.');
  244. // Try to load the field normally and make sure it does not show up.
  245. $field = FieldConfig::load('entity_test.' . '.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
  246. $this->assertTrue(empty($field), 'Field was deleted');
  247. // Make sure the other field is not deleted.
  248. $another_field = FieldConfig::load('entity_test.' . $another_field_definition['bundle'] . '.' . $another_field_definition['field_name']);
  249. $this->assertTrue(!empty($another_field) && !$another_field->isDeleted(), 'A non-deleted field is not marked for deletion.');
  250. }
  251. /**
  252. * Tests the cross deletion behavior between field storages and fields.
  253. */
  254. public function testDeleteFieldCrossDeletion() {
  255. $field_definition_2 = $this->fieldDefinition;
  256. $field_definition_2['bundle'] .= '_another_bundle';
  257. entity_test_create_bundle($field_definition_2['bundle']);
  258. // Check that deletion of a field storage deletes its fields.
  259. $field_storage = $this->fieldStorage;
  260. FieldConfig::create($this->fieldDefinition)->save();
  261. FieldConfig::create($field_definition_2)->save();
  262. $field_storage->delete();
  263. $this->assertNull(FieldConfig::loadByName('entity_test', $this->fieldDefinition['bundle'], $field_storage->getName()));
  264. $this->assertNull(FieldConfig::loadByName('entity_test', $field_definition_2['bundle'], $field_storage->getName()));
  265. // Check that deletion of the last field deletes the storage.
  266. $field_storage = FieldStorageConfig::create($this->fieldStorageDefinition);
  267. $field_storage->save();
  268. $field = FieldConfig::create($this->fieldDefinition);
  269. $field->save();
  270. $field_2 = FieldConfig::create($field_definition_2);
  271. $field_2->save();
  272. $field->delete();
  273. $this->assertNotEmpty(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
  274. $field_2->delete();
  275. $this->assertNull(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
  276. // Check that deletion of all fields using a storage simultaneously deletes
  277. // the storage.
  278. $field_storage = FieldStorageConfig::create($this->fieldStorageDefinition);
  279. $field_storage->save();
  280. $field = FieldConfig::create($this->fieldDefinition);
  281. $field->save();
  282. $field_2 = FieldConfig::create($field_definition_2);
  283. $field_2->save();
  284. $this->container->get('entity_type.manager')->getStorage('field_config')->delete([$field, $field_2]);
  285. $this->assertNull(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
  286. }
  287. }