diff --git a/migrate_example/config/migrate.migration.migrate_example_content.yml b/migrate_example/config/migrate.migration.migrate_example_content.yml new file mode 100644 index 0000000..66ca514 --- /dev/null +++ b/migrate_example/config/migrate.migration.migrate_example_content.yml @@ -0,0 +1,22 @@ +id: migrate_example_content +source: + plugin: migrate_example_content +destination: + plugin: entity + entity_type: node + type: page +process: + title: subject + # @todo The 'body' field to be migrated when https://drupal.org/node/2164451. + # body: text + uid: + - + plugin: migration + migration: migrate_example_people + source: + - author + created: + - + plugin: callback + callable: strtotime + source: date diff --git a/migrate_example/config/migrate.migration.migrate_example_people.yml b/migrate_example/config/migrate.migration.migrate_example_people.yml new file mode 100644 index 0000000..7020954 --- /dev/null +++ b/migrate_example/config/migrate.migration.migrate_example_people.yml @@ -0,0 +1,26 @@ +id: migrate_example_people +source: + plugin: migrate_example_people +destination: + plugin: entity_user + md5_passwords: true +process: + name: + - + plugin: concat + delimiter: . + source: + - first_name + - last_name + - + plugin: callback + callable: + - '\Drupal\Component\Utility\Unicode' + - strtolower + mail: email + pass: pass + roles: + - + plugin: explode + delimiter: ';' + source: groups diff --git a/migrate_example/config/migrate.migration.migrate_example_user_role.yml b/migrate_example/config/migrate.migration.migrate_example_user_role.yml new file mode 100644 index 0000000..b16582c --- /dev/null +++ b/migrate_example/config/migrate.migration.migrate_example_user_role.yml @@ -0,0 +1,16 @@ +id: migrate_example_user_role +source: + plugin: migrate_example_user_role +destination: + plugin: entity + entity_type: user_role +process: + id: + - + plugin: machine_name + source: group + - + plugin: dedupe_entity + entity_type: user_role + field: id + label: group diff --git a/migrate_example/lib/Drupal/migrate_example/Controller/MigrateExampleController.php b/migrate_example/lib/Drupal/migrate_example/Controller/MigrateExampleController.php new file mode 100644 index 0000000..b6ed5dc --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Controller/MigrateExampleController.php @@ -0,0 +1,40 @@ +' . t(' A basic example of defining a migration through configuration YAML files and custom source and process plugins.') . '

'; + $markup .= '

' . t("In this example we are performing three migrations: roles, users, content. The source of the migration is proprietary hypothetical CMS into Drupal 8, using the migrate API provided by the core Migrate module. The biggest part of the migration is defined in the migration YAML files, placed under config/' directory.") . '

'; + $markup .= '

' . t("However, migrate config files are not enough because we still need to implement some migration plugins. The module implements a source plugin for each migration and a process plugin for transforming the value to be imported. Sandbox data is defined in migrate_example.install file and it's used in tests provided by the module.") . '

'; + $markup .= '

' . t("Migrate examples provided by the module:") . '

'; + $markup .= ''; + $markup .= '

' . t("How to test?") . '

'; + $markup .= '

' . t("There are three simpletest tests provided for each migration or you can simply enable the module and run manually the migrations.") . '

