From 5e5738dcdddc3052e80504d45db657678cdaf6fa Mon Sep 17 00:00:00 2001 From: webchick <drupal@webchick.net> Date: Mon, 19 Oct 2015 10:44:31 -0700 Subject: [PATCH] Issue #2560637 by phenaproxima, benjy, quietone, mikeryan, chx, neclimdul: Improve handling of uid 1 during migration --- .../Tests/Migrate/d7/MigrateCommentTest.php | 7 -- .../schema/migrate.data_types.schema.yml | 7 ++ .../migrate/destination/EntityContentBase.php | 11 +++ core/modules/migrate/src/Row.php | 9 ++ .../src/Tests/d6/EntityContentBaseTest.php | 93 +++++++++++++++++++ .../migrate_drupal/tests/fixtures/drupal7.php | 4 +- .../migrate_overwrite_test.info.yml | 6 ++ .../migration_templates/users.yml | 31 +++++++ .../src/Tests/Migrate/d7/MigrateNodeTest.php | 2 +- .../src/Plugin/migrate/source/d6/User.php | 2 +- .../src/Plugin/migrate/source/d7/User.php | 2 +- 11 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php create mode 100644 core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migrate_overwrite_test.info.yml create mode 100644 core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migration_templates/users.yml diff --git a/core/modules/comment/src/Tests/Migrate/d7/MigrateCommentTest.php b/core/modules/comment/src/Tests/Migrate/d7/MigrateCommentTest.php index 6c7277e7e225..8d6e668df4bb 100644 --- a/core/modules/comment/src/Tests/Migrate/d7/MigrateCommentTest.php +++ b/core/modules/comment/src/Tests/Migrate/d7/MigrateCommentTest.php @@ -11,7 +11,6 @@ use Drupal\comment\Entity\Comment; use Drupal\migrate_drupal\Tests\d7\MigrateDrupal7TestBase; use Drupal\node\NodeInterface; -use Drupal\user\Entity\User; /** * Tests migration of comments from Drupal 7. @@ -37,12 +36,6 @@ protected function setUp() { 'd7_user_role', 'd7_user', ]); - // The test database doesn't include uid 1, so we'll need to create it. - User::create(array( - 'uid' => 1, - 'name' => 'admin', - 'mail' => 'admin@local.host', - ))->save(); $this->executeMigration('d7_node_type'); // We only need the test_content_type node migration to run for real, so // mock all the others. diff --git a/core/modules/migrate/config/schema/migrate.data_types.schema.yml b/core/modules/migrate/config/schema/migrate.data_types.schema.yml index 7521ee129b09..60dd5193f66c 100644 --- a/core/modules/migrate/config/schema/migrate.data_types.schema.yml +++ b/core/modules/migrate/config/schema/migrate.data_types.schema.yml @@ -10,6 +10,13 @@ migrate_plugin: migrate_destination: type: migrate_plugin label: 'Destination' + mapping: + overwrite_properties: + type: sequence + label: 'Properties to overwrite' + sequence: + type: string + label: 'Property' migrate_source: type: migrate_plugin diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index 69c9e660a1fd..f80034ce2c26 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -109,6 +109,17 @@ public function getIds() { * The row object to update from. */ protected function updateEntity(EntityInterface $entity, Row $row) { + // If the migration has specified a list of properties to be overwritten, + // clone the row with an empty set of destination values, and re-add only + // the specified properties. + if (isset($this->configuration['overwrite_properties'])) { + $clone = $row->cloneWithoutDestination(); + foreach ($this->configuration['overwrite_properties'] as $property) { + $clone->setDestinationProperty($property, $row->getDestinationProperty($property)); + } + $row = $clone; + } + foreach ($row->getDestination() as $field_name => $values) { $field = $entity->$field_name; if ($field instanceof TypedDataInterface) { diff --git a/core/modules/migrate/src/Row.php b/core/modules/migrate/src/Row.php index 8774976950fa..e055c1d4c4dc 100644 --- a/core/modules/migrate/src/Row.php +++ b/core/modules/migrate/src/Row.php @@ -187,6 +187,15 @@ public function freezeSource() { return $this; } + /** + * Clones the row with an empty set of destination values. + * + * @return static + */ + public function cloneWithoutDestination() { + return (new static($this->getSource(), $this->sourceIds, $this->isStub()))->freezeSource(); + } + /** * Tests if destination property exists. * diff --git a/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php b/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php new file mode 100644 index 000000000000..1015aa16ad4b --- /dev/null +++ b/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * @file + * Contains \Drupal\migrate_drupal\Tests\d6\EntityContentBaseTest. + */ + +namespace Drupal\migrate_drupal\Tests\d6; + +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\user\Entity\User; + +/** + * @group migrate_drupal + */ +class EntityContentBaseTest extends MigrateDrupal6TestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['migrate_overwrite_test']; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + // Create a field on the user entity so that we can test nested property + // overwrites. + // @see static::testOverwriteSelectedNestedProperty() + FieldStorageConfig::create([ + 'field_name' => 'signature', + 'entity_type' => 'user', + 'type' => 'text_long', + ])->save(); + + FieldConfig::create([ + 'field_name' => 'signature', + 'entity_type' => 'user', + 'bundle' => 'user', + ])->save(); + + User::create([ + 'uid' => 2, + 'name' => 'Ford Prefect', + 'mail' => 'ford.prefect@localhost', + 'signature' => array( + array( + 'value' => 'Bring a towel.', + 'format' => 'filtered_html', + ), + ), + 'init' => 'proto@zo.an', + ])->save(); + + $this->executeMigrations(['d6_filter_format', 'd6_user_role']); + } + + /** + * Tests overwriting all mapped properties in the destination entity (default + * behavior). + */ + public function testOverwriteAllMappedProperties() { + $this->executeMigration('d6_user'); + /** @var \Drupal\user\UserInterface $account */ + $account = User::load(2); + $this->assertIdentical('john.doe', $account->label()); + $this->assertIdentical('john.doe@example.com', $account->getEmail()); + $this->assertIdentical('doe@example.com', $account->getInitialEmail()); + } + + /** + * Tests overwriting selected properties in the destination entity, specified + * in the destination configuration. + */ + public function testOverwriteProperties() { + // Execute the migration in migrate_overwrite_test, which documents how + // property overwrites work. + $this->executeMigration('users'); + + /** @var \Drupal\user\UserInterface $account */ + $account = User::load(2); + $this->assertIdentical('john.doe', $account->label()); + $this->assertIdentical('john.doe@example.com', $account->getEmail()); + $this->assertIdentical('The answer is 42.', $account->signature->value); + // This value is not overwritten because it's not listed in + // overwrite_properties. + $this->assertIdentical('proto@zo.an', $account->getInitialEmail()); + } + +} diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index 3a9d266cf160..0804334f9c4a 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -39895,9 +39895,9 @@ )) ->values(array( 'uid' => '1', - 'name' => 'root', + 'name' => 'admin', 'pass' => '$S$D/HVkgCg1Hvi7DN5KVSgNl.2C5g8W6oe/OoIRMUlyjkmPugQRhoB', - 'mail' => '', + 'mail' => 'admin@local.host', 'theme' => '', 'signature' => '', 'signature_format' => NULL, diff --git a/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migrate_overwrite_test.info.yml b/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migrate_overwrite_test.info.yml new file mode 100644 index 000000000000..503c6cec716d --- /dev/null +++ b/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migrate_overwrite_test.info.yml @@ -0,0 +1,6 @@ +name: 'Migrate property overwrite test' +type: module +description: 'Example module demonstrating property overwrite support in the Migrate API.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migration_templates/users.yml b/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migration_templates/users.yml new file mode 100644 index 000000000000..e23d90d9d7c2 --- /dev/null +++ b/core/modules/migrate_drupal/tests/modules/migrate_overwrite_test/migration_templates/users.yml @@ -0,0 +1,31 @@ +id: users +label: User migration +migration_tags: + - Drupal 6 + - Drupal 7 +source: + plugin: d6_user +process: + # If the entity's ID is migrated, the Migrate API will try to update + # an existing entity with that ID. If no entity with that ID already + # exists, it will be created. + uid: uid + name: name + mail: mail + password: password + 'signature/value': + plugin: default_value + default_value: 'The answer is 42.' +destination: + plugin: entity:user + # If the destination is going to update an existing user, you can optionally + # specify the properties that should be overwritten. For example, if the + # migration tries to import user 31 and user 31 already exists in the + # destination database, only the 'name' and 'mail' properties of the user + # will be overwritten. If user 31 doesn't exist, it will be created and + # the overwrite_properties list will be ignored. + overwrite_properties: + - name + - mail + # It's possible to overwrite nested properties too. + - 'signature/value' diff --git a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php index b2537f573bfe..9a9024babece 100644 --- a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php +++ b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php @@ -123,7 +123,7 @@ protected function assertRevision($id, $title, $uid, $log, $timestamp) { */ public function testNode() { $this->assertEntity(1, 'test_content_type', 'en', 'A Node', '2', TRUE, '1421727515', '1441032132', TRUE, FALSE); - $this->assertRevision(1, 'A Node', '2', NULL, '1441032132'); + $this->assertRevision(1, 'A Node', '1', NULL, '1441032132'); $node = Node::load(1); $this->assertTrue($node->field_boolean->value); diff --git a/core/modules/user/src/Plugin/migrate/source/d6/User.php b/core/modules/user/src/Plugin/migrate/source/d6/User.php index 59c3229ab1ba..e344d06517ad 100644 --- a/core/modules/user/src/Plugin/migrate/source/d6/User.php +++ b/core/modules/user/src/Plugin/migrate/source/d6/User.php @@ -25,7 +25,7 @@ class User extends DrupalSqlBase { public function query() { return $this->select('users', 'u') ->fields('u', array_keys($this->baseFields())) - ->condition('uid', 1, '>'); + ->condition('uid', 0, '>'); } /** diff --git a/core/modules/user/src/Plugin/migrate/source/d7/User.php b/core/modules/user/src/Plugin/migrate/source/d7/User.php index fc05bf3cd911..fa872c63874e 100755 --- a/core/modules/user/src/Plugin/migrate/source/d7/User.php +++ b/core/modules/user/src/Plugin/migrate/source/d7/User.php @@ -25,7 +25,7 @@ class User extends FieldableEntity { public function query() { return $this->select('users', 'u') ->fields('u') - ->condition('uid', 1, '>'); + ->condition('uid', 0, '>'); } /** -- GitLab