From bea23d7ce262917ecdafb31a465c59e3e65a484f Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Thu, 13 Sep 2018 07:27:17 +1000
Subject: [PATCH] Issue #2949964 by Sam152, longwave, Berdir, amateescu: Add an
 EntityOwnerTrait to standardize the base field needed by EntityOwnerInterface

---
 core/modules/comment/comment.install          | 13 +++
 core/modules/comment/src/Entity/Comment.php   | 43 +++------
 .../Functional/Hal/CommentHalJsonTestBase.php |  2 +-
 .../Rest/CommentResourceTestBase.php          |  2 +-
 .../Functional/Update/CommentUpdateTest.php   | 18 ++++
 .../content_moderation.install                | 13 +++
 .../src/Entity/ContentModerationState.php     | 45 +++-------
 .../Update/ContentModerationUpdateTest.php    | 43 +++++++++
 .../src/Kernel/ContentModerationStateTest.php | 10 +++
 core/modules/file/file.install                | 13 +++
 core/modules/file/src/Entity/File.php         | 50 +++--------
 core/modules/file/src/FileStorageSchema.php   |  5 ++
 .../src/Functional/Update/FileUpdateTest.php  | 18 ++++
 core/modules/media/media.install              | 13 +++
 core/modules/media/src/Entity/Media.php       | 42 ++-------
 .../src/Functional/Update/MediaUpdateTest.php | 18 ++++
 .../media/tests/src/Kernel/MediaTest.php      | 10 +++
 core/modules/node/node.install                | 13 +++
 core/modules/node/src/Entity/Node.php         | 45 +++-------
 .../src/Functional/Update/NodeUpdateTest.php  | 18 ++++
 .../src/Kernel/NodeFieldOverridesTest.php     |  2 +-
 .../node/tests/src/Kernel/NodeOwnerTest.php   | 33 +++++++
 core/modules/user/src/EntityOwnerTrait.php    | 90 +++++++++++++++++++
 23 files changed, 387 insertions(+), 172 deletions(-)
 create mode 100644 core/modules/content_moderation/tests/src/Functional/Update/ContentModerationUpdateTest.php
 create mode 100644 core/modules/user/src/EntityOwnerTrait.php

diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 82b578b48956..51aaec9996dd 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -207,3 +207,16 @@ function comment_update_8600() {
   $field_storage_definition->setDefaultValueCallback(Comment::class . '::getDefaultHostname');
   $entity_definition_update_manager->updateFieldStorageDefinition($field_storage_definition);
 }
