EntityDefinitionUpdateTest.php 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. <?php
  2. namespace Drupal\KernelTests\Core\Entity;
  3. use Drupal\Component\Plugin\Exception\PluginNotFoundException;
  4. use Drupal\Core\Database\Database;
  5. use Drupal\Core\Database\DatabaseExceptionWrapper;
  6. use Drupal\Core\Database\IntegrityConstraintViolationException;
  7. use Drupal\Core\Entity\ContentEntityType;
  8. use Drupal\Core\Entity\EntityStorageException;
  9. use Drupal\Core\Entity\EntityTypeEvents;
  10. use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
  11. use Drupal\Core\Field\BaseFieldDefinition;
  12. use Drupal\Core\Field\FieldException;
  13. use Drupal\Core\Field\FieldStorageDefinitionEvents;
  14. use Drupal\Core\Language\LanguageInterface;
  15. use Drupal\entity_test_update\Entity\EntityTestUpdate;
  16. use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
  17. /**
  18. * Tests EntityDefinitionUpdateManager functionality.
  19. *
  20. * @group Entity
  21. */
  22. class EntityDefinitionUpdateTest extends EntityKernelTestBase {
  23. use EntityDefinitionTestTrait;
  24. /**
  25. * The entity definition update manager.
  26. *
  27. * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
  28. */
  29. protected $entityDefinitionUpdateManager;
  30. /**
  31. * The database connection.
  32. *
  33. * @var \Drupal\Core\Database\Connection
  34. */
  35. protected $database;
  36. /**
  37. * Modules to enable.
  38. *
  39. * @var array
  40. */
  41. public static $modules = ['entity_test_update'];
  42. /**
  43. * {@inheritdoc}
  44. */
  45. protected function setUp() {
  46. parent::setUp();
  47. $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager');
  48. $this->database = $this->container->get('database');
  49. // Install every entity type's schema that wasn't installed in the parent
  50. // method.
  51. foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(['user', 'entity_test'])) as $entity_type_id => $entity_type) {
  52. $this->installEntitySchema($entity_type_id);
  53. }
  54. }
  55. /**
  56. * Tests that new entity type definitions are correctly handled.
  57. */
  58. public function testNewEntityType() {
  59. $entity_type_id = 'entity_test_new';
  60. $schema = $this->database->schema();
  61. // Check that the "entity_test_new" is not defined.
  62. $entity_types = $this->entityManager->getDefinitions();
  63. $this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.');
  64. $this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.');
  65. // Check that the "entity_test_new" is now defined and the related schema
  66. // has been created.
  67. $this->enableNewEntityType();
  68. $entity_types = $this->entityManager->getDefinitions();
  69. $this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.');
  70. $this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.');
  71. }
  72. /**
  73. * Tests when no definition update is needed.
  74. */
  75. public function testNoUpdates() {
  76. // Ensure that the definition update manager reports no updates.
  77. $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that no updates are needed.');
  78. $this->assertIdentical($this->entityDefinitionUpdateManager->getChangeSummary(), [], 'EntityDefinitionUpdateManager reports an empty change summary.');
  79. // Ensure that applyUpdates() runs without error (it's not expected to do
  80. // anything when there aren't updates).
  81. $this->entityDefinitionUpdateManager->applyUpdates();
  82. }
  83. /**
  84. * Tests updating entity schema when there are no existing entities.
  85. */
  86. public function testEntityTypeUpdateWithoutData() {
  87. // The 'entity_test_update' entity type starts out non-revisionable, so
  88. // ensure the revision table hasn't been created during setUp().
  89. $this->assertFalse($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table not created for entity_test_update.');
  90. // Update it to be revisionable and ensure the definition update manager
  91. // reports that an update is needed.
  92. $this->updateEntityTypeToRevisionable();
  93. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  94. $expected = [
  95. 'entity_test_update' => [
  96. t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
  97. // The revision key is now defined, so the revision field needs to be
  98. // created.
  99. t('The %field_name field needs to be installed.', ['%field_name' => 'Revision ID']),
  100. ],
  101. ];
  102. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  103. // Run the update and ensure the revision table is created.
  104. $this->entityDefinitionUpdateManager->applyUpdates();
  105. $this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table created for entity_test_update.');
  106. }
  107. /**
  108. * Tests updating entity schema when there are existing entities.
  109. */
  110. public function testEntityTypeUpdateWithData() {
  111. // Save an entity.
  112. $this->entityManager->getStorage('entity_test_update')->create()->save();
  113. // Update the entity type to be revisionable and try to apply the update.
  114. // It's expected to throw an exception.
  115. $this->updateEntityTypeToRevisionable();
  116. try {
  117. $this->entityDefinitionUpdateManager->applyUpdates();
  118. $this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.');
  119. }
  120. catch (EntityStorageException $e) {
  121. $this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.');
  122. }
  123. }
  124. /**
  125. * Tests creating, updating, and deleting a base field if no entities exist.
  126. */
  127. public function testBaseFieldCreateUpdateDeleteWithoutData() {
  128. // Add a base field, ensure the update manager reports it, and the update
  129. // creates its schema.
  130. $this->addBaseField();
  131. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  132. $expected = [
  133. 'entity_test_update' => [
  134. t('The %field_name field needs to be installed.', ['%field_name' => t('A new base field')]),
  135. ],
  136. ];
  137. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  138. $this->entityDefinitionUpdateManager->applyUpdates();
  139. $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
  140. // Add an index on the base field, ensure the update manager reports it,
  141. // and the update creates it.
  142. $this->addBaseFieldIndex();
  143. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  144. $expected = [
  145. 'entity_test_update' => [
  146. t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
  147. ],
  148. ];
  149. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  150. $this->entityDefinitionUpdateManager->applyUpdates();
  151. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index created.');
  152. // Remove the above index, ensure the update manager reports it, and the
  153. // update deletes it.
  154. $this->removeBaseFieldIndex();
  155. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  156. $expected = [
  157. 'entity_test_update' => [
  158. t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
  159. ],
  160. ];
  161. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  162. $this->entityDefinitionUpdateManager->applyUpdates();
  163. $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index deleted.');
  164. // Update the type of the base field from 'string' to 'text', ensure the
  165. // update manager reports it, and the update adjusts the schema
  166. // accordingly.
  167. $this->modifyBaseField();
  168. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  169. $expected = [
  170. 'entity_test_update' => [
  171. t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
  172. ],
  173. ];
  174. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  175. $this->entityDefinitionUpdateManager->applyUpdates();
  176. $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Original column deleted in shared table for new_base_field.');
  177. $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__value'), 'Value column created in shared table for new_base_field.');
  178. $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__format'), 'Format column created in shared table for new_base_field.');
  179. // Remove the base field, ensure the update manager reports it, and the
  180. // update deletes the schema.
  181. $this->removeBaseField();
  182. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  183. $expected = [
  184. 'entity_test_update' => [
  185. t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new base field')]),
  186. ],
  187. ];
  188. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  189. $this->entityDefinitionUpdateManager->applyUpdates();
  190. $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_value'), 'Value column deleted from shared table for new_base_field.');
  191. $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_format'), 'Format column deleted from shared table for new_base_field.');
  192. }
  193. /**
  194. * Tests creating, updating, and deleting a bundle field if no entities exist.
  195. */
  196. public function testBundleFieldCreateUpdateDeleteWithoutData() {
  197. // Add a bundle field, ensure the update manager reports it, and the update
  198. // creates its schema.
  199. $this->addBundleField();
  200. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  201. $expected = [
  202. 'entity_test_update' => [
  203. t('The %field_name field needs to be installed.', ['%field_name' => t('A new bundle field')]),
  204. ],
  205. ];
  206. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  207. $this->entityDefinitionUpdateManager->applyUpdates();
  208. $this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
  209. // Update the type of the base field from 'string' to 'text', ensure the
  210. // update manager reports it, and the update adjusts the schema
  211. // accordingly.
  212. $this->modifyBundleField();
  213. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  214. $expected = [
  215. 'entity_test_update' => [
  216. t('The %field_name field needs to be updated.', ['%field_name' => t('A new bundle field')]),
  217. ],
  218. ];
  219. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  220. $this->entityDefinitionUpdateManager->applyUpdates();
  221. $this->assertTrue($this->database->schema()->fieldExists('entity_test_update__new_bundle_field', 'new_bundle_field_format'), 'Format column created in dedicated table for new_base_field.');
  222. // Remove the bundle field, ensure the update manager reports it, and the
  223. // update deletes the schema.
  224. $this->removeBundleField();
  225. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  226. $expected = [
  227. 'entity_test_update' => [
  228. t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new bundle field')]),
  229. ],
  230. ];
  231. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  232. $this->entityDefinitionUpdateManager->applyUpdates();
  233. $this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
  234. }
  235. /**
  236. * Tests creating and deleting a base field if entities exist.
  237. *
  238. * This tests deletion when there are existing entities, but not existing data
  239. * for the field being deleted.
  240. *
  241. * @see testBaseFieldDeleteWithExistingData()
  242. */
  243. public function testBaseFieldCreateDeleteWithExistingEntities() {
  244. // Save an entity.
  245. $name = $this->randomString();
  246. $storage = $this->entityManager->getStorage('entity_test_update');
  247. $entity = $storage->create(['name' => $name]);
  248. $entity->save();
  249. // Add a base field and run the update. Ensure the base field's column is
  250. // created and the prior saved entity data is still there.
  251. $this->addBaseField();
  252. $this->entityDefinitionUpdateManager->applyUpdates();
  253. $schema_handler = $this->database->schema();
  254. $this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
  255. $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
  256. $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
  257. // Remove the base field and run the update. Ensure the base field's column
  258. // is deleted and the prior saved entity data is still there.
  259. $this->removeBaseField();
  260. $this->entityDefinitionUpdateManager->applyUpdates();
  261. $this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.');
  262. $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
  263. $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
  264. // Add a base field with a required property and run the update. Ensure
  265. // 'not null' is not applied and thus no exception is thrown.
  266. $this->addBaseField('shape_required');
  267. $this->entityDefinitionUpdateManager->applyUpdates();
  268. $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
  269. $this->assertTrue($assert, 'Columns created in shared table for new_base_field.');
  270. // Recreate the field after emptying the base table and check that its
  271. // columns are not 'not null'.
  272. // @todo Revisit this test when allowing for required storage field
  273. // definitions. See https://www.drupal.org/node/2390495.
  274. $entity->delete();
  275. $this->removeBaseField();
  276. $this->entityDefinitionUpdateManager->applyUpdates();
  277. $assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
  278. $this->assert($assert, 'Columns removed from the shared table for new_base_field.');
  279. $this->addBaseField('shape_required');
  280. $this->entityDefinitionUpdateManager->applyUpdates();
  281. $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
  282. $this->assertTrue($assert, 'Columns created again in shared table for new_base_field.');
  283. $entity = $storage->create(['name' => $name]);
  284. $entity->save();
  285. $this->pass('The new_base_field columns are still nullable');
  286. }
  287. /**
  288. * Tests creating and deleting a bundle field if entities exist.
  289. *
  290. * This tests deletion when there are existing entities, but not existing data
  291. * for the field being deleted.
  292. *
  293. * @see testBundleFieldDeleteWithExistingData()
  294. */
  295. public function testBundleFieldCreateDeleteWithExistingEntities() {
  296. // Save an entity.
  297. $name = $this->randomString();
  298. $storage = $this->entityManager->getStorage('entity_test_update');
  299. $entity = $storage->create(['name' => $name]);
  300. $entity->save();
  301. // Add a bundle field and run the update. Ensure the bundle field's table
  302. // is created and the prior saved entity data is still there.
  303. $this->addBundleField();
  304. $this->entityDefinitionUpdateManager->applyUpdates();
  305. $schema_handler = $this->database->schema();
  306. $this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
  307. $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
  308. $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
  309. // Remove the base field and run the update. Ensure the bundle field's
  310. // table is deleted and the prior saved entity data is still there.
  311. $this->removeBundleField();
  312. $this->entityDefinitionUpdateManager->applyUpdates();
  313. $this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
  314. $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
  315. $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
  316. // Test that required columns are created as 'not null'.
  317. $this->addBundleField('shape_required');
  318. $this->entityDefinitionUpdateManager->applyUpdates();
  319. $message = 'The new_bundle_field_shape column is not nullable.';
  320. $values = [
  321. 'bundle' => $entity->bundle(),
  322. 'deleted' => 0,
  323. 'entity_id' => $entity->id(),
  324. 'revision_id' => $entity->id(),
  325. 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
  326. 'delta' => 0,
  327. 'new_bundle_field_color' => $this->randomString(),
  328. ];
  329. try {
  330. // Try to insert a record without providing a value for the 'not null'
  331. // column. This should fail.
  332. $this->database->insert('entity_test_update__new_bundle_field')
  333. ->fields($values)
  334. ->execute();
  335. $this->fail($message);
  336. }
  337. catch (\RuntimeException $e) {
  338. if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) {
  339. // Now provide a value for the 'not null' column. This is expected to
  340. // succeed.
  341. $values['new_bundle_field_shape'] = $this->randomString();
  342. $this->database->insert('entity_test_update__new_bundle_field')
  343. ->fields($values)
  344. ->execute();
  345. $this->pass($message);
  346. }
  347. else {
  348. // Keep throwing it.
  349. throw $e;
  350. }
  351. }
  352. }
  353. /**
  354. * Tests deleting a base field when it has existing data.
  355. */
  356. public function testBaseFieldDeleteWithExistingData() {
  357. // Add the base field and run the update.
  358. $this->addBaseField();
  359. $this->entityDefinitionUpdateManager->applyUpdates();
  360. // Save an entity with the base field populated.
  361. $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => 'foo'])->save();
  362. // Remove the base field and apply updates. It's expected to throw an
  363. // exception.
  364. // @todo Revisit that expectation once purging is implemented for
  365. // all fields: https://www.drupal.org/node/2282119.
  366. $this->removeBaseField();
  367. try {
  368. $this->entityDefinitionUpdateManager->applyUpdates();
  369. $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
  370. }
  371. catch (FieldStorageDefinitionUpdateForbiddenException $e) {
  372. $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
  373. }
  374. }
  375. /**
  376. * Tests deleting a bundle field when it has existing data.
  377. */
  378. public function testBundleFieldDeleteWithExistingData() {
  379. // Add the bundle field and run the update.
  380. $this->addBundleField();
  381. $this->entityDefinitionUpdateManager->applyUpdates();
  382. // Save an entity with the bundle field populated.
  383. entity_test_create_bundle('custom');
  384. $this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save();
  385. // Remove the bundle field and apply updates. It's expected to throw an
  386. // exception.
  387. // @todo Revisit that expectation once purging is implemented for
  388. // all fields: https://www.drupal.org/node/2282119.
  389. $this->removeBundleField();
  390. try {
  391. $this->entityDefinitionUpdateManager->applyUpdates();
  392. $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
  393. }
  394. catch (FieldStorageDefinitionUpdateForbiddenException $e) {
  395. $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
  396. }
  397. }
  398. /**
  399. * Tests updating a base field when it has existing data.
  400. */
  401. public function testBaseFieldUpdateWithExistingData() {
  402. // Add the base field and run the update.
  403. $this->addBaseField();
  404. $this->entityDefinitionUpdateManager->applyUpdates();
  405. // Save an entity with the base field populated.
  406. $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => 'foo'])->save();
  407. // Change the field's field type and apply updates. It's expected to
  408. // throw an exception.
  409. $this->modifyBaseField();
  410. try {
  411. $this->entityDefinitionUpdateManager->applyUpdates();
  412. $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
  413. }
  414. catch (FieldStorageDefinitionUpdateForbiddenException $e) {
  415. $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
  416. }
  417. }
  418. /**
  419. * Tests updating a bundle field when it has existing data.
  420. */
  421. public function testBundleFieldUpdateWithExistingData() {
  422. // Add the bundle field and run the update.
  423. $this->addBundleField();
  424. $this->entityDefinitionUpdateManager->applyUpdates();
  425. // Save an entity with the bundle field populated.
  426. entity_test_create_bundle('custom');
  427. $this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save();
  428. // Change the field's field type and apply updates. It's expected to
  429. // throw an exception.
  430. $this->modifyBundleField();
  431. try {
  432. $this->entityDefinitionUpdateManager->applyUpdates();
  433. $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
  434. }
  435. catch (FieldStorageDefinitionUpdateForbiddenException $e) {
  436. $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
  437. }
  438. }
  439. /**
  440. * Tests creating and deleting a multi-field index when there are no existing entities.
  441. */
  442. public function testEntityIndexCreateDeleteWithoutData() {
  443. // Add an entity index and ensure the update manager reports that as an
  444. // update to the entity type.
  445. $this->addEntityIndex();
  446. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  447. $expected = [
  448. 'entity_test_update' => [
  449. t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
  450. ],
  451. ];
  452. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  453. // Run the update and ensure the new index is created.
  454. $this->entityDefinitionUpdateManager->applyUpdates();
  455. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
  456. // Remove the index and ensure the update manager reports that as an
  457. // update to the entity type.
  458. $this->removeEntityIndex();
  459. $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
  460. $expected = [
  461. 'entity_test_update' => [
  462. t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
  463. ],
  464. ];
  465. $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
  466. // Run the update and ensure the index is deleted.
  467. $this->entityDefinitionUpdateManager->applyUpdates();
  468. $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
  469. // Test that composite indexes are handled correctly when dropping and
  470. // re-creating one of their columns.
  471. $this->addEntityIndex();
  472. $this->entityDefinitionUpdateManager->applyUpdates();
  473. $storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update');
  474. $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
  475. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
  476. $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
  477. $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
  478. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition);
  479. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.');
  480. }
  481. /**
  482. * Tests creating a multi-field index when there are existing entities.
  483. */
  484. public function testEntityIndexCreateWithData() {
  485. // Save an entity.
  486. $name = $this->randomString();
  487. $entity = $this->entityManager->getStorage('entity_test_update')->create(['name' => $name]);
  488. $entity->save();
  489. // Add an entity index, run the update. Ensure that the index is created
  490. // despite having data.
  491. $this->addEntityIndex();
  492. $this->entityDefinitionUpdateManager->applyUpdates();
  493. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index added.');
  494. }
  495. /**
  496. * Tests entity type and field storage definition events.
  497. */
  498. public function testDefinitionEvents() {
  499. /** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */
  500. $event_subscriber = $this->container->get('entity_test.definition.subscriber');
  501. $event_subscriber->enableEventTracking();
  502. // Test field storage definition events.
  503. $storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev'));
  504. $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.');
  505. $this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
  506. $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.');
  507. $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.');
  508. $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
  509. $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.');
  510. $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update was not dispatched yet.');
  511. $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $storage_definition);
  512. $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update event successfully dispatched.');
  513. // Test entity type events.
  514. $entity_type = $this->entityManager->getDefinition('entity_test_rev');
  515. $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create was not dispatched yet.');
  516. $this->entityManager->onEntityTypeCreate($entity_type);
  517. $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create event successfully dispatched.');
  518. $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update was not dispatched yet.');
  519. $this->entityManager->onEntityTypeUpdate($entity_type, $entity_type);
  520. $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update event successfully dispatched.');
  521. $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete was not dispatched yet.');
  522. $this->entityManager->onEntityTypeDelete($entity_type);
  523. $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete event successfully dispatched.');
  524. }
  525. /**
  526. * Tests updating entity schema and creating a base field.
  527. *
  528. * This tests updating entity schema and creating a base field at the same
  529. * time when there are no existing entities.
  530. */
  531. public function testEntityTypeSchemaUpdateAndBaseFieldCreateWithoutData() {
  532. $this->updateEntityTypeToRevisionable();
  533. $this->addBaseField();
  534. $message = 'Successfully updated entity schema and created base field at the same time.';
  535. // Entity type updates create base fields as well, thus make sure doing both
  536. // at the same time does not lead to errors due to the base field being
  537. // created twice.
  538. try {
  539. $this->entityDefinitionUpdateManager->applyUpdates();
  540. $this->pass($message);
  541. }
  542. catch (\Exception $e) {
  543. $this->fail($message);
  544. throw $e;
  545. }
  546. }
  547. /**
  548. * Tests updating entity schema and creating a revisionable base field.
  549. *
  550. * This tests updating entity schema and creating a revisionable base field
  551. * at the same time when there are no existing entities.
  552. */
  553. public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData() {
  554. $this->updateEntityTypeToRevisionable();
  555. $this->addRevisionableBaseField();
  556. $message = 'Successfully updated entity schema and created revisionable base field at the same time.';
  557. // Entity type updates create base fields as well, thus make sure doing both
  558. // at the same time does not lead to errors due to the base field being
  559. // created twice.
  560. try {
  561. $this->entityDefinitionUpdateManager->applyUpdates();
  562. $this->pass($message);
  563. }
  564. catch (\Exception $e) {
  565. $this->fail($message);
  566. throw $e;
  567. }
  568. }
  569. /**
  570. * Tests applying single updates.
  571. */
  572. public function testSingleActionCalls() {
  573. $db_schema = $this->database->schema();
  574. // Ensure that a non-existing entity type cannot be installed.
  575. $message = 'A non-existing entity type cannot be installed';
  576. try {
  577. $this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo']));
  578. $this->fail($message);
  579. }
  580. catch (PluginNotFoundException $e) {
  581. $this->pass($message);
  582. }
  583. // Ensure that a field cannot be installed on non-existing entity type.
  584. $message = 'A field cannot be installed on a non-existing entity type';
  585. try {
  586. $storage_definition = BaseFieldDefinition::create('string')
  587. ->setLabel(t('A new revisionable base field'))
  588. ->setRevisionable(TRUE);
  589. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition);
  590. $this->fail($message);
  591. }
  592. catch (PluginNotFoundException $e) {
  593. $this->pass($message);
  594. }
  595. // Ensure that a non-existing field cannot be installed.
  596. $storage_definition = BaseFieldDefinition::create('string')
  597. ->setLabel(t('A new revisionable base field'))
  598. ->setRevisionable(TRUE);
  599. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition);
  600. $this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed.");
  601. // Ensure that installing an existing entity type is a no-op.
  602. $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
  603. $this->entityDefinitionUpdateManager->installEntityType($entity_type);
  604. $this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op');
  605. // Create a new base field.
  606. $this->addRevisionableBaseField();
  607. $storage_definition = BaseFieldDefinition::create('string')
  608. ->setLabel(t('A new revisionable base field'))
  609. ->setRevisionable(TRUE);
  610. $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
  611. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  612. $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
  613. // Ensure that installing an existing field is a no-op.
  614. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  615. $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing field is a no-op');
  616. // Update an existing field schema.
  617. $this->modifyBaseField();
  618. $storage_definition = BaseFieldDefinition::create('text')
  619. ->setName('new_base_field')
  620. ->setTargetEntityTypeId('entity_test_update')
  621. ->setLabel(t('A new revisionable base field'))
  622. ->setRevisionable(TRUE);
  623. $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
  624. $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists.");
  625. $this->assertTrue(
  626. $db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
  627. "New schema for 'new_base_field' has been created."
  628. );
  629. // Drop an existing field schema.
  630. $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
  631. $this->assertFalse(
  632. $db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
  633. "The schema for 'new_base_field' has been dropped."
  634. );
  635. // Make the entity type revisionable.
  636. $this->updateEntityTypeToRevisionable();
  637. $this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update.");
  638. $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
  639. $keys = $entity_type->getKeys();
  640. $keys['revision'] = 'revision_id';
  641. $entity_type->set('entity_keys', $keys);
  642. $this->entityDefinitionUpdateManager->updateEntityType($entity_type);
  643. $this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
  644. }
  645. /**
  646. * Ensures that a new field and index on a shared table are created.
  647. *
  648. * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema
  649. */
  650. public function testCreateFieldAndIndexOnSharedTable() {
  651. $this->addBaseField();
  652. $this->addBaseFieldIndex();
  653. $this->entityDefinitionUpdateManager->applyUpdates();
  654. $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
  655. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table.");
  656. // Check index size in for MySQL.
  657. if (Database::getConnection()->driver() == 'mysql') {
  658. $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject();
  659. $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
  660. }
  661. }
  662. /**
  663. * Ensures that a new entity level index is created when data exists.
  664. *
  665. * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
  666. */
  667. public function testCreateIndexUsingEntityStorageSchemaWithData() {
  668. // Save an entity.
  669. $name = $this->randomString();
  670. $storage = $this->entityManager->getStorage('entity_test_update');
  671. $entity = $storage->create(['name' => $name]);
  672. $entity->save();
  673. // Create an index.
  674. $indexes = [
  675. 'entity_test_update__type_index' => ['type'],
  676. ];
  677. $this->state->set('entity_test_update.additional_entity_indexes', $indexes);
  678. $this->entityDefinitionUpdateManager->applyUpdates();
  679. $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
  680. // Check index size in for MySQL.
  681. if (Database::getConnection()->driver() == 'mysql') {
  682. $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
  683. $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
  684. }
  685. }
  686. /**
  687. * Tests updating a base field when it has existing data.
  688. */
  689. public function testBaseFieldEntityKeyUpdateWithExistingData() {
  690. // Add the base field and run the update.
  691. $this->addBaseField();
  692. $this->entityDefinitionUpdateManager->applyUpdates();
  693. // Save an entity with the base field populated.
  694. $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();
  695. // Save an entity with the base field not populated.
  696. /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
  697. $entity = $this->entityManager->getStorage('entity_test_update')->create();
  698. $entity->save();
  699. // Promote the base field to an entity key. This will trigger the addition
  700. // of a NOT NULL constraint.
  701. $this->makeBaseFieldEntityKey();
  702. // Field storage CRUD operations use the last installed entity type
  703. // definition so we need to update it before doing any other field storage
  704. // updates.
  705. $this->entityDefinitionUpdateManager->updateEntityType($this->state->get('entity_test_update.entity_type'));
  706. // Try to apply the update and verify they fail since we have a NULL value.
  707. $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
  708. try {
  709. $this->entityDefinitionUpdateManager->applyUpdates();
  710. $this->fail($message);
  711. }
  712. catch (EntityStorageException $e) {
  713. $this->pass($message);
  714. }
  715. // Check that the update is correctly applied when no NULL data is left.
  716. $entity->set('new_base_field', $this->randomString());
  717. $entity->save();
  718. $this->entityDefinitionUpdateManager->applyUpdates();
  719. $this->pass('The update is correctly performed when no NULL data exists.');
  720. // Check that the update actually applied a NOT NULL constraint.
  721. $entity->set('new_base_field', NULL);
  722. $message = 'The NOT NULL constraint was correctly applied.';
  723. try {
  724. $entity->save();
  725. $this->fail($message);
  726. }
  727. catch (EntityStorageException $e) {
  728. $this->pass($message);
  729. }
  730. }
  731. /**
  732. * Check that field schema is correctly handled with long-named fields.
  733. */
  734. public function testLongNameFieldIndexes() {
  735. $this->addLongNameBaseField();
  736. $entity_type_id = 'entity_test_update';
  737. $entity_type = $this->entityManager->getDefinition($entity_type_id);
  738. $definitions = EntityTestUpdate::baseFieldDefinitions($entity_type);
  739. $name = 'new_long_named_entity_reference_base_field';
  740. $this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]);
  741. $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
  742. }
  743. /**
  744. * Tests adding a base field with initial values.
  745. */
  746. public function testInitialValue() {
  747. $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
  748. $db_schema = $this->database->schema();
  749. // Create two entities before adding the base field.
  750. /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
  751. $storage->create()->save();
  752. $storage->create()->save();
  753. // Add a base field with an initial value.
  754. $this->addBaseField();
  755. $storage_definition = BaseFieldDefinition::create('string')
  756. ->setLabel(t('A new base field'))
  757. ->setInitialValue('test value');
  758. $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
  759. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  760. $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
  761. // Check that the initial values have been applied.
  762. $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
  763. $entities = $storage->loadMultiple();
  764. $this->assertEquals('test value', $entities[1]->get('new_base_field')->value);
  765. $this->assertEquals('test value', $entities[2]->get('new_base_field')->value);
  766. }
  767. /**
  768. * Tests adding a base field with initial values inherited from another field.
  769. */
  770. public function testInitialValueFromField() {
  771. $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
  772. $db_schema = $this->database->schema();
  773. // Create two entities before adding the base field.
  774. /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
  775. $storage->create(['name' => 'First entity'])->save();
  776. $storage->create(['name' => 'Second entity'])->save();
  777. // Add a base field with an initial value inherited from another field.
  778. $this->addBaseField();
  779. $storage_definition = BaseFieldDefinition::create('string')
  780. ->setLabel(t('A new base field'))
  781. ->setInitialValueFromField('name');
  782. $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
  783. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  784. $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
  785. // Check that the initial values have been applied.
  786. $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
  787. $entities = $storage->loadMultiple();
  788. $this->assertEquals('First entity', $entities[1]->get('new_base_field')->value);
  789. $this->assertEquals('Second entity', $entities[2]->get('new_base_field')->value);
  790. }
  791. /**
  792. * Tests the error handling when using initial values from another field.
  793. */
  794. public function testInitialValueFromFieldErrorHandling() {
  795. // Check that setting invalid values for 'initial value from field' doesn't
  796. // work.
  797. try {
  798. $this->addBaseField();
  799. $storage_definition = BaseFieldDefinition::create('string')
  800. ->setLabel(t('A new base field'))
  801. ->setInitialValueFromField('field_that_does_not_exist');
  802. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  803. $this->fail('Using a non-existent field as initial value does not work.');
  804. }
  805. catch (FieldException $e) {
  806. $this->assertEquals('Illegal initial value definition on new_base_field: The field field_that_does_not_exist does not exist.', $e->getMessage());
  807. $this->pass('Using a non-existent field as initial value does not work.');
  808. }
  809. try {
  810. $this->addBaseField();
  811. $storage_definition = BaseFieldDefinition::create('integer')
  812. ->setLabel(t('A new base field'))
  813. ->setInitialValueFromField('name');
  814. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
  815. $this->fail('Using a field of a different type as initial value does not work.');
  816. }
  817. catch (FieldException $e) {
  818. $this->assertEquals('Illegal initial value definition on new_base_field: The field types do not match.', $e->getMessage());
  819. $this->pass('Using a field of a different type as initial value does not work.');
  820. }
  821. try {
  822. // Add a base field that will not be stored in the shared tables.
  823. $initial_field = BaseFieldDefinition::create('string')
  824. ->setName('initial_field')
  825. ->setLabel(t('An initial field'))
  826. ->setCardinality(2);
  827. $this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field]);
  828. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('initial_field', 'entity_test_update', 'entity_test', $initial_field);
  829. // Now add the base field which will try to use the previously added field
  830. // as the source of its initial values.
  831. $new_base_field = BaseFieldDefinition::create('string')
  832. ->setName('new_base_field')
  833. ->setLabel(t('A new base field'))
  834. ->setInitialValueFromField('initial_field');
  835. $this->state->set('entity_test_update.additional_base_field_definitions', ['initial_field' => $initial_field, 'new_base_field' => $new_base_field]);
  836. $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $new_base_field);
  837. $this->fail('Using a field that is not stored in the shared tables as initial value does not work.');
  838. }
  839. catch (FieldException $e) {
  840. $this->assertEquals('Illegal initial value definition on new_base_field: Both fields have to be stored in the shared entity tables.', $e->getMessage());
  841. $this->pass('Using a field that is not stored in the shared tables as initial value does not work.');
  842. }
  843. }
  844. }