summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2015-11-12 12:31:44 -0800
committerwebchick2015-11-12 12:34:55 -0800
commitde14b6d199ea98f1524b742d3c9607fe14921440 (patch)
treeffc1a27faa7d61f23988b6a96978f1c4e7709740
parent87ac540626363682b114735146689742cb0f4853 (diff)
Issue #2590993 by mikeryan, webflo, svendecabooter, dixon_, phenaproxima: Create stub entities with proper default values
-rw-r--r--core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php63
-rw-r--r--core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php60
-rw-r--r--core/modules/comment/src/Plugin/migrate/destination/EntityComment.php37
-rw-r--r--core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php78
-rw-r--r--core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php8
-rw-r--r--core/modules/file/src/Plugin/migrate/destination/EntityFile.php44
-rw-r--r--core/modules/file/src/Plugin/migrate/process/d6/FileUri.php5
-rw-r--r--core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php42
-rw-r--r--core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php42
-rw-r--r--core/modules/migrate/src/MigrateExecutable.php4
-rw-r--r--core/modules/migrate/src/Plugin/migrate/destination/Entity.php13
-rw-r--r--core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php60
-rw-r--r--core/modules/migrate/src/Row.php2
-rw-r--r--core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php10
-rw-r--r--core/modules/migrate_drupal/src/Tests/StubTestTrait.php80
-rw-r--r--core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php48
-rw-r--r--core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php44
-rw-r--r--core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php30
-rw-r--r--core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php118
-rw-r--r--core/modules/user/src/Plugin/migrate/destination/EntityUser.php34
-rw-r--r--core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php43
21 files changed, 777 insertions, 88 deletions
diff --git a/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php b/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php
new file mode 100644
index 0000000..d620259
--- /dev/null
+++ b/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Tests\Migrate\MigrateAggregatorStubTest.
+ */
+
+namespace Drupal\aggregator\Tests\Migrate;
+
+use Drupal\migrate\MigrateException;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for aggregator feeds and items.
+ *
+ * @group aggregator
+ */
+class MigrateAggregatorStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['aggregator'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('aggregator_feed');
+ $this->installEntitySchema('aggregator_item');
+ }
+
+ /**
+ * Tests creation of aggregator feed stubs.
+ */
+ public function testFeedStub() {
+ $this->performStubTest('aggregator_feed');
+ }
+
+ /**
+ * Tests creation of aggregator feed items.
+ */
+ public function testItemStub() {
+ try {
+ // We expect an exception, because there's no feed to reference.
+ $this->performStubTest('aggregator_item');
+ $this->fail('Expected exception has not been thrown.');
+ }
+ catch (MigrateException $e) {
+ $this->assertIdentical($e->getMessage(),
+ 'Stubbing failed, unable to generate value for field fid');
+ }
+
+ // The stub should pass when there's a feed to point to.
+ $this->createStub('aggregator_feed');
+ $this->performStubTest('aggregator_item');
+ }
+
+}
diff --git a/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php b/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php
new file mode 100644
index 0000000..1971a9d
--- /dev/null
+++ b/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\Migrate\MigrateBlockContentStubTest.
+ */
+
+namespace Drupal\block_content\Tests\Migrate;
+
+use Drupal\block_content\Entity\BlockContentType;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for block_content entities.
+ *
+ * @group block_content
+ */
+class MigrateBlockContentStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['block_content'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('block_content');
+ }
+
+ /**
+ * Tests creation of block content stubs with no block_content_type available.
+ */
+ public function testStubFailure() {
+ $entity_id = $this->createStub('block_content');
+ $violations = $this->validateStub('block_content', $entity_id);
+ $this->assertIdentical(count($violations), 1);
+ $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', [
+ '%type' => 'block_content_type',
+ '%id' => 'block_content',
+ ]));
+ }
+
+ /**
+ * Tests creation of block content stubs when there is a block_content_type.
+ */
+ public function testStubSuccess() {
+ BlockContentType::create([
+ 'id' => 'test_block_content_type',
+ 'label' => 'Test block content type',
+ ])->save();
+ $this->performStubTest('block_content');
+ }
+
+}
diff --git a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
index 477cdcd..7ebbc83 100644
--- a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
+++ b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
@@ -10,6 +10,7 @@ namespace Drupal\comment\Plugin\migrate\destination;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\State\StateInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateException;
@@ -62,13 +63,15 @@ class EntityComment extends EntityContentBase {
* The list of bundles this entity type has.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
+ * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+ * The field type plugin manager service.
* @param \Drupal\Core\State\StateInterface $state
* The state storage object.
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* The query object that can query the given entity type.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StateInterface $state, QueryFactory $entity_query) {
- parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, StateInterface $state, QueryFactory $entity_query) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
$this->state = $state;
$this->entityQuery = $entity_query;
}
@@ -86,6 +89,7 @@ class EntityComment extends EntityContentBase {
$container->get('entity.manager')->getStorage($entity_type),
array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
$container->get('entity.manager'),
+ $container->get('plugin.manager.field.field_type'),
$container->get('state'),
$container->get('entity.query')
);
@@ -110,32 +114,9 @@ class EntityComment extends EntityContentBase {
*/
protected function processStubRow(Row $row) {
parent::processStubRow($row);
- $stub_commented_entity_type = $row->getDestinationProperty('entity_type');
-
- // While parent::getEntity() fills the bundle property for stub entities
- // if it's still empty, here we must also make sure entity_id/entity_type
- // are filled (so $comment->getCommentedEntity() always returns a value).
- if (empty($this->stubCommentedEntityIds[$stub_commented_entity_type])) {
- // Fill stub entity id. Any id will do, as long as it exists.
- $entity_type = $this->entityManager->getDefinition($stub_commented_entity_type);
- $id_key = $entity_type->getKey('id');
- $result = $this->entityQuery
- ->get($stub_commented_entity_type)
- ->range(0, 1)
- ->execute();
- if ($result) {
- $this->stubCommentedEntityIds[$stub_commented_entity_type] = array_pop($result);
- $row->setSourceProperty($id_key, $this->stubCommentedEntityIds[$stub_commented_entity_type]);
- }
- else {
- throw new MigrateException(t('Could not find parent entity to use for comment %id', ['%id' => implode(':', $row->getSourceIdValues())]), MigrationInterface::MESSAGE_ERROR);
- }
- }
-
- $row->setDestinationProperty('entity_id', $this->stubCommentedEntityIds[$stub_commented_entity_type]);
- $row->setDestinationProperty('entity_type', $stub_commented_entity_type);
- $row->setDestinationProperty('created', REQUEST_TIME);
- $row->setDestinationProperty('changed', REQUEST_TIME);
+ // Neither uid nor name is required in itself, but it is required to set one
+ // of them.
+ $row->setDestinationProperty('name', 'anonymous_stub');
}
}
diff --git a/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php b/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php
new file mode 100644
index 0000000..22818f5
--- /dev/null
+++ b/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\Tests\Migrate\MigrateCommentStubTest.
+ */
+
+namespace Drupal\comment\Tests\Migrate;
+
+use Drupal\comment\Entity\CommentType;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Test stub creation for comment entities.
+ *
+ * @group comment
+ */
+class MigrateCommentStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['comment', 'node'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('comment');
+ $this->installEntitySchema('node');
+ // Make sure uid 0 is created (default uid for comments is 0).
+ $storage = \Drupal::entityManager()->getStorage('user');
+ // Insert a row for the anonymous user.
+ $storage
+ ->create(array(
+ 'uid' => 0,
+ 'status' => 0,
+ 'name' => '',
+ ))
+ ->save();
+ // Need at least one node type and comment type present.
+ NodeType::create([
+ 'type' => 'testnodetype',
+ 'name' => 'Test node type',
+ ])->save();
+ CommentType::create([
+ 'id' => 'testcommenttype',
+ 'label' => 'Test comment type',
+ 'target_entity_type_id' => 'node',
+ ])->save();
+ }
+
+ /**
+ * Tests creation of comment stubs.
+ */
+ public function testStub() {
+ try {
+ // We expect an exception, because there's no node to reference.
+ $this->performStubTest('comment');
+ $this->fail('Expected exception has not been thrown.');
+ }
+ catch (MigrateException $e) {
+ $this->assertIdentical($e->getMessage(),
+ 'Stubbing failed, unable to generate value for field entity_id');
+ }
+
+ // The stub should pass when there's a node to point to.
+ $this->createStub('node');
+ $this->performStubTest('comment');
+ }
+
+}
diff --git a/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php b/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
index 4545e46..7219d95 100644
--- a/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
+++ b/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
@@ -22,13 +22,7 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
- public static $modules = [
- 'comment',
- // Directly testing that a stub comment's entity_id is populated upon
- // importing is not straightforward, but RDF module serves as an implicit
- // test - its hook_comment_storage_load() references a stubbed comment.
- 'rdf',
- ];
+ public static $modules = ['comment'];
/**
* {@inheritdoc}
diff --git a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
index c5e8989..576b38b 100644
--- a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
+++ b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
@@ -9,6 +9,8 @@ namespace Drupal\file\Plugin\migrate\destination;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\UriItem;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StreamWrapper\LocalStream;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
@@ -41,7 +43,7 @@ class EntityFile extends EntityContentBase {
/**
* {@inheritdoc}
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
$configuration += array(
'source_base_path' => '',
'source_path_property' => 'filepath',
@@ -49,7 +51,7 @@ class EntityFile extends EntityContentBase {
'move' => FALSE,
'urlencode' => FALSE,
);
- parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
$this->streamWrapperManager = $stream_wrappers;
$this->fileSystem = $file_system;
@@ -68,6 +70,7 @@ class EntityFile extends EntityContentBase {
$container->get('entity.manager')->getStorage($entity_type),
array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
$container->get('entity.manager'),
+ $container->get('plugin.manager.field.field_type'),
$container->get('stream_wrapper_manager'),
$container->get('file_system')
);
@@ -77,6 +80,12 @@ class EntityFile extends EntityContentBase {
* {@inheritdoc}
*/
protected function getEntity(Row $row, array $old_destination_id_values) {
+ // For stub rows, there is no real file to deal with, let the stubbing
+ // process take its default path.
+ if ($row->isStub()) {
+ return parent::getEntity($row, $old_destination_id_values);
+ }
+
$destination = $row->getDestinationProperty($this->configuration['destination_path_property']);
$entity = $this->storage->loadByProperties(['uri' => $destination]);
if ($entity) {
@@ -91,6 +100,12 @@ class EntityFile extends EntityContentBase {
* {@inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = array()) {
+ // For stub rows, there is no real file to deal with, let the stubbing
+ // process create the stub entity.
+ if ($row->isStub()) {
+ return parent::import($row, $old_destination_id_values);
+ }
+
$file = $row->getSourceProperty($this->configuration['source_path_property']);
$destination = $row->getDestinationProperty($this->configuration['destination_path_property']);
$source = $this->configuration['source_base_path'] . $file;
@@ -256,4 +271,29 @@ class EntityFile extends EntityContentBase {
return $filename;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function processStubRow(Row $row) {
+ // We stub the uri value ourselves so we can create a real stub file for it.
+ if (!$row->getDestinationProperty('uri')) {
+ $field_definitions = $this->entityManager
+ ->getFieldDefinitions($this->storage->getEntityTypeId(),
+ $this->getKey('bundle'));
+ $value = UriItem::generateSampleValue($field_definitions['uri']);
+ if (empty($value)) {
+ throw new MigrateException('Stubbing failed, unable to generate value for field uri');
+ }
+ // generateSampleValue() wraps the value in an array.
+ $value = reset($value);
+ // Make it into a proper public file uri, stripping off the existing
+ // scheme if present.
+ $value = 'public://' . preg_replace('|^[a-z]+://|i', '', $value);
+ // Create a real file, so File::preSave() can do filesize() on it.
+ touch($value);
+ $row->setDestinationProperty('uri', $value);
+ }
+ parent::processStubRow($row);
+ }
+
}
diff --git a/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php b/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
index b464ee0..912371c 100644
--- a/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
+++ b/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
@@ -24,6 +24,11 @@ class FileUri extends ProcessPluginBase {
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+ // If we're stubbing a file entity, return a uri of NULL so it will get
+ // stubbed by the general process.
+ if ($row->isStub()) {
+ return NULL;
+ }
list($filepath, $file_directory_path, $temp_directory_path, $is_public) = $value;
// Specific handling using $temp_directory_path for temporary files.
diff --git a/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php b/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php
new file mode 100644
index 0000000..ae7446c
--- /dev/null
+++ b/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\file\Tests\Migrate\MigrateFileStubTest.
+ */
+
+namespace Drupal\file\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for file entities.
+ *
+ * @group file
+ */
+class MigrateFileStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['file'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('file');
+ }
+
+ /**
+ * Tests creation of file stubs.
+ */
+ public function testStub() {
+ $this->performStubTest('file');
+ }
+
+}
diff --git a/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php b/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php
new file mode 100644
index 0000000..47e94d1
--- /dev/null
+++ b/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link_content\Tests\Migrate\MigrateMenuLinkContentStubTest.
+ */
+
+namespace Drupal\menu_link_content\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for menu link content entities.
+ *
+ * @group menu_link_content
+ */
+class MigrateMenuLinkContentStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['menu_link_content', 'link'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('menu_link_content');
+ }
+
+ /**
+ * Tests creation of menu link content stubs.
+ */
+ public function testStub() {
+ $this->performStubTest('menu_link_content');
+ }
+
+}
diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php
index 2749313..a370623 100644
--- a/core/modules/migrate/src/MigrateExecutable.php
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -401,8 +401,8 @@ class MigrateExecutable implements MigrateExecutableInterface {
$multiple = $multiple || $plugin->multiple();
}
}
- // No plugins means do not set.
- if ($plugins) {
+ // No plugins or no value means do not set.
+ if ($plugins && !is_null($value)) {
$row->setDestinationProperty($destination, $value);
}
// Reset the value.
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
index 64123d6..f7a6a1e 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
@@ -137,19 +137,6 @@ abstract class Entity extends DestinationBase implements ContainerFactoryPluginI
}
/**
- * Process the stub values.
- *
- * @param \Drupal\migrate\Row $row
- * The row of data.
- */
- protected function processStubRow(Row $row) {
- $bundle_key = $this->getKey('bundle');
- if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
- $row->setDestinationProperty($bundle_key, reset($this->bundles));
- }
- }
-
- /**
* Returns a specific entity key.
*
* @param string $key
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index ae3030a..4dfd6c6 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -7,12 +7,17 @@
namespace Drupal\migrate\Plugin\migrate\destination;
+use Drupal\Component\Utility\Random;
+use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\link\LinkItemInterface;
use Drupal\migrate\Entity\MigrationInterface;
+use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -30,6 +35,13 @@ class EntityContentBase extends Entity {
protected $entityManager;
/**
+ * Field type plugin manager.
+ *
+ * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+ */
+ protected $fieldTypeManager;
+
+ /**
* Constructs a content entity.
*
* @param array $configuration
@@ -46,10 +58,13 @@ class EntityContentBase extends Entity {
* The list of bundles this entity type has.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
+ * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+ * The field type plugin manager service.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles);
$this->entityManager = $entity_manager;
+ $this->fieldTypeManager = $field_type_manager;
}
/**
@@ -64,7 +79,8 @@ class EntityContentBase extends Entity {
$migration,
$container->get('entity.manager')->getStorage($entity_type),
array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
- $container->get('entity.manager')
+ $container->get('entity.manager'),
+ $container->get('plugin.manager.field.field_type')
);
}
@@ -132,4 +148,44 @@ class EntityContentBase extends Entity {
$this->setRollbackAction($row->getIdMap());
}
+ /**
+ * Do as much population of the stub row as we can.
+ *
+ * @param \Drupal\migrate\Row $row
+ * The row of data.
+ */
+ protected function processStubRow(Row $row) {
+ $bundle_key = $this->getKey('bundle');
+ if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
+ $row->setDestinationProperty($bundle_key, reset($this->bundles));
+ }
+
+ // Populate any required fields not already populated.
+ $fields = $this->entityManager
+ ->getFieldDefinitions($this->storage->getEntityTypeId(), $bundle_key);
+ foreach ($fields as $field_name => $field_definition) {
+ 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;
+ }
+ 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());
+ $values = $field_type_class::generateSampleValue($field_definition);
+ if (is_null($values)) {
+ // Handle failure to generate a sample value.
+ throw new MigrateException('Stubbing failed, unable to generate value for field ' . $field_name);
+ break;
+ }
+ }
+
+ $row->setDestinationProperty($field_name, $values);
+ }
+ }
+ }
+
}
diff --git a/core/modules/migrate/src/Row.php b/core/modules/migrate/src/Row.php
index a509da4..4e788af 100644
--- a/core/modules/migrate/src/Row.php
+++ b/core/modules/migrate/src/Row.php
@@ -73,7 +73,7 @@ class Row {
*
* @see getRawDestination()
*/
- protected $rawDestination;
+ protected $rawDestination = [];
/**
* TRUE when this row is a stub.
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index d5f71a0..2db5d9e 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -36,6 +36,11 @@ class EntityRevisionTest extends UnitTestCase {
*/
protected $entityManager;
+ /**
+ * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+ */
+ protected $fieldTypeManager;
+
public function setUp() {
parent::setUp();
@@ -43,6 +48,7 @@ class EntityRevisionTest extends UnitTestCase {
$this->migration = $this->prophesize('\Drupal\migrate\Entity\MigrationInterface');
$this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface');
$this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface');
+ $this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface');
}
/**
@@ -183,7 +189,9 @@ class EntityRevisionTest extends UnitTestCase {
$this->migration->reveal(),
$this->storage->reveal(),
[],
- $this->entityManager->reveal());
+ $this->entityManager->reveal(),
+ $this->fieldTypeManager->reveal()
+ );
}
}
diff --git a/core/modules/migrate_drupal/src/Tests/StubTestTrait.php b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
new file mode 100644
index 0000000..1e028bf
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate_drupal\Tests\StubTestTrait.
+ */
+
+namespace Drupal\migrate_drupal\Tests;
+use Drupal\migrate\Entity\Migration;
+use Drupal\migrate\Row;
+
+/**
+ * Provides common functionality for testing stubbing.
+ */
+trait StubTestTrait {
+
+ /**
+ * Test that creating a stub of the given entity type results in a valid
+ * entity.
+ *
+ * @param string $entity_type_id
+ * The entity type we are stubbing.
+ */
+ protected function performStubTest($entity_type_id) {
+ $entity_id = $this->createStub($entity_type_id);
+ $this->assertTrue($entity_id, 'Stub successfully created');
+ if ($entity_id) {
+ $violations = $this->validateStub($entity_type_id, $entity_id);
+ if (!$this->assertIdentical(count($violations), 0, 'Stub is a valid entity')) {
+ foreach ($violations as $violation) {
+ $this->fail((string) $violation->getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a stub of the given entity type.
+ *
+ * @param string $entity_type_id
+ * The entity type we are stubbing.
+ *
+ * @return int
+ * ID of the created entity.
+ */
+ protected function createStub($entity_type_id) {
+ // Create a dummy migration to pass to the destination plugin.
+ $config = [
+ 'id' => 'dummy',
+ 'migration_tags' => ['Stub test'],
+ 'source' => ['plugin' => 'empty'],
+ 'process' => [],
+ 'destination' => ['plugin' => 'entity:' . $entity_type_id],
+ ];
+ $migration = Migration::create($config);
+ $destination_plugin = $migration->getDestinationPlugin(TRUE);
+ $stub_row = new Row([], [], TRUE);
+ $destination_ids = $destination_plugin->import($stub_row);
+ return reset($destination_ids);
+ }
+
+ /**
+ * Perform validation on a stub entity.
+ *
+ * @param string $entity_type_id
+ * The entity type we are stubbing.
+ * @param string $entity_id
+ * ID of the stubbed entity to validate.
+ *
+ * @return \Drupal\Core\Entity\EntityConstraintViolationListInterface
+ * List of constraint violations identified.
+ */
+ protected function validateStub($entity_type_id, $entity_id) {
+ $controller = \Drupal::entityManager()->getStorage($entity_type_id);
+ /** @var \Drupal\Core\Entity\ContentEntityInterface $stub_entity */
+ $stub_entity = $controller->load($entity_id);
+ return $stub_entity->validate();
+ }
+
+}
diff --git a/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php b/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php
new file mode 100644
index 0000000..22d140b
--- /dev/null
+++ b/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Tests\Migrate\MigrateNodeStubTest.
+ */
+
+namespace Drupal\node\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Test stub creation for nodes.
+ *
+ * @group node
+ */
+class MigrateNodeStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['node'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('node');
+ // Need at least one node type present.
+ NodeType::create([
+ 'type' => 'testnodetype',
+ 'name' => 'Test node type',
+ ])->save();
+ }
+
+ /**
+ * Tests creation of node stubs.
+ */
+ public function testStub() {
+ $this->performStubTest('node');
+ }
+
+}
diff --git a/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php b/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php
new file mode 100644
index 0000000..ba9a000
--- /dev/null
+++ b/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Tests\Migrate\MigrateShortcutStubTest.
+ */
+
+namespace Drupal\shortcut\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for shortcut entities.
+ *
+ * @group shortcut
+ */
+class MigrateShortcutStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['shortcut', 'link'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('shortcut');
+ // Make sure the 'default' shortcut_set is installed.
+ $this->installConfig(['shortcut']);
+ }
+
+ /**
+ * Tests creation of shortcut stubs.
+ */
+ public function testStub() {
+ $this->performStubTest('shortcut');
+ }
+
+}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php b/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php
deleted file mode 100644
index 0a28f5b..0000000
--- a/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\taxonomy\Plugin\migrate\destination\EntityTaxonomyTerm.
- */
-
-namespace Drupal\taxonomy\Plugin\migrate\destination;
-
-use Drupal\migrate\Row;
-use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
-
-/**
- * @MigrateDestination(
- * id = "entity:taxonomy_term"
- * )
- */
-class EntityTaxonomyTerm extends EntityContentBase {
-
- /**
- * {@inheritdoc}
- */
- protected function getEntity(Row $row, array $old_destination_id_values) {
- if ($row->isStub()) {
- $row->setDestinationProperty('name', $this->t('Stub name for source tid:') . $row->getSourceProperty('tid'));
- }
- return parent::getEntity($row, $old_destination_id_values);
- }
-
-}
diff --git a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php
new file mode 100644
index 0000000..a7cd9ca
--- /dev/null
+++ b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Tests\Migrate\MigrateTaxonomyTermStubTest.
+ */
+
+namespace Drupal\taxonomy\Tests\Migrate;
+
+use Drupal\migrate\Entity\Migration;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\Entity\Vocabulary;
+
+/**
+ * Test stub creation for taxonomy terms.
+ *
+ * @group taxonomy
+ */
+class MigrateTaxonomyTermStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['taxonomy', 'text'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('taxonomy_term');
+ }
+
+ /**
+ * Tests creation of taxonomy term stubs.
+ */
+ public function testStub() {
+ Vocabulary::create([
+ 'vid' => 'test_vocabulary',
+ 'name' => 'Test vocabulary',
+ ])->save();
+ $this->performStubTest('taxonomy_term');
+ }
+
+ /**
+ * Tests creation of stubs when weight is mapped.
+ */
+ public function testStubWithWeightMapping() {
+ // Create a vocabulary via migration for the terms to reference.
+ $vocabulary_data_rows = [
+ ['id' => '1', 'name' => 'tags'],
+ ];
+ $ids = ['id' => ['type' => 'integer']];
+ $config = [
+ 'id' => 'vocabularies',
+ 'migration_tags' => ['Stub test'],
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => $vocabulary_data_rows,
+ 'ids' => $ids,
+ ],
+ 'process' => [
+ 'vid' => 'id',
+ 'name' => 'name',
+ ],
+ 'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
+ ];
+ $vocabulary_migration = Migration::create($config);
+ $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
+ $vocabulary_executable->import();
+
+ // We have a term referencing an unmigrated parent, forcing a stub to be
+ // created.
+ $term_data_rows = [
+ ['id' => '1', 'vocab' => '1', 'name' => 'music', 'parent' => '2'],
+ ];
+ $ids = ['id' => ['type' => 'integer']];
+ $config = [
+ 'id' => 'terms',
+ 'migration_tags' => ['Import and rollback test'],
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => $term_data_rows,
+ 'ids' => $ids,
+ ],
+ 'process' => [
+ 'tid' => 'id',
+ 'vid' => 'vocab',
+ 'name' => 'name',
+ 'weight' => 'weight',
+ 'parent' => [
+ 'plugin' => 'migration',
+ 'migration' => 'terms',
+ 'source' => 'parent',
+ ],
+ ],
+ 'destination' => ['plugin' => 'entity:taxonomy_term'],
+ 'migration_dependencies' => ['required' => ['vocabularies']],
+ ];
+
+ $term_migration = Migration::create($config);
+ $term_migration->save();
+ $term_executable = new MigrateExecutable($term_migration, $this);
+ $term_executable->import();
+ // Load the referenced term, which should exist as a stub.
+ /** @var \Drupal\Core\Entity\ContentEntityBase $stub_entity */
+ $stub_entity = Term::load(2);
+ $this->assertTrue($stub_entity, 'Stub successfully created');
+ if ($stub_entity) {
+ $this->assertIdentical(count($stub_entity->validate()), 0, 'Stub is a valid entity');
+ }
+ }
+}
diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
index 785a3b5..beda7c8 100644
--- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
+++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
@@ -7,8 +7,11 @@
namespace Drupal\user\Plugin\migrate\destination;
+use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
use Drupal\Core\Password\PasswordInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateException;
@@ -50,11 +53,13 @@ class EntityUser extends EntityContentBase {
* The migrate plugin manager.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
+ * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+ * The field type plugin manager service.
* @param \Drupal\Core\Password\PasswordInterface $password
* The password service.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, PasswordInterface $password) {
- parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, PasswordInterface $password) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
if (isset($configuration['md5_passwords'])) {
$this->password = $password;
}
@@ -73,6 +78,7 @@ class EntityUser extends EntityContentBase {
$container->get('entity.manager')->getStorage($entity_type),
array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
$container->get('entity.manager'),
+ $container->get('plugin.manager.field.field_type'),
$container->get('password')
);
}
@@ -102,4 +108,28 @@ class EntityUser extends EntityContentBase {
return $ids;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function processStubRow(Row $row) {
+ parent::processStubRow($row);
+ // Email address is not defined as required in the base field definition but
+ // is effectively required by the UserMailRequired constraint. This means
+ // that Entity::processStubRow() did not populate it - we do it here.
+ $field_definitions = $this->entityManager
+ ->getFieldDefinitions($this->storage->getEntityTypeId(),
+ $this->getKey('bundle'));
+ $mail = EmailItem::generateSampleValue($field_definitions['mail']);
+ $row->setDestinationProperty('mail', reset($mail));
+
+ // @todo Work-around for https://www.drupal.org/node/2602066.
+ $name = $row->getDestinationProperty('name');
+ if (is_array($name)) {
+ $name = reset($name);
+ }
+ if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
+ $row->setDestinationProperty('name', Unicode::substr($name, 0, USERNAME_MAX_LENGTH));
+ }
+ }
+
}
diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php
new file mode 100644
index 0000000..0a65ddf
--- /dev/null
+++ b/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\Migrate\MigrateUserStubTest.
+ */
+
+namespace Drupal\user\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for user entities.
+ *
+ * @group user
+ */
+class MigrateUserStubTest extends MigrateDrupalTestBase {
+
+ use StubTestTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['user'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('user');
+ $this->installSchema('system', ['sequences']);
+ }
+
+ /**
+ * Tests creation of user stubs.
+ */
+ public function testStub() {
+ $this->performStubTest('user');
+ }
+
+}