+
+/**
+ * Set the 'owner' entity key and update the field.
+ */
+function comment_update_8601() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $entity_type = $definition_update_manager->getEntityType('comment');
+  $keys = $entity_type->getKeys();
+  $keys['owner'] = 'uid';
+  $entity_type->set('entity_keys', $keys);
+  $definition_update_manager->updateEntityType($entity_type);
+  $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'comment'));
+}
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 98562717614c..58e4229b1a4e 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -13,7 +13,7 @@
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\user\Entity\User;
-use Drupal\user\UserInterface;
+use Drupal\user\EntityOwnerTrait;
 
 /**
  * Defines the comment entity class.
@@ -52,6 +52,7 @@
  *     "langcode" = "langcode",
  *     "uuid" = "uuid",
  *     "published" = "status",
+ *     "owner" = "uid",
  *   },
  *   links = {
  *     "canonical" = "/comment/{comment}",
@@ -70,6 +71,7 @@
 class Comment extends ContentEntityBase implements CommentInterface {
 
   use EntityChangedTrait;
+  use EntityOwnerTrait;
   use EntityPublishedTrait;
 
   /**
@@ -221,6 +223,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
     $fields = parent::baseFieldDefinitions($entity_type);
     $fields += static::publishedBaseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     $fields['cid']->setLabel(t('Comment ID'))
       ->setDescription(t('The comment ID.'));
@@ -256,12 +259,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ])
       ->setDisplayConfigurable('form', TRUE);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
-      ->setLabel(t('User ID'))
-      ->setDescription(t('The user ID of the comment author.'))
-      ->setTranslatable(TRUE)
-      ->setSetting('target_type', 'user')
-      ->setDefaultValue(0);
+    $fields['uid']
+      ->setDescription(t('The user ID of the comment author.'));
 
     $fields['name'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Name'))
@@ -320,6 +319,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     return $fields;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function getDefaultEntityOwner() {
+    return 0;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -524,29 +530,6 @@ public function getOwner() {
     return $user;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->get('uid')->target_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->set('uid', $account->id());
-    return $this;
-  }
-
   /**
    * Get the comment type ID for this comment.
    *
diff --git a/core/modules/comment/tests/src/Functional/Hal/CommentHalJsonTestBase.php b/core/modules/comment/tests/src/Functional/Hal/CommentHalJsonTestBase.php
index a9cf1c24256f..ca19d31cf527 100644
--- a/core/modules/comment/tests/src/Functional/Hal/CommentHalJsonTestBase.php
+++ b/core/modules/comment/tests/src/Functional/Hal/CommentHalJsonTestBase.php
@@ -42,8 +42,8 @@ abstract class CommentHalJsonTestBase extends CommentResourceTestBase {
     'thread' => NULL,
     'entity_type' => NULL,
     'field_name' => NULL,
-    'entity_id' => NULL,
     'uid' => "The 'administer comments' permission is required.",
+    'entity_id' => NULL,
   ];
 
   /**
diff --git a/core/modules/comment/tests/src/Functional/Rest/CommentResourceTestBase.php b/core/modules/comment/tests/src/Functional/Rest/CommentResourceTestBase.php
index 7d1abdbb6805..675263415105 100644
--- a/core/modules/comment/tests/src/Functional/Rest/CommentResourceTestBase.php
+++ b/core/modules/comment/tests/src/Functional/Rest/CommentResourceTestBase.php
@@ -31,9 +31,9 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
    */
   protected static $patchProtectedFieldNames = [
     'status' => "The 'administer comments' permission is required.",
+    'uid' => "The 'administer comments' permission is required.",
     'pid' => NULL,
     'entity_id' => NULL,
-    'uid' => "The 'administer comments' permission is required.",
     'name' => "The 'administer comments' permission is required.",
     'homepage' => "The 'administer comments' permission is required.",
     'created' => "The 'administer comments' permission is required.",
diff --git a/core/modules/comment/tests/src/Functional/Update/CommentUpdateTest.php b/core/modules/comment/tests/src/Functional/Update/CommentUpdateTest.php
index d68b1e540cad..45c2115f92a9 100644
--- a/core/modules/comment/tests/src/Functional/Update/CommentUpdateTest.php
+++ b/core/modules/comment/tests/src/Functional/Update/CommentUpdateTest.php
@@ -72,4 +72,22 @@ public function testPublishedEntityKey() {
     $this->assertTrue(\Drupal::database()->schema()->indexExists('comment_field_data', 'comment__status_comment_type'));
   }
 
+  /**
+   * Tests that the comment entity type has an 'owner' entity key.
+   *
+   * @see comment_update_8600()
+   */
+  public function testOwnerEntityKey() {
+    // Check that the 'owner' entity key does not exist prior to the update.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('comment');
+    $this->assertFalse($entity_type->getKey('owner'));
+
+    // Run updates.
+    $this->runUpdates();
+
+    // Check that the entity key exists and it has the correct value.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('comment');
+    $this->assertEquals('uid', $entity_type->getKey('owner'));
+  }
+
 }
diff --git a/core/modules/content_moderation/content_moderation.install b/core/modules/content_moderation/content_moderation.install
index ddeecb5624ee..e60fc59927ce 100644
--- a/core/modules/content_moderation/content_moderation.install
+++ b/core/modules/content_moderation/content_moderation.install
@@ -29,3 +29,16 @@ function content_moderation_update_8401() {
     $database_schema->dropTable('content_revision_tracker');
   }
 }