'; + + // @todo The 'body' field to be migrated in https://drupal.org/node/2164451. + + return array('#markup' => $markup); + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/process/Explode.php b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/process/Explode.php new file mode 100644 index 0000000..5e5aaae --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/process/Explode.php @@ -0,0 +1,30 @@ +configuration['delimiter'], $value); + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Base.php b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Base.php new file mode 100644 index 0000000..9006329 --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Base.php @@ -0,0 +1,28 @@ +database)) { + $this->database = Database::getConnection('default', 'default'); + } + return $this->database; + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Content.php b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Content.php new file mode 100644 index 0000000..5fcb80c --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Content.php @@ -0,0 +1,46 @@ + array('type' => 'integer')); + } + + /** + * {@inheritdoc} + */ + public function fields() { + return array( + 'article_id' => $this->t('Article ID'), + 'subject' => $this->t('Subject'), + 'text' => $this->t('Article body text'), + 'author' => $this->t('Author user ID.'), + 'date' => $this->t('Article release date'), + ); + } + + /** + * {@inheritdoc} + */ + public function query() { + $fields = array_keys($this->fields()); + return $this->select('migrate_example_content', 'c') + ->fields('c', $fields); + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/People.php b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/People.php new file mode 100644 index 0000000..b0bb3ec --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/People.php @@ -0,0 +1,46 @@ + array('type' => 'integer')); + } + + /** + * {@inheritdoc} + */ + public function fields() { + return array( + 'id' => $this->t('User ID'), + 'first_name' => $this->t('First name'), + 'last_name' => $this->t('Last name'), + 'email' => $this->t('E-mail'), + 'pass' => $this->t('MD5 hashed password'), + 'groups' => $this->t('User groups'), + ); + } + + /** + * {@inheritdoc} + */ + public function query() { + return $this->select('migrate_example_people', 'p') + ->fields('p', array('id', 'first_name', 'last_name', 'email', 'pass', 'groups')); + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Role.php b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Role.php new file mode 100644 index 0000000..6c002b3 --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Plugin/migrate/source/Role.php @@ -0,0 +1,89 @@ + array('type' => 'string')); + } + + /** + * {@inheritdoc} + */ + public function __toString() { + return (string) $this->query(); + } + + /** + * {@inheritdoc} + */ + public function fields() { + return array( + 'group' => $this->t('Group'), + ); + } + + /** + * {@inheritdoc} + */ + public function getIterator() { + if (!isset($this->iterator)) { + $people = $this->query()->execute()->fetchCol(); + + $items = array(); + foreach ($people as $groups) { + $groups = explode(';', $groups); + foreach ($groups as $group) { + if (!isset($items[$group])) { + $items[$group] = array( + 'group' => $group, + ); + } + } + } + $this->iterator = new \IteratorIterator(new \ArrayIterator($items)); + } + + return $this->iterator; + } + + /** + * {@inheritdoc} + */ + public function count() { + $this->getIterator()->count(); + } + + /** + * Gets the source user records. + * + * @returns \Drupal\Core\Database\Query\SelectInterface + */ + protected function query() { + return db_select('migrate_example_people', 'p') + ->fields('p', array('groups')); + } +} diff --git a/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleContentTest.php b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleContentTest.php new file mode 100644 index 0000000..7196648 --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleContentTest.php @@ -0,0 +1,64 @@ + 'Migrate Content Example', + 'description' => 'Example of migrating content from a proprietary legacy system.', + 'group' => 'Examples', + ); + } + + function testContent() { + // Migrate users prior to content migration. + $user_migration = entity_load('migration', 'migrate_example_people'); + $executable = new MigrateExecutable($user_migration, $this); + $executable->import(); + + // Migrate content. + $migration = entity_load('migration', 'migrate_example_content'); + $executable = new MigrateExecutable($migration, $this); + $executable->import(); + + $source = db_select('migrate_example_content', 'c') + ->fields('c', array('article_id', 'subject', 'text', 'author', 'date')) + ->execute() + ->fetchAllAssoc('article_id'); + + foreach ($source as $article_id => $content) { + // Get the destination node ID from the migration map. + $nid = $migration->getIdMap()->lookupDestinationId(array($article_id)); + $nid = reset($nid); + + $node = node_load($nid); + + // Assertions. + $this->assertEqual($node->title->value, $content->subject); + + // @todo The 'body' field to be migrated only when + // https://drupal.org/node/2164451. + // $this->assertEqual($node->body->value, $content->text); + + $uid = $user_migration->getIdMap()->lookupDestinationId(array($content->author)); + $uid = reset($uid); + $this->assertEqual($node->uid->value, $uid); + $this->assertEqual($node->getCreatedTime(), strtotime($content->date)); + } + + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExamplePeopleTest.php b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExamplePeopleTest.php new file mode 100644 index 0000000..aea4b5d --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExamplePeopleTest.php @@ -0,0 +1,61 @@ + 'Migrate Users Example', + 'description' => 'Example of migrating users from a proprietary legacy system.', + 'group' => 'Examples', + ); + } + + function testUsers() { + + $migration = entity_load('migration', 'migrate_example_people'); + $executable = new MigrateExecutable($migration, $this); + $executable->import(); + + $source = db_select('migrate_example_people', 'p') + ->fields('p', array('id', 'first_name', 'last_name', 'email', 'pass', 'plain_pass', 'groups')) + ->execute() + ->fetchAllAssoc('id'); + + foreach ($source as $id => $person) { + // Get the destination user ID. + $uid = $migration->getIdMap()->lookupDestinationId(array($id)); + $uid = reset($uid); + + $user = entity_load('user', $uid); + + // Assertions. + $name = Unicode::strtolower($person->first_name . '.' . $person->last_name); + $this->assertEqual($user->label(), $name); + $this->assertEqual($user->mail->value, $person->email); + + $groups = explode(';', $person->groups); + $diff = array_diff($user->getRoles(), $groups); + $this->assertEqual($diff, array('authenticated')); + + // Login using the UI. + $this->drupalPostForm('user/login', array('name' => $name, 'pass' => $person->plain_pass), t('Log in')); + $this->assertNoRaw(t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password', array('query' => array('name' => $name)))))); + $this->drupalLogout(); + } + + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleTestBase.php b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleTestBase.php new file mode 100644 index 0000000..65b702d --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleTestBase.php @@ -0,0 +1,29 @@ +pass($message); + } + else { + $this->fail($message); + } + } + +} diff --git a/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleUserRoleTest.php b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleUserRoleTest.php new file mode 100644 index 0000000..29dd302 --- /dev/null +++ b/migrate_example/lib/Drupal/migrate_example/Tests/MigrateExampleUserRoleTest.php @@ -0,0 +1,39 @@ + 'Migrate User Roles Example', + 'description' => 'Example of migrating user groups/roles from a proprietary legacy system.', + 'group' => 'Examples', + ); + } + + function testUserRole() { + + /** @var \Drupal\migrate\entity\Migration $migration */ + $migration = entity_load('migration', 'migrate_example_user_role'); + $executable = new MigrateExecutable($migration, $this); + $executable->import(); + + foreach (array('staff', 'management', 'shareholder') as $rid) { + $role = entity_load('user_role', $rid); + $this->assertEqual($role->id(), $rid); + $this->assertEqual(array($rid), $migration->getIdMap()->lookupDestinationId(array($rid))); + } + } + +} diff --git a/migrate_example/migrate_example.info.yml b/migrate_example/migrate_example.info.yml new file mode 100755 index 0000000..7fbfa12 --- /dev/null +++ b/migrate_example/migrate_example.info.yml @@ -0,0 +1,7 @@ +name: Migrate Example +description: The simplest example of implementing a migration. +package: Example modules +type: module +core: 8.x +dependencies: + - migrate diff --git a/migrate_example/migrate_example.install b/migrate_example/migrate_example.install new file mode 100644 index 0000000..540c1c7 --- /dev/null +++ b/migrate_example/migrate_example.install @@ -0,0 +1,197 @@ + mt_rand(1, 100), + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'doe@example.com', + 'pass' => md5('doe-pass'), + 'plain_pass' => 'doe-pass', + 'groups' => 'staff', + ), + array( + 'id' => mt_rand(101, 200), + 'first_name' => 'Joe', + 'last_name' => 'Roe', + 'email' => 'roe@example.com', + 'pass' => md5('roe-pass'), + 'plain_pass' => 'roe-pass', + 'groups' => 'staff;management', + ), + array( + 'id' => mt_rand(201, 300), + 'first_name' => 'Joe', + 'last_name' => 'Bloggs ', + 'email' => 'bloggs@example.com', + 'pass' => md5('bloggs-pass'), + 'plain_pass' => 'bloggs-pass', + 'groups' => 'shareholder;management', + ), + ); + + $query = db_insert('migrate_example_people') + ->fields(array('id', 'first_name', 'last_name', 'email', 'pass', 'plain_pass', 'groups')); + foreach ($people as $record) { + $query->values($record); + } + $query->execute(); + + $content = array( + array( + 'article_id' => mt_rand(0, 100), + 'subject' => 'Foo', + 'text' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur id metus ut est mollis congue ornare et enim. Mauris vel accumsan sem. In gravida eget libero et sagittis.', + 'author' => $people[0]['id'], + 'date' => '2004-12-12 13:45:06', + ), + array( + 'article_id' => mt_rand(101, 200), + 'subject' => 'Bar', + 'text' => 'Morbi pellentesque sapien molestie eros aliquam porttitor. Nullam est dui, dapibus vulputate posuere sed, euismod sit amet libero. Nunc in lacus a enim malesuada luctus.', + 'author' => $people[1]['id'], + 'date' => '2008-02-23 03:56:32', + ), + array( + 'article_id' => mt_rand(201, 300), + 'subject' => 'Foo bar', + 'text' => 'Mauris mollis auctor augue, sed pulvinar nunc sodales in. Integer est tortor, bibendum vel sodales ut, commodo a ligula. Morbi porta, lorem a laoreet facilisis, diam justo ullamcorper dui, eu commodo nunc lorem tempus purus.', + 'author' => $people[2]['id'], + 'date' => '2012-11-02 10:45:01', + ), + array( + 'article_id' => mt_rand(301, 400), + 'subject' => 'Qux baz', + 'text' => 'Donec aliquam sed purus id posuere. Sed tincidunt ante vel semper malesuada. Sed imperdiet leo egestas eleifend egestas. Curabitur tristique sapien adipiscing nisi sodales pulvinar. Aenean commodo luctus lectus non pretium.', + 'author' => $people[0]['id'], + 'date' => '2001-01-02 08:19:13', + ), + ); + + $query = db_insert('migrate_example_content') + ->fields(array('article_id', 'subject', 'text', 'author', 'date')); + foreach ($content as $record) { + $query->values($record); + } + $query->execute(); + +} + +/** + * Implements hook_schema(). + * + * Defines the database tables used by this module. + * + * @see hook_schema() + * @ingroup migrate_example + */ +function migrate_example_schema() { + $schema['migrate_example_people'] = array( + 'description' => 'Stores example person entries for demonstration purposes.', + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique person ID.', + ), + 'first_name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'First name of the person.', + ), + 'last_name' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Last name of the person.', + ), + 'email' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'E-mail of the person.', + ), + 'pass' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The MD5 hashed password of the person.', + ), + 'plain_pass' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The plain password of the person. Just for testing purposes.', + ), + 'groups' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The list of user groups separated by semicolon.', + ), + ), + 'primary key' => array('id'), + ); + + $schema['migrate_example_content'] = array( + 'description' => 'Stores example content entries for demonstration purposes.', + 'fields' => array( + 'article_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique content ID.', + ), + 'subject' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Article subject.', + ), + 'text' => array( + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + 'description' => 'Article text body.', + ), + 'author' => array( + 'type' => 'int', + 'not null' => FALSE, + 'description' => 'Article author.', + ), + 'date' => array( + 'type' => 'varchar', + 'length' => 20, + 'not null' => TRUE, + 'description' => "The article creation date in format 'Y-m-d H:i:s'.", + ), + ), + 'primary key' => array('article_id'), + ); + + return $schema; +} diff --git a/migrate_example/migrate_example.module b/migrate_example/migrate_example.module new file mode 100644 index 0000000..885a1d1 --- /dev/null +++ b/migrate_example/migrate_example.module @@ -0,0 +1,48 @@ + 'Migrate Example', + 'route_name' => 'migrate_example.description', + ); + return $items; +} diff --git a/migrate_example/migrate_example.routing.yml b/migrate_example/migrate_example.routing.yml new file mode 100644 index 0000000..7949e46 --- /dev/null +++ b/migrate_example/migrate_example.routing.yml @@ -0,0 +1,6 @@ +migrate_example.description: + path: '/examples/migrate_example' + defaults: + _content: '\Drupal\migrate_example\Controller\MigrateExampleController::description' + requirements: + _access: 'TRUE'