diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 2915609..5df66a0 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -1198,22 +1198,26 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields[$entity_type->getKey('id')] = BaseFieldDefinition::create('integer') ->setLabel(new TranslatableMarkup('ID')) ->setReadOnly(TRUE) + ->setStorageRequired(TRUE) ->setSetting('unsigned', TRUE); } if ($entity_type->hasKey('uuid')) { $fields[$entity_type->getKey('uuid')] = BaseFieldDefinition::create('uuid') ->setLabel(new TranslatableMarkup('UUID')) - ->setReadOnly(TRUE); + ->setReadOnly(TRUE) + ->setStorageRequired(TRUE); } if ($entity_type->hasKey('revision')) { $fields[$entity_type->getKey('revision')] = BaseFieldDefinition::create('integer') ->setLabel(new TranslatableMarkup('Revision ID')) ->setReadOnly(TRUE) + ->setStorageRequired(TRUE) ->setSetting('unsigned', TRUE); } if ($entity_type->hasKey('langcode')) { $fields[$entity_type->getKey('langcode')] = BaseFieldDefinition::create('language') ->setLabel(new TranslatableMarkup('Language')) + ->setStorageRequired(TRUE) ->setDisplayOptions('view', [ 'region' => 'hidden', ]) diff --git a/core/lib/Drupal/Core/Entity/EntityFieldManager.php b/core/lib/Drupal/Core/Entity/EntityFieldManager.php index 378a95c..c8293fa 100644 --- a/core/lib/Drupal/Core/Entity/EntityFieldManager.php +++ b/core/lib/Drupal/Core/Entity/EntityFieldManager.php @@ -216,7 +216,8 @@ protected function buildBaseFieldDefinitions($entity_type_id) { ->setDescription($this->t('A flag indicating whether this is the default translation.')) ->setTranslatable(TRUE) ->setRevisionable(TRUE) - ->setDefaultValue(TRUE); + ->setDefaultValue(TRUE) + ->setRequired(TRUE); } } diff --git a/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php index 871aceb..7b788ec 100644 --- a/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php @@ -37,6 +37,7 @@ public static function publishedBaseFieldDefinitions(EntityTypeInterface $entity ->setDescription(new TranslatableMarkup('A boolean indicating the published state.')) ->setRevisionable(TRUE) ->setTranslatable(TRUE) + ->setRequired(TRUE) ->setDefaultValue(TRUE)]; } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index 807a169..d01ccda 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -1044,11 +1044,7 @@ protected function processRevisionTable(ContentEntityTypeInterface $entity_type, * @return array * A partial schema array for the base table. */ - protected function processDataTable(ContentEntityTypeInterface $entity_type, array &$schema) { - // Marking the respective fields as NOT NULL makes the indexes more - // performant. - $schema['fields'][$entity_type->getKey('default_langcode')]['not null'] = TRUE; - } + protected function processDataTable(ContentEntityTypeInterface $entity_type, array &$schema) { } /** * Processes the gathered schema for a base table. @@ -1061,11 +1057,7 @@ protected function processDataTable(ContentEntityTypeInterface $entity_type, arr * @return array * A partial schema array for the base table. */ - protected function processRevisionDataTable(ContentEntityTypeInterface $entity_type, array &$schema) { - // Marking the respective fields as NOT NULL makes the indexes more - // performant. - $schema['fields'][$entity_type->getKey('default_langcode')]['not null'] = TRUE; - } + protected function processRevisionDataTable(ContentEntityTypeInterface $entity_type, array &$schema) { } /** * Processes the specified entity key. @@ -1612,35 +1604,29 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st } $field_name = $storage_definition->getName(); - $base_table = $this->storage->getBaseTable(); + $properties = $storage_definition->getPropertyDefinitions(); // A shared table contains rows for entities where the field is empty // (since other fields stored in the same table might not be empty), thus // the only columns that can be 'not null' are those for required - // properties of required fields. However, even those would break in the - // case where a new field is added to a table that contains existing rows. - // For now, we only hardcode 'not null' to a couple "entity keys", in order - // to keep their indexes optimized. - // @todo Revisit once we have support for 'initial' in - // https://www.drupal.org/node/2346019. - $not_null_keys = $this->entityType->getKeys(); - // Label fields are not necessarily required. - unset($not_null_keys['label']); + // properties of required fields. + $field_storage_is_required = $storage_definition->isStorageRequired(); + // Because entity ID and revision ID are both serial fields in the base and // revision table respectively, the revision ID is not known yet, when // inserting data into the base table. Instead the revision ID in the base // table is updated after the data has been inserted into the revision // table. For this reason the revision ID field cannot be marked as NOT // NULL. - if ($table_name == $base_table) { - unset($not_null_keys['revision']); + if ($table_name == $this->storage->getBaseTable() && $field_name == $this->entityType->getKey('revision')) { + $field_storage_is_required = FALSE; } foreach ($column_mapping as $field_column_name => $schema_field_name) { $column_schema = $field_schema['columns'][$field_column_name]; $schema['fields'][$schema_field_name] = $column_schema; - $schema['fields'][$schema_field_name]['not null'] = in_array($field_name, $not_null_keys); + $schema['fields'][$schema_field_name]['not null'] = $field_storage_is_required && $properties[$field_column_name]->isRequired(); } if (!empty($field_schema['indexes'])) { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php index 0b9bba9..e5f3f08 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/LanguageItem.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Language\LanguageInterface; @@ -117,4 +118,12 @@ public function onChange($property_name, $notify = TRUE) { parent::onChange($property_name, $notify); } + /** + * {@inheritdoc} + */ + public static function generateSampleValue(FieldDefinitionInterface $field_definition) { + $values['value'] = LanguageInterface::LANGCODE_NOT_SPECIFIED; + return $values; + } + } diff --git a/core/modules/aggregator/src/Tests/FeedParserTest.php b/core/modules/aggregator/src/Tests/FeedParserTest.php index 6c4a04d..128ea83 100644 --- a/core/modules/aggregator/src/Tests/FeedParserTest.php +++ b/core/modules/aggregator/src/Tests/FeedParserTest.php @@ -85,7 +85,7 @@ public function testHtmlEntitiesSample() { */ public function testRedirectFeed() { $redirect_url = Url::fromRoute('aggregator_test.redirect')->setAbsolute()->toString(); - $feed = Feed::create(array('url' => $redirect_url, 'title' => $this->randomMachineName())); + $feed = Feed::create(array('url' => $redirect_url, 'title' => $this->randomMachineName(), 'refresh' => 900)); $feed->save(); $feed->refreshItems(); @@ -99,7 +99,7 @@ public function testRedirectFeed() { public function testInvalidFeed() { // Simulate a typo in the URL to force a curl exception. $invalid_url = 'http:/www.drupal.org'; - $feed = Feed::create(array('url' => $invalid_url, 'title' => $this->randomMachineName())); + $feed = Feed::create(array('url' => $invalid_url, 'title' => $this->randomMachineName(), 'refresh' => 900)); $feed->save(); // Update the feed. Use the UI to be able to check the message easily. diff --git a/core/modules/aggregator/tests/src/Kernel/AggregatorTitleTest.php b/core/modules/aggregator/tests/src/Kernel/AggregatorTitleTest.php index 782ac6f..4c2891c 100644 --- a/core/modules/aggregator/tests/src/Kernel/AggregatorTitleTest.php +++ b/core/modules/aggregator/tests/src/Kernel/AggregatorTitleTest.php @@ -51,6 +51,7 @@ public function testStringFormatter() { $aggregator_feed = Feed::create([ 'title' => 'testing title', 'url' => 'http://www.example.com', + 'refresh' => 900, ]); $aggregator_feed->save(); diff --git a/core/modules/aggregator/tests/src/Kernel/Views/AggregatorFeedViewsFieldAccessTest.php b/core/modules/aggregator/tests/src/Kernel/Views/AggregatorFeedViewsFieldAccessTest.php index d0ca46e..9fa4496 100644 --- a/core/modules/aggregator/tests/src/Kernel/Views/AggregatorFeedViewsFieldAccessTest.php +++ b/core/modules/aggregator/tests/src/Kernel/Views/AggregatorFeedViewsFieldAccessTest.php @@ -34,6 +34,7 @@ public function testAggregatorFeedFields() { 'title' => 'Drupal org', 'url' => 'https://www.drupal.org/rss.xml', 'link' => 'https://www.drupal.org/rss.xml', + 'refresh' => 900, ]); $feed->save(); diff --git a/core/modules/aggregator/tests/src/Kernel/Views/AggregatorItemViewsFieldAccessTest.php b/core/modules/aggregator/tests/src/Kernel/Views/AggregatorItemViewsFieldAccessTest.php index a095d3c..e007d8e 100644 --- a/core/modules/aggregator/tests/src/Kernel/Views/AggregatorItemViewsFieldAccessTest.php +++ b/core/modules/aggregator/tests/src/Kernel/Views/AggregatorItemViewsFieldAccessTest.php @@ -35,6 +35,7 @@ public function testAggregatorItemFields() { $feed = Feed::create([ 'title' => 'Drupal org', 'url' => 'https://www.drupal.org/rss.xml', + 'refresh' => 900, ]); $feed->save(); $item = Item::create([ diff --git a/core/modules/comment/tests/src/Kernel/Views/CommentLinksTest.php b/core/modules/comment/tests/src/Kernel/Views/CommentLinksTest.php index ffb91b0..08a326d 100644 --- a/core/modules/comment/tests/src/Kernel/Views/CommentLinksTest.php +++ b/core/modules/comment/tests/src/Kernel/Views/CommentLinksTest.php @@ -19,6 +19,13 @@ class CommentLinksTest extends CommentViewsKernelTestBase { /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['entity_test']; + + /** * Views used by this test. * * @var array @@ -26,14 +33,26 @@ class CommentLinksTest extends CommentViewsKernelTestBase { public static $testViews = ['test_comment']; /** + * {@inheritdoc} + */ + protected function setUp($import_test_views = TRUE) { + parent::setUp($import_test_views); + + $this->installEntitySchema('entity_test'); + } + + /** * Test the comment approve link. */ public function testLinkApprove() { + $host = EntityTest::create(['name' => $this->randomString()]); + $host->save(); // Create an unapproved comment. $comment = $this->commentStorage->create([ 'uid' => $this->adminUser->id(), 'entity_type' => 'entity_test', + 'entity_id' => $host->id(), 'comment_type' => 'entity_test', 'status' => 0, ]); @@ -91,8 +110,7 @@ public function testLinkApprove() { * Test the comment reply link. */ public function testLinkReply() { - $this->enableModules(['field', 'entity_test']); - $this->installEntitySchema('entity_test'); + $this->enableModules(['field']); $this->installSchema('comment', ['comment_entity_statistics']); $this->installConfig(['field']); diff --git a/core/modules/comment/tests/src/Kernel/Views/CommentUserNameTest.php b/core/modules/comment/tests/src/Kernel/Views/CommentUserNameTest.php index dae2c48..92112e4 100644 --- a/core/modules/comment/tests/src/Kernel/Views/CommentUserNameTest.php +++ b/core/modules/comment/tests/src/Kernel/Views/CommentUserNameTest.php @@ -4,6 +4,7 @@ use Drupal\comment\Entity\Comment; use Drupal\Core\Session\AnonymousUserSession; +use Drupal\entity_test\Entity\EntityTest; use Drupal\Tests\views\Kernel\ViewsKernelTestBase; use Drupal\user\Entity\Role; use Drupal\user\Entity\User; @@ -36,6 +37,7 @@ protected function setUp($import_test_views = TRUE) { $this->installEntitySchema('user'); $this->installEntitySchema('comment'); + $this->installEntitySchema('entity_test'); // Create the anonymous role. $this->installConfig(['user']); @@ -67,12 +69,16 @@ protected function setUp($import_test_views = TRUE) { ]); $this->adminUser->save(); + $host = EntityTest::create(['name' => $this->randomString()]); + $host->save(); + // Create some comments. $comment = Comment::create([ 'subject' => 'My comment title', 'uid' => $this->adminUser->id(), 'name' => $this->adminUser->label(), 'entity_type' => 'entity_test', + 'entity_id' => $host->id(), 'comment_type' => 'entity_test', 'status' => 1, ]); @@ -85,6 +91,7 @@ protected function setUp($import_test_views = TRUE) { 'mail' => 'test@example.com', 'homepage' => 'https://example.com', 'entity_type' => 'entity_test', + 'entity_id' => $host->id(), 'comment_type' => 'entity_test', 'created' => 123456, 'status' => 1, diff --git a/core/modules/comment/tests/src/Kernel/Views/CommentViewsFieldAccessTest.php b/core/modules/comment/tests/src/Kernel/Views/CommentViewsFieldAccessTest.php index f807c58..1ca5e43 100644 --- a/core/modules/comment/tests/src/Kernel/Views/CommentViewsFieldAccessTest.php +++ b/core/modules/comment/tests/src/Kernel/Views/CommentViewsFieldAccessTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\comment\Kernel\Views; use Drupal\comment\Entity\Comment; +use Drupal\entity_test\Entity\EntityTest; use Drupal\user\Entity\User; use Drupal\Tests\views\Kernel\Handler\FieldFieldAccessTestBase; @@ -25,6 +26,7 @@ protected function setUp($import_test_views = TRUE) { parent::setUp($import_test_views); $this->installEntitySchema('comment'); + $this->installEntitySchema('entity_test'); } /** @@ -36,10 +38,14 @@ public function testCommentFields() { ]); $user->save(); + $host = EntityTest::create(['name' => $this->randomString()]); + $host->save(); + $comment = Comment::create([ 'subject' => 'My comment title', 'uid' => $user->id(), 'entity_type' => 'entity_test', + 'entity_id' => $host->id(), 'comment_type' => 'entity_test', ]); $comment->save(); @@ -51,6 +57,7 @@ public function testCommentFields() { 'mail' => 'test@example.com', 'homepage' => 'https://example.com', 'entity_type' => 'entity_test', + 'entity_id' => $host->id(), 'comment_type' => 'entity_test', 'created' => 123456, 'status' => 1, diff --git a/core/modules/content_moderation/src/Entity/ContentModerationState.php b/core/modules/content_moderation/src/Entity/ContentModerationState.php index 6d5b39c..baafa37 100644 --- a/core/modules/content_moderation/src/Entity/ContentModerationState.php +++ b/core/modules/content_moderation/src/Entity/ContentModerationState.php @@ -59,14 +59,16 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Workflow')) ->setDescription(t('The workflow the moderation state is in.')) ->setSetting('target_type', 'workflow') - ->setRequired(TRUE) + // @todo Make this field required in https://www.drupal.org/node/2817835. + //->setRequired(TRUE) ->setTranslatable(TRUE) ->setRevisionable(TRUE); $fields['moderation_state'] = BaseFieldDefinition::create('string') ->setLabel(t('Moderation state')) ->setDescription(t('The moderation state of the referenced content.')) - ->setRequired(TRUE) + // @todo Make this field required in https://www.drupal.org/node/2817835. + //->setRequired(TRUE) ->setTranslatable(TRUE) ->setRevisionable(TRUE); diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php index 5151155..e9e33d0 100644 --- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php @@ -90,10 +90,15 @@ public function testBasicModeration($entity_type_id) { $workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id); $workflow->save(); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); $entity = $entity_storage->create([ - 'title' => 'Test title', - $this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id, + $entity_type->getKey('label') => 'Test title', + $entity_type->getKey('bundle') => $bundle_id, ]); + // Make sure we add values for all of the required fields. + if ($entity_type_id == 'block_content') { + $entity->info = $this->randomString(); + } $entity->save(); $entity = $this->reloadEntity($entity); $this->assertEquals('draft', $entity->moderation_state->value); diff --git a/core/modules/menu_link_content/src/Tests/LinksTest.php b/core/modules/menu_link_content/src/Tests/LinksTest.php index 84fecde..8ea6519 100644 --- a/core/modules/menu_link_content/src/Tests/LinksTest.php +++ b/core/modules/menu_link_content/src/Tests/LinksTest.php @@ -125,6 +125,7 @@ public function testCreateLink() { 'menu_name' => 'menu_test', 'bundle' => 'menu_link_content', 'link' => [['uri' => 'internal:/']], + 'title' => 'Menu link test', ); $link = MenuLinkContent::create($options); $link->save(); diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php index cf802b7..ab39617 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php @@ -70,7 +70,7 @@ public function testTranslationLinkOnMenuEditForm() { $this->drupalGet('admin/structure/menu/manage/tools'); $this->assertNoLink(t('Translate')); - $menu_link_content = MenuLinkContent::create(['menu_name' => 'tools', 'link' => ['uri' => 'internal:/admin/structure/menu']]); + $menu_link_content = MenuLinkContent::create(['menu_name' => 'tools', 'link' => ['uri' => 'internal:/admin/structure/menu'], 'title' => 'Link test']); $menu_link_content->save(); $this->drupalGet('admin/structure/menu/manage/tools'); $this->assertLink(t('Translate')); diff --git a/core/modules/menu_link_content/tests/src/Kernel/MenuLinkContentCacheabilityBubblingTest.php b/core/modules/menu_link_content/tests/src/Kernel/MenuLinkContentCacheabilityBubblingTest.php index c7689d9..87d1916 100644 --- a/core/modules/menu_link_content/tests/src/Kernel/MenuLinkContentCacheabilityBubblingTest.php +++ b/core/modules/menu_link_content/tests/src/Kernel/MenuLinkContentCacheabilityBubblingTest.php @@ -109,6 +109,7 @@ public function testOutboundPathAndRouteProcessing() { $menu_link_content = MenuLinkContent::create([ 'link' => ['uri' => $expectation['uri']], 'menu_name' => 'tools', + 'title' => 'Link test', ]); $menu_link_content->save(); $tree = $menu_tree->load('tools', new MenuTreeParameters()); @@ -129,6 +130,7 @@ public function testOutboundPathAndRouteProcessing() { $menu_link_content = MenuLinkContent::create([ 'link' => ['uri' => $expectation['uri']], 'menu_name' => 'tools', + 'title' => 'Link test', ]); $menu_link_content->save(); $expected_cacheability = $expected_cacheability->merge($expectation['cacheability']); diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index 9da3de6..350370f 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -213,11 +213,10 @@ protected function processStubRow(Row $row) { if ($field_definition->isRequired() && is_null($row->getDestinationProperty($field_name))) { // Use the configured default value for this specific field, if any. if ($default_value = $field_definition->getDefaultValueLiteral()) { - $values[] = $default_value; + $values = reset($default_value); } else { // Otherwise, ask the field type to generate a sample value. - $field_type = $field_definition->getType(); /** @var \Drupal\Core\Field\FieldItemInterface $field_type_class */ $field_type_class = $this->fieldTypeManager ->getPluginClass($field_definition->getType()); diff --git a/core/modules/node/node.install b/core/modules/node/node.install index 0582459..fe5a13d 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -189,7 +189,9 @@ function node_update_8002() { // Regenerate entity type indexes, this should drop "node__default_langcode". $manager->updateEntityType($manager->getEntityType('node')); // Regenerate "langcode" indexes, this should drop "node_field__langcode". - $manager->updateFieldStorageDefinition($manager->getFieldStorageDefinition('langcode', 'node')); + $field_definition = $manager->getFieldStorageDefinition('langcode', 'node'); + $field_definition->setStorageRequired(TRUE); + $manager->updateFieldStorageDefinition($field_definition); } /** @@ -253,5 +255,18 @@ function node_update_8301() { } /** + * Update the 'uid' field in order to make it required at the storage level. + */ +function node_update_8302() { + // The SQL content entity schema no longer uses entity keys to mark their + // corresponding fields as NOT NULL in the database, so we need to update the + // 'uid' field manually. + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $field_storage_definition = $definition_update_manager->getFieldStorageDefinition('uid', 'node'); + $field_storage_definition->setStorageRequired(TRUE); + $definition_update_manager->updateFieldStorageDefinition($field_storage_definition); +} + +/** * @} End of "addtogroup updates-8.3.x". */ diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php index 0edb58f..bb100fc 100644 --- a/core/modules/node/src/Entity/Node.php +++ b/core/modules/node/src/Entity/Node.php @@ -380,6 +380,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['uid'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Authored by')) ->setDescription(t('The username of the content author.')) + ->setStorageRequired(TRUE) ->setRevisionable(TRUE) ->setSetting('target_type', 'user') ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId') diff --git a/core/modules/node/src/NodeStorageSchema.php b/core/modules/node/src/NodeStorageSchema.php index 18d6303..e5dd458 100644 --- a/core/modules/node/src/NodeStorageSchema.php +++ b/core/modules/node/src/NodeStorageSchema.php @@ -35,7 +35,7 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st if ($table_name == 'node_revision') { switch ($field_name) { case 'langcode': - $this->addSharedTableFieldIndex($storage_definition, $schema, TRUE); + $this->addSharedTableFieldIndex($storage_definition, $schema); break; case 'revision_uid': @@ -47,9 +47,7 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st if ($table_name == 'node_field_data') { switch ($field_name) { case 'promote': - case 'status': case 'sticky': - case 'title': // Improves the performance of the indexes defined // in getEntitySchema(). $schema['fields'][$field_name]['not null'] = TRUE; diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php index 0123597..87efdca 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php @@ -135,14 +135,12 @@ protected function modifyBaseField() { } /** - * Promotes a field to an entity key. + * Makes a base field required. */ - protected function makeBaseFieldEntityKey() { - $entity_type = clone $this->entityManager->getDefinition('entity_test_update'); - $entity_keys = $entity_type->getKeys(); - $entity_keys['new_base_field'] = 'new_base_field'; - $entity_type->set('entity_keys', $entity_keys); - $this->state->set('entity_test_update.entity_type', $entity_type); + protected function makeBaseFieldRequired() { + $definitions = \Drupal::state()->get('entity_test_update.additional_base_field_definitions', []); + $definitions['new_base_field']->setRequired(TRUE); + $this->state->set('entity_test_update.additional_base_field_definitions', $definitions); } /** diff --git a/core/modules/system/src/Tests/Update/EntityUpdateTest.php b/core/modules/system/src/Tests/Update/EntityUpdateTest.php index 6328f58..8a10b3f 100644 --- a/core/modules/system/src/Tests/Update/EntityUpdateTest.php +++ b/core/modules/system/src/Tests/Update/EntityUpdateTest.php @@ -21,9 +21,9 @@ protected function setDatabaseDumpFiles() { } /** - * Tests system_update_8302(). + * Tests system_update_8203(). */ - public function testSystemUpdate8302() { + public function testSystemUpdate8203() { // The installed field storage schema is wrong before running the update. $key_value_store = \Drupal::keyValue('entity.storage_schema.sql'); $id_schema = $key_value_store->get('node.field_schema_data.nid', []); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 019abd9..c6aed42 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -14,6 +14,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; +use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Site\Settings; use Drupal\Core\StreamWrapper\PrivateStream; use Drupal\Core\StreamWrapper\PublicStream; @@ -1089,6 +1090,18 @@ function system_schema() { } /** + * Implements hook_update_dependencies(). + */ +function system_update_dependencies() { + // system_update_8302() has to run after the 'status' field was made an entity + // key for the Comment entity type. + $dependencies['system'][8302] = array( + 'comment' => 8301, + ); + return $dependencies; +} + +/** * @addtogroup updates-8.0.0-beta * @{ */ @@ -1833,7 +1846,7 @@ function system_update_8301() { } /** - * Correct the last installed storage schema for all ID and revision fields. + * Correct the installed schema and NOT NULL handling for required base fields. */ function system_update_8302() { $entity_type_manager = \Drupal::entityTypeManager(); @@ -1841,16 +1854,35 @@ function system_update_8302() { /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_respository */ $entity_last_installed_schema_respository = \Drupal::service('entity.last_installed_schema.repository'); - foreach ($entity_type_manager->getDefinitions() as $entity_type_id => $entity_type) { + foreach ($entity_type_manager->getDefinitions() as $entity_type_id => $entityType) { + $original_entity_type = $entity_last_installed_schema_respository->getLastInstalledDefinition($entity_type_id); $storage = $entity_type_manager->getStorage($entity_type_id); if ($storage instanceof SqlEntityStorageInterface) { - $field_names_to_update = [$entity_type->getKey('id')]; - if ($entity_type->isRevisionable()) { - $field_names_to_update[] = $entity_type->getKey('revision'); - } foreach ($entity_last_installed_schema_respository->getLastInstalledFieldStorageDefinitions($entity_type_id) as $field_name => $field_definition) { - if (in_array($field_definition->getName(), $field_names_to_update)) { - $definition_update_manager->updateFieldStorageDefinition($field_definition); + /** @var \Drupal\Core\Field\BaseFieldDefinition $field_definition */ + if ($field_definition instanceof BaseFieldDefinition) { + $field_name = $field_definition->getName(); + + // The SQL content entity schema no longer uses entity keys to mark + // their corresponding fields as NOT NULL in the database, so we need + // to update them manually. + $entity_keys_names = [ + $original_entity_type->getKey('id'), + $original_entity_type->getKey('uuid'), + $original_entity_type->getKey('revision'), + $original_entity_type->getKey('langcode'), + ]; + if (in_array($field_name, $entity_keys_names)) { + $field_definition->setStorageRequired(TRUE); + } + + if ($field_name == $original_entity_type->getKey('published')) { + $field_definition->setRequired(TRUE); + } + + if ($field_definition->isStorageRequired()) { + $definition_update_manager->updateFieldStorageDefinition($field_definition); + } } } } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php index 3913075..a3fb47e 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php @@ -58,6 +58,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Revision ID')) ->setDescription(t('The version id of the test entity.')) ->setReadOnly(TRUE) + ->setStorageRequired(TRUE) ->setSetting('unsigned', TRUE); $fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean') diff --git a/core/modules/user/src/UserStorageSchema.php b/core/modules/user/src/UserStorageSchema.php index c8c3e46..9ebf15c 100644 --- a/core/modules/user/src/UserStorageSchema.php +++ b/core/modules/user/src/UserStorageSchema.php @@ -44,9 +44,6 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st if ($table_name == 'users_field_data') { switch ($field_name) { case 'name': - // Improves the performance of the user__name index defined - // in getEntitySchema(). - $schema['fields'][$field_name]['not null'] = TRUE; // Make sure the field is no longer than 191 characters so we can // add a unique constraint in MySQL. $schema['fields'][$field_name]['length'] = USERNAME_MAX_LENGTH; diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index e716b70..ebfe35b 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -765,9 +765,9 @@ public function testBaseFieldEntityKeyUpdateWithExistingData() { $entity = $this->entityManager->getStorage('entity_test_update')->create(); $entity->save(); - // Promote the base field to an entity key. This will trigger the addition - // of a NOT NULL constraint. - $this->makeBaseFieldEntityKey(); + // Make the base field required. This will trigger the addition of a NOT + // NULL constraint. + $this->makeBaseFieldRequired(); // Try to apply the update and verify they fail since we have a NULL value. $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.'; diff --git a/core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php b/core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php index 1efb14b..3a440da 100644 --- a/core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php +++ b/core/tests/Drupal/KernelTests/Core/Menu/MenuLinkTreeTest.php @@ -62,9 +62,9 @@ public function testDeleteLinksInMenu() { \Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu1'))->save(); \Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu2'))->save(); - \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save(); - \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save(); - \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu2', 'bundle' => 'menu_link_content'))->save(); + \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content', 'title' => 'Link test'))->save(); + \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu1', 'bundle' => 'menu_link_content', 'title' => 'Link test'))->save(); + \Drupal::entityManager()->getStorage('menu_link_content')->create(array('link' => ['uri' => 'internal:/menu_name_test'], 'menu_name' => 'menu2', 'bundle' => 'menu_link_content', 'title' => 'Link test'))->save(); $output = $this->linkTree->load('menu1', new MenuTreeParameters()); $this->assertEqual(count($output), 2); diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php index b0f2948..dd55888 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php @@ -74,6 +74,7 @@ protected function setUp() { 'columns' => array( 'value' => array( 'type' => 'int', + 'not null' => TRUE, ), ), )); @@ -500,6 +501,7 @@ public function testGetSchemaTranslatable() { 'columns' => array( 'value' => array( 'type' => 'varchar', + 'not null' => TRUE, ), ), )); @@ -509,6 +511,7 @@ public function testGetSchemaTranslatable() { 'value' => array( 'type' => 'int', 'size' => 'tiny', + 'not null' => TRUE, ), ), )); @@ -618,6 +621,7 @@ public function testGetSchemaRevisionableTranslatable() { 'columns' => array( 'value' => array( 'type' => 'int', + 'not null' => TRUE, ), ), )); @@ -625,6 +629,7 @@ public function testGetSchemaRevisionableTranslatable() { 'columns' => array( 'value' => array( 'type' => 'varchar', + 'not null' => TRUE, ), ), )); @@ -633,6 +638,7 @@ public function testGetSchemaRevisionableTranslatable() { 'value' => array( 'type' => 'int', 'size' => 'tiny', + 'not null' => TRUE, ), ), )); @@ -1403,6 +1409,9 @@ public function setUpStorageDefinition($field_name, array $schema) { $this->storageDefinitions[$field_name]->expects($this->any()) ->method('getColumns') ->will($this->returnValue($schema['columns'])); + $this->storageDefinitions[$field_name]->expects($this->any()) + ->method('isStorageRequired') + ->will($this->returnValue(TRUE)); // Add property definitions. if (!empty($schema['columns'])) { $property_definitions = array(); diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 5d56929..b287f75 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -298,9 +298,6 @@ public function testOnEntityTypeCreate() { ->method('getSchema') ->will($this->returnValue(array('columns' => $columns))); - $this->entityType->expects($this->once()) - ->method('getKeys') - ->will($this->returnValue(array('id' => 'id'))); $this->entityType->expects($this->any()) ->method('getKey') ->will($this->returnValueMap(array( diff --git a/core/tests/Drupal/Tests/Core/Field/TestBaseFieldDefinitionInterface.php b/core/tests/Drupal/Tests/Core/Field/TestBaseFieldDefinitionInterface.php index 953c902..afd19f7 100644 --- a/core/tests/Drupal/Tests/Core/Field/TestBaseFieldDefinitionInterface.php +++ b/core/tests/Drupal/Tests/Core/Field/TestBaseFieldDefinitionInterface.php @@ -4,9 +4,10 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\Field\RequiredFieldStorageDefinitionInterface; /** * Defines a test interface to mock entity base field definitions. */ -interface TestBaseFieldDefinitionInterface extends FieldDefinitionInterface, FieldStorageDefinitionInterface { +interface TestBaseFieldDefinitionInterface extends FieldDefinitionInterface, FieldStorageDefinitionInterface, RequiredFieldStorageDefinitionInterface { }