+
+/**
+ * Set the 'owner' entity key and update the field.
+ */
+function content_moderation_update_8600() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $entity_type = $definition_update_manager->getEntityType('content_moderation_state');
+  $keys = $entity_type->getKeys();
+  $keys['owner'] = 'uid';
+  $entity_type->set('entity_keys', $keys);
+  $definition_update_manager->updateEntityType($entity_type);
+  $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'content_moderation_state'));
+}
diff --git a/core/modules/content_moderation/src/Entity/ContentModerationState.php b/core/modules/content_moderation/src/Entity/ContentModerationState.php
index abb92eb8411e..48fde7b0f700 100644
--- a/core/modules/content_moderation/src/Entity/ContentModerationState.php
+++ b/core/modules/content_moderation/src/Entity/ContentModerationState.php
@@ -7,7 +7,7 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\TypedData\TranslatableInterface;
-use Drupal\user\UserInterface;
+use Drupal\user\EntityOwnerTrait;
 
 /**
  * Defines the Content moderation state entity.
@@ -37,6 +37,7 @@
  *     "revision" = "revision_id",
  *     "uuid" = "uuid",
  *     "uid" = "uid",
+ *     "owner" = "uid",
  *     "langcode" = "langcode",
  *   }
  * )
@@ -48,18 +49,18 @@
  */
 class ContentModerationState extends ContentEntityBase implements ContentModerationStateInterface {
 
+  use EntityOwnerTrait;
+
   /**
    * {@inheritdoc}
    */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
+    $fields['uid']
       ->setLabel(t('User'))
       ->setDescription(t('The username of the entity creator.'))
-      ->setSetting('target_type', 'user')
-      ->setDefaultValueCallback('Drupal\content_moderation\Entity\ContentModerationState::getCurrentUserId')
-      ->setTranslatable(TRUE)
       ->setRevisionable(TRUE);
 
     $fields['workflow'] = BaseFieldDefinition::create('entity_reference')
@@ -98,36 +99,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     return $fields;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->getEntityKey('uid');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->set('uid', $account->id());
-    return $this;
-  }
-
   /**
    * Creates or updates an entity's moderation state whilst saving that entity.
    *
@@ -185,10 +156,14 @@ public static function loadFromModeratedEntity(EntityInterface $entity) {
    *
    * @see \Drupal\content_moderation\Entity\ContentModerationState::baseFieldDefinitions()
    *
+   * @deprecated The ::getCurrentUserId method is deprecated in 8.6.x and will
+   *   be removed before 9.0.0.
+   *
    * @return array
    *   An array of default values.
    */
   public static function getCurrentUserId() {
+    @trigger_error('The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.', E_USER_DEPRECATED);
     return [\Drupal::currentUser()->id()];
   }
 
diff --git a/core/modules/content_moderation/tests/src/Functional/Update/ContentModerationUpdateTest.php b/core/modules/content_moderation/tests/src/Functional/Update/ContentModerationUpdateTest.php
new file mode 100644
index 000000000000..02b15e8703d0
--- /dev/null
+++ b/core/modules/content_moderation/tests/src/Functional/Update/ContentModerationUpdateTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\Tests\content_moderation\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests that content moderation settings are updated during database updates.
+ *
+ * @group content_moderation
+ * @group legacy
+ */
+class ContentModerationUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
+      __DIR__ . '/../../../fixtures/update/drupal-8.4.0-content_moderation_installed.php',
+    ];
+  }
+
+  /**
+   * Tests that the content moderation state entity has an 'owner' entity key.
+   *
+   * @see content_moderation_update_8600()
+   */
+  public function testOwnerEntityKey() {
+    // Check that the 'owner' entity key does not exist prior to the update.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('content_moderation_state');
+    $this->assertFalse($entity_type->getKey('owner'));
+
+    // Run updates.
+    $this->runUpdates();
+
+    // Check that the entity key exists and it has the correct value.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('content_moderation_state');
+    $this->assertEquals('uid', $entity_type->getKey('owner'));
+  }
+
+}
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
index 8574ad6a171b..c32b1ae70ad0 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
@@ -616,6 +616,16 @@ public function testRevisionDefaultState($entity_type_id) {
     $this->assertEquals($entity->getLoadedRevisionId(), $cms_entity->get('content_entity_revision_id')->value);
   }
 
+  /**
+   * Tests the legacy method used as the default entity owner.
+   *
+   * @group legacy
+   * @expectedDeprecation The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.
+   */
+  public function testGetCurrentUserId() {
+    $this->assertEquals(['0'], ContentModerationState::getCurrentUserId());
+  }
+
   /**
    * Creates an entity.
    *
diff --git a/core/modules/file/file.install b/core/modules/file/file.install
index 6274efc7c912..cca8249a8f75 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -164,3 +164,16 @@ function file_update_8001() {
     }
   }
 }
+
+/**
+ * Set the 'owner' entity key and update the field.
+ */
+function file_update_8600() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $entity_type = $definition_update_manager->getEntityType('file');
+  $keys = $entity_type->getKeys();
+  $keys['owner'] = 'uid';
+  $entity_type->set('entity_keys', $keys);
+  $definition_update_manager->updateEntityType($entity_type);
+  $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'file'));
+}
diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index 33f6d744cc2b..92aa436423ea 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -8,7 +8,7 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\file\FileInterface;
-use Drupal\user\UserInterface;
+use Drupal\user\EntityOwnerTrait;
 
 /**
  * Defines the file entity class.
@@ -36,13 +36,15 @@
  *     "id" = "fid",
  *     "label" = "filename",
  *     "langcode" = "langcode",
- *     "uuid" = "uuid"
+ *     "uuid" = "uuid",
+ *     "owner" = "uid",
  *   }
  * )
  */
 class File extends ContentEntityBase implements FileInterface {
 
   use EntityChangedTrait;
+  use EntityOwnerTrait;
 
   /**
    * {@inheritdoc}
@@ -116,36 +118,6 @@ public function getCreatedTime() {
     return $this->get('created')->value;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->get('uid')->target_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->set('uid', $account->id());
-    return $this;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -232,6 +204,7 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     $fields['fid']->setLabel(t('File ID'))
       ->setDescription(t('The file ID.'));
@@ -241,10 +214,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['langcode']->setLabel(t('Language code'))
       ->setDescription(t('The file language code.'));
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
-      ->setLabel(t('User ID'))
-      ->setDescription(t('The user ID of the file.'))
-      ->setSetting('target_type', 'user');
+    $fields['uid']
+      ->setDescription(t('The user ID of the file.'));
 
     $fields['filename'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Filename'))
@@ -284,4 +255,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     return $fields;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function getDefaultEntityOwner() {
+    return NULL;
+  }
+
 }
diff --git a/core/modules/file/src/FileStorageSchema.php b/core/modules/file/src/FileStorageSchema.php
index 17e909ab2220..f03d45202956 100644
--- a/core/modules/file/src/FileStorageSchema.php
+++ b/core/modules/file/src/FileStorageSchema.php
@@ -26,6 +26,11 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st
           break;
       }
     }
+    // Entity keys automatically have not null assigned to TRUE, but for the
+    // file entity, NULL is a valid value for uid.
+    if ($field_name === 'uid') {
+      $schema['fields']['uid']['not null'] = FALSE;
+    }
 
     return $schema;
   }
diff --git a/core/modules/file/tests/src/Functional/Update/FileUpdateTest.php b/core/modules/file/tests/src/Functional/Update/FileUpdateTest.php
index 5a35d63693f6..974ec63da66e 100644
--- a/core/modules/file/tests/src/Functional/Update/FileUpdateTest.php
+++ b/core/modules/file/tests/src/Functional/Update/FileUpdateTest.php
@@ -56,4 +56,22 @@ public function testPostUpdate8001() {
     $this->assertEqual($formatter_settings, ['use_description_as_link_text' => FALSE]);
   }
 
+  /**
+   * Tests that the file entity type has an 'owner' entity key.
+   *
+   * @see file_update_8600()
+   */
+  public function testOwnerEntityKey() {
+    // Check that the 'owner' entity key does not exist prior to the update.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('file');
+    $this->assertFalse($entity_type->getKey('owner'));
+
+    // Run updates.
+    $this->runUpdates();
+
+    // Check that the entity key exists and it has the correct value.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('file');
+    $this->assertEqual('uid', $entity_type->getKey('owner'));
+  }
+
 }
diff --git a/core/modules/media/media.install b/core/modules/media/media.install
index d3386ea35a77..58c8677f4f32 100644
--- a/core/modules/media/media.install
+++ b/core/modules/media/media.install
@@ -163,3 +163,16 @@ function media_update_8600() {
     ->set('oembed_providers_url', 'https://oembed.com/providers.json')
     ->save(TRUE);
 }
+
+/**
+ * Set the 'owner' entity key and update the field.
+ */
+function media_update_8601() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $entity_type = $definition_update_manager->getEntityType('media');
+  $keys = $entity_type->getKeys();
+  $keys['owner'] = 'uid';
+  $entity_type->set('entity_keys', $keys);
+  $definition_update_manager->updateEntityType($entity_type);
+  $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'media'));
+}
diff --git a/core/modules/media/src/Entity/Media.php b/core/modules/media/src/Entity/Media.php
index 1bbaf0881d22..ab1f070a1a92 100644
--- a/core/modules/media/src/Entity/Media.php
+++ b/core/modules/media/src/Entity/Media.php
@@ -10,7 +10,7 @@
 use Drupal\media\MediaInterface;
 use Drupal\media\MediaSourceEntityConstraintsInterface;
 use Drupal\media\MediaSourceFieldConstraintsInterface;
-use Drupal\user\UserInterface;
+use Drupal\user\EntityOwnerTrait;
 
 /**
  * Defines the media entity class.
@@ -60,6 +60,7 @@
  *     "langcode" = "langcode",
  *     "uuid" = "uuid",
  *     "published" = "status",
+ *     "owner" = "uid",
  *   },
  *   revision_metadata_keys = {
  *     "revision_user" = "revision_user",
@@ -85,6 +86,7 @@
  */
 class Media extends EditorialContentEntityBase implements MediaInterface {
 
+  use EntityOwnerTrait;
   use StringTranslationTrait;
 
   /**
@@ -130,34 +132,6 @@ public function setCreatedTime($timestamp) {
     return $this->set('created', $timestamp);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    return $this->set('uid', $account->id());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->get('uid')->target_id;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    return $this->set('uid', $uid);
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -453,6 +427,7 @@ public function validate() {
    */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     $fields['name'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Name'))
@@ -484,13 +459,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDisplayConfigurable('view', TRUE)
       ->setReadOnly(TRUE);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
+    $fields['uid']
       ->setLabel(t('Authored by'))
       ->setDescription(t('The user ID of the author.'))
       ->setRevisionable(TRUE)
-      ->setDefaultValueCallback(static::class . '::getCurrentUserId')
-      ->setSetting('target_type', 'user')
-      ->setTranslatable(TRUE)
       ->setDisplayOptions('form', [
         'type' => 'entity_reference_autocomplete',
         'weight' => 5,
@@ -551,10 +523,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    *
    * @see ::baseFieldDefinitions()
    *
+   * @deprecated The ::getCurrentUserId method is deprecated in 8.6.x and will
+   *   be removed before 9.0.0.
+   *
    * @return int[]
    *   An array of default values.
    */
   public static function getCurrentUserId() {
+    @trigger_error('The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.', E_USER_DEPRECATED);
     return [\Drupal::currentUser()->id()];
   }
 
diff --git a/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
index 4d4c3ea76f45..655ff658502b 100644
--- a/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
+++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
@@ -62,4 +62,22 @@ public function testOEmbedConfig() {
     $this->assertSame('', $config->get('iframe_domain'));
   }
 
+  /**
+   * Tests that the media entity type has an 'owner' entity key.
+   *
+   * @see media_update_8600()
+   */
+  public function testOwnerEntityKey() {
+    // Check that the 'owner' entity key does not exist prior to the update.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('media');
+    $this->assertFalse($entity_type->getKey('owner'));
+
+    // Run updates.
+    $this->runUpdates();
+
+    // Check that the entity key exists and it has the correct value.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('media');
+    $this->assertEquals('uid', $entity_type->getKey('owner'));
+  }
+
 }
diff --git a/core/modules/media/tests/src/Kernel/MediaTest.php b/core/modules/media/tests/src/Kernel/MediaTest.php
index 9c7ca28eef9a..4997bb67fa25 100644
--- a/core/modules/media/tests/src/Kernel/MediaTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaTest.php
@@ -34,4 +34,14 @@ public function testNameBaseField() {
     $this->assertSame($field_definitions['name']->getDisplayOptions('view'), ['region' => 'hidden']);
   }
 
+  /**
+   * Tests the legacy method used as the default entity owner.
+   *
+   * @group legacy
+   * @expectedDeprecation The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.
+   */
+  public function testGetCurrentUserId() {
+    $this->assertEquals(['1'], Media::getCurrentUserId());
+  }
+
 }
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index 9b0cda8e5677..2463b21646ff 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -267,3 +267,16 @@ function node_update_8401() {
     node_access_needs_rebuild(TRUE);
   }
 }
+
+/**
+ * Set the 'owner' entity key and update the field.
+ */
+function node_update_8600() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $entity_type = $definition_update_manager->getEntityType('node');
+  $keys = $entity_type->getKeys();
+  $keys['owner'] = 'uid';
+  $entity_type->set('entity_keys', $keys);
+  $definition_update_manager->updateEntityType($entity_type);
+  $definition_update_manager->updateFieldStorageDefinition($definition_update_manager->getFieldStorageDefinition('uid', 'node'));
+}
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 827c459f724e..b3d19d4e89ee 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -8,7 +8,7 @@
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\node\NodeInterface;
-use Drupal\user\UserInterface;
+use Drupal\user\EntityOwnerTrait;
 
 /**
  * Defines the node entity class.
@@ -59,6 +59,7 @@
  *     "status" = "status",
  *     "published" = "status",
  *     "uid" = "uid",
+ *     "owner" = "uid",
  *   },
  *   revision_metadata_keys = {
  *     "revision_user" = "revision_uid",
@@ -82,6 +83,8 @@
  */
 class Node extends EditorialContentEntityBase implements NodeInterface {
 
+  use EntityOwnerTrait;
+
   /**
    * Whether the node is being previewed or not.
    *
@@ -250,36 +253,6 @@ public function setSticky($sticky) {
     return $this;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwner() {
-    return $this->get('uid')->entity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getOwnerId() {
-    return $this->getEntityKey('uid');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwnerId($uid) {
-    $this->set('uid', $uid);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOwner(UserInterface $account) {
-    $this->set('uid', $account->id());
-    return $this;
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -300,6 +273,7 @@ public function setRevisionAuthorId($uid) {
    */
   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields = parent::baseFieldDefinitions($entity_type);
+    $fields += static::ownerBaseFieldDefinitions($entity_type);
 
     $fields['title'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Title'))
@@ -318,13 +292,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ])
       ->setDisplayConfigurable('form', TRUE);
 
-    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
+    $fields['uid']
       ->setLabel(t('Authored by'))
       ->setDescription(t('The username of the content author.'))
       ->setRevisionable(TRUE)
-      ->setSetting('target_type', 'user')
-      ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
-      ->setTranslatable(TRUE)
       ->setDisplayOptions('view', [
         'label' => 'hidden',
         'type' => 'author',
@@ -409,10 +380,14 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    *
    * @see ::baseFieldDefinitions()
    *
+   * @deprecated The ::getCurrentUserId method is deprecated in 8.6.x and will
+   *   be removed before 9.0.0.
+   *
    * @return array
    *   An array of default values.
    */
   public static function getCurrentUserId() {
+    @trigger_error('The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.', E_USER_DEPRECATED);
     return [\Drupal::currentUser()->id()];
   }
 
diff --git a/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php b/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
index 8f53f9b62806..180215b8fb24 100644
--- a/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
+++ b/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
@@ -65,4 +65,22 @@ public function testStatusCheckbox() {
     }
   }
 
+  /**
+   * Tests that the node entity type has an 'owner' entity key.
+   *
+   * @see node_update_8600()
+   */
+  public function testOwnerEntityKey() {
+    // Check that the 'owner' entity key does not exist prior to the update.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('node');
+    $this->assertFalse($entity_type->getKey('owner'));
+
+    // Run updates.
+    $this->runUpdates();
+
+    // Check that the entity key exists and it has the correct value.
+    $entity_type = \Drupal::entityDefinitionUpdateManager()->getEntityType('node');
+    $this->assertEquals('uid', $entity_type->getKey('owner'));
+  }
+
 }
diff --git a/core/modules/node/tests/src/Kernel/NodeFieldOverridesTest.php b/core/modules/node/tests/src/Kernel/NodeFieldOverridesTest.php
index 7c7bc0334884..1d781c6f5602 100644
--- a/core/modules/node/tests/src/Kernel/NodeFieldOverridesTest.php
+++ b/core/modules/node/tests/src/Kernel/NodeFieldOverridesTest.php
@@ -53,7 +53,7 @@ public function testFieldOverrides() {
     $uid_field = \Drupal::entityManager()->getBaseFieldDefinitions('node')['uid'];
     $config = $uid_field->getConfig('ponies');
     $config->save();
-    $this->assertEqual($config->get('default_value_callback'), 'Drupal\node\Entity\Node::getCurrentUserId');
+    $this->assertEquals($config->get('default_value_callback'), 'Drupal\node\Entity\Node::getDefaultEntityOwner');
     /** @var \Drupal\node\NodeInterface $node */
     $node = Node::create(['type' => 'ponies']);
     $owner = $node->getOwner();
diff --git a/core/modules/node/tests/src/Kernel/NodeOwnerTest.php b/core/modules/node/tests/src/Kernel/NodeOwnerTest.php
index c12f3115c160..7d54d98012d0 100644
--- a/core/modules/node/tests/src/Kernel/NodeOwnerTest.php
+++ b/core/modules/node/tests/src/Kernel/NodeOwnerTest.php
@@ -6,6 +6,7 @@
 use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
+use Drupal\user\Entity\User;
 
 /**
  * Tests node owner functionality.
@@ -75,4 +76,36 @@ public function testOwner() {
     $this->assertEqual(0, $italian->getOwnerId());
   }
 
+  /**
+   * Test an unsaved node owner.
+   */
+  public function testUnsavedNodeOwner() {
+    $user = User::create([
+      'name' => 'foo',
+    ]);
+    $node = Node::create([
+      'type' => 'page',
+      'title' => $this->randomMachineName(),
+    ]);
+    // Set the node owner while the user is unsaved and then immediately save
+    // the user and node.
+    $node->setOwner($user);
+    $user->save();
+    $node->save();
+
+    // The ID assigned to the newly saved user will now be the owner ID of the
+    // node.
+    $this->assertEquals($user->id(), $node->getOwnerId());
+  }
+
+  /**
+   * Tests the legacy method used as the default entity owner.
+   *
+   * @group legacy
+   * @expectedDeprecation The ::getCurrentUserId method is deprecated in 8.6.x and will be removed before 9.0.0.
+   */
+  public function testGetCurrentUserId() {
+    $this->assertEquals(['0'], Node::getCurrentUserId());
+  }
+
 }
diff --git a/core/modules/user/src/EntityOwnerTrait.php b/core/modules/user/src/EntityOwnerTrait.php
new file mode 100644
index 000000000000..ad55d0f3dc37
--- /dev/null
+++ b/core/modules/user/src/EntityOwnerTrait.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\user;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Provides a trait for entities that have an owner.
+ */
+trait EntityOwnerTrait {
+
+  /**
+   * Returns an array of base field definitions for entity owners.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type to add the owner field to.
+   *
+   * @return \Drupal\Core\Field\BaseFieldDefinition[]
+   *   An array of base field definitions.
+   *
+   * @throws \Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException
+   *   Thrown when the entity type does not implement EntityOwnerInterface or
+   *   if it does not have an "owner" entity key.
+   */
+  public static function ownerBaseFieldDefinitions(EntityTypeInterface $entity_type) {
+    if (!is_subclass_of($entity_type->getClass(), EntityOwnerInterface::class)) {
+      throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not implement \Drupal\user\EntityOwnerInterface.');
+    }
+    if (!$entity_type->hasKey('owner')) {
+      throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not have an "owner" entity key.');
+    }
+
+    return [
+      $entity_type->getKey('owner') => BaseFieldDefinition::create('entity_reference')
+        ->setLabel(new TranslatableMarkup('User ID'))
+        ->setSetting('target_type', 'user')
+        ->setTranslatable($entity_type->isTranslatable())
+        ->setDefaultValueCallback(static::class . '::getDefaultEntityOwner'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwnerId() {
+    return $this->getEntityKey('owner');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwnerId($uid) {
+    $key = $this->getEntityType()->getKey('owner');
+    $this->set($key, $uid);
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOwner() {
+    $key = $this->getEntityType()->getKey('owner');
+    return $this->get($key)->entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOwner(UserInterface $account) {
+    $key = $this->getEntityType()->getKey('owner');
+    $this->set($key, $account);
+
+    return $this;
+  }
+
+  /**
+   * Default value callback for 'owner' base field.
+   *
+   * @return mixed
+   *   A default value for the owner field.
+   */
+  public static function getDefaultEntityOwner() {
+    return \Drupal::currentUser()->id();
+  }
+
+}
-- 
GitLab