Update to Drupal 8.1.5. For more information, see https://www.drupal.org/project/drupal/releases/8.1.5

This commit is contained in:
Pantheon Automation 2016-07-07 09:44:38 -07:00 committed by Greg Anderson
parent 13b6ca7cc2
commit 38ba7c357d
342 changed files with 7814 additions and 1534 deletions

View file

@ -17,33 +17,30 @@ use Drupal\migrate\Row;
* @section overview Overview of migration
* Migration is an
* @link http://wikipedia.org/wiki/Extract,_transform,_load Extract, Transform, Load @endlink
* (ETL) process. For historical reasons, in the Drupal migration tool the
* extract phase is called "source", the transform phase is called "process",
* and the load phase is called "destination".
* (ETL) process. In the Drupal migration API the extract phase is called
* "source", the transform phase is called "process", and the load phase is
* called "destination". It is important to understand that the "load" in ETL
* means to load data into storage, while traditionally Drupal uses "load" to
* mean load data from storage into memory.
*
* Source, process, and destination phases are each provided by plugins. Source
* plugins extract data from a data source in "rows", containing "properties".
* Each row is handed off to one or more series of process plugins, where each
* series operates to transform the row data into one result property. After all
* the properties are processed, the resulting row is handed off to a
* destination plugin, which saves the data.
* Source, process, and destination phases are each provided by plugins.
* Source plugins extract data from a data source in "rows", containing
* "properties". Each row is handed off to one or more process plugins which
* transform the row's properties. After all the properties are processed, the
* resulting row is handed off to a destination plugin, which saves the data.
*
* The Migrate module provides process plugins for common operations (setting
* default values, mapping values, etc.), and destination plugins for Drupal
* core objects (configuration, entity, URL alias, etc.). The Migrate Drupal
* module provides source plugins to extract data from various versions of
* Drupal. Custom and contributed modules can provide additional plugins; see
* the @link plugin_api Plugin API topic @endlink for generic information about
* providing plugins, and sections below for details about the plugin types.
* A source plugin, one or more process plugins, and a destination plugin are
* brought together to extract, transform, and load (in the ETL sense) a specific
* type of data by a migration plugin.
*
* The configuration of migrations is stored in configuration entities, which
* list the IDs and configurations of the plugins that are involved. See
* @ref sec_entity below for details. To migrate an entire site, you'll need to
* create a migration manifest; see @ref sec_manifest for details.
*
* https://www.drupal.org/node/2127611 has more complete information on the
* Migration API, including information on load plugins, which are only used
* in Drupal 6 migration.
* @section sec_migrations Migration plugins
* Migration plugin definitions are stored in a module's 'migrations' directory.
* For backwards compatibility we also scan the 'migration_templates' directory.
* Examples of migration plugin definitions can be found in
* 'core/modules/action/migration_templates'. The plugin class is
* \Drupal\migrate\Plugin\Migration, with interface
* \Drupal\migrate\Plugin\MigrationInterface. Migration plugins are managed by
* the \Drupal\migrate\Plugin\MigrationPluginManager class.
*
* @section sec_source Source plugins
* Migration source plugins implement
@ -61,7 +58,9 @@ use Drupal\migrate\Row;
* with \Drupal\migrate\Annotation\MigrateProcessPlugin annotation, and must be
* in namespace subdirectory Plugin\migrate\process under the namespace of the
* module that defines them. Migration process plugins are managed by the
* \Drupal\migrate\Plugin\MigratePluginManager class.
* \Drupal\migrate\Plugin\MigratePluginManager class. The Migrate module
* provides process plugins for common operations (setting default values,
* mapping values, etc.).
*
* @section sec_destination Destination plugins
* Migration destination plugins implement
@ -70,34 +69,12 @@ use Drupal\migrate\Row;
* annotated with \Drupal\migrate\Annotation\MigrateDestination annotation, and
* must be in namespace subdirectory Plugin\migrate\destination under the
* namespace of the module that defines them. Migration destination plugins
* are managed by the
* \Drupal\migrate\Plugin\MigrateDestinationPluginManager class.
* are managed by the \Drupal\migrate\Plugin\MigrateDestinationPluginManager
* class. The Migrate module provides destination plugins for Drupal core
* objects (configuration and entity).
*
* @section sec_entity Migration configuration entities
* The definition of how to migrate each type of data is stored in configuration
* entities. The migration configuration entity class is
* \Drupal\migrate\Entity\Migration, with interface
* \Drupal\migrate\Entity\MigrationInterface; the configuration schema can be
* found in the migrate.schema.yml file. Migration configuration consists of IDs
* and configuration for the source, process, and destination plugins, as well
* as information on dependencies. Process configuration consists of sections,
* each of which defines the series of process plugins needed for one
* destination property. You can find examples of migration configuration files
* in the core/modules/migrate_drupal/config/install directory.
*
* @section sec_manifest Migration manifests
* You can run a migration with the "drush migrate-manifest" command, providing
* a migration manifest file. This file lists the configuration names of the
* migrations you want to execute, as well as any dependencies they have (you
* can find these in the "migration_dependencies" sections of the individual
* configuration files). For example, to migrate blocks from a Drupal 6 site,
* you would list:
* @code
* # Migrate blocks from Drupal 6 to 8
* - d6_filter_format
* - d6_custom_block
* - d6_block
* @endcode
* @section sec_more_info More information
* @link https://www.drupal.org/node/2127611 Migration API documentation. @endlink
*
* @see update_api
* @}

View file

@ -662,7 +662,7 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
* {@inheritdoc}
*/
public function getMigrationDependencies() {
return $this->migration_dependencies + ['required' => [], 'optional' => []];
return ($this->migration_dependencies ?: []) + ['required' => [], 'optional' => []];
}
/**
@ -703,14 +703,14 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
* {@inheritdoc}
*/
public function getTrackLastImported() {
$this->trackLastImported;
return $this->trackLastImported;
}
/**
* {@inheritdoc}
*/
public function getDestinationIds() {
$this->destinationIds;
return $this->destinationIds;
}
}

View file

@ -88,6 +88,21 @@ abstract class Entity extends DestinationBase implements ContainerFactoryPluginI
return substr($plugin_id, 7);
}
/**
* Gets the bundle for the row taking into account the default.
*
* @param \Drupal\migrate\Row $row
* The current row we're importing.
*
* @return string
* The bundle for this row.
*/
public function getBundle(Row $row) {
$default_bundle = isset($this->configuration['default_bundle']) ? $this->configuration['default_bundle'] : '';
$bundle_key = $this->getKey('bundle');
return $row->getDestinationProperty($bundle_key) ?: $default_bundle;
}
/**
* {@inheritdoc}
*/
@ -112,6 +127,11 @@ abstract class Entity extends DestinationBase implements ContainerFactoryPluginI
$this->updateEntity($entity, $row);
}
else {
// Attempt to ensure we always have a bundle.
if ($bundle = $this->getBundle($row)) {
$row->setDestinationProperty($this->getKey('bundle'), $bundle);
}
// Stubs might need some required fields filled in.
if ($row->isStub()) {
$this->processStubRow($row);

View file

@ -143,7 +143,7 @@ abstract class SourcePluginBase extends PluginBase implements MigrateSourceInter
// Set up some defaults based on the source configuration.
$this->cacheCounts = !empty($configuration['cache_counts']);
$this->skipCount = !empty($configuration['skip_count']);
$this->cacheKey = !empty($configuration['cache_key']) ? !empty($configuration['cache_key']) : NULL;
$this->cacheKey = !empty($configuration['cache_key']) ? $configuration['cache_key'] : NULL;
$this->trackChanges = !empty($configuration['track_changes']) ? $configuration['track_changes'] : FALSE;
$this->idMap = $this->migration->getIdMap();

View file

@ -1,10 +0,0 @@
id: node_template
label: Template test - node
migration_tags:
- Template Test
source:
plugin: empty
process:
src: barfoo
destination:
plugin: entity:node

View file

@ -1,10 +0,0 @@
id: other_template
label: Template with a different tag
migration_tags:
- Different Template Test
source:
plugin: empty
process:
src: raboof
destination:
plugin: entity:user

View file

@ -1,10 +0,0 @@
id: url_template
label: Template test - URL
migration_tags:
- Template Test
source:
plugin: empty
process:
src: foobar
destination:
plugin: url_alias

View file

@ -1,5 +0,0 @@
name: 'Migration template test'
type: module
package: Testing
version: VERSION
core: 8.x

View file

@ -0,0 +1,155 @@
<?php
namespace Drupal\Tests\migrate\Kernel;
use Drupal\migrate\MigrateExecutable;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests setting of bundles on content entity migrations.
*
* @group migrate
*/
class MigrateBundleTest extends MigrateTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['taxonomy', 'text'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('taxonomy_vocabulary');
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['taxonomy']);
// Set up two vocabularies (taxonomy bundles).
Vocabulary::create(['vid' => 'tags', 'name' => 'Tags']);
Vocabulary::create(['vid' => 'categories', 'name' => 'Categories']);
}
/**
* Tests setting the bundle in the destination.
*/
public function testDestinationBundle() {
$term_data_rows = [
['id' => 1, 'name' => 'Category 1'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'id' => 'terms',
'migration_tags' => ['Bundle test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $term_data_rows,
'ids' => $ids,
],
'process' => [
'tid' => 'id',
'name' => 'name',
],
'destination' => [
'plugin' => 'entity:taxonomy_term',
'default_bundle' => 'categories',
],
'migration_dependencies' => [],
];
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
// Import and validate the term entity was created with the correct bundle.
$term_executable = new MigrateExecutable($term_migration, $this);
$term_executable->import();
/** @var Term $term */
$term = Term::load(1);
$this->assertEquals($term->bundle(), 'categories');
}
/**
* Tests setting the bundle in the process pipeline.
*/
public function testProcessBundle() {
$term_data_rows = [
['id' => 1, 'vocab' => 'categories', 'name' => 'Category 1'],
['id' => 2, 'vocab' => 'tags', 'name' => 'Tag 1'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'id' => 'terms',
'migration_tags' => ['Bundle test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $term_data_rows,
'ids' => $ids,
],
'process' => [
'tid' => 'id',
'vid' => 'vocab',
'name' => 'name',
],
'destination' => [
'plugin' => 'entity:taxonomy_term',
],
'migration_dependencies' => [],
];
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
// Import and validate the term entities were created with the correct bundle.
$term_executable = new MigrateExecutable($term_migration, $this);
$term_executable->import();
/** @var Term $term */
$term = Term::load(1);
$this->assertEquals($term->bundle(), 'categories');
$term = Term::load(2);
$this->assertEquals($term->bundle(), 'tags');
}
/**
* Tests setting bundles both in process and destination.
*/
public function testMixedBundles() {
$term_data_rows = [
['id' => 1, 'vocab' => 'categories', 'name' => 'Category 1'],
['id' => 2, 'name' => 'Tag 1'],
];
$ids = ['id' => ['type' => 'integer']];
$definition = [
'id' => 'terms',
'migration_tags' => ['Bundle test'],
'source' => [
'plugin' => 'embedded_data',
'data_rows' => $term_data_rows,
'ids' => $ids,
],
'process' => [
'tid' => 'id',
'vid' => 'vocab',
'name' => 'name',
],
'destination' => [
'plugin' => 'entity:taxonomy_term',
// When no vocab is provided, the destination bundle is applied.
'default_bundle' => 'tags',
],
'migration_dependencies' => [],
];
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
// Import and validate the term entities were created with the correct bundle.
$term_executable = new MigrateExecutable($term_migration, $this);
$term_executable->import();
/** @var Term $term */
$term = Term::load(1);
$this->assertEquals($term->bundle(), 'categories');
$term = Term::load(2);
$this->assertEquals($term->bundle(), 'tags');
}
}

View file

@ -27,4 +27,41 @@ class MigrationTest extends KernelTestBase {
$this->assertEquals([], $migration->getProcessPlugins([]));
}
/**
* Tests Migration::getMigrationDependencies()
*
* @covers ::getMigrationDependencies
*/
public function testGetMigrationDependencies() {
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([
'migration_dependencies' => NULL
]);
$this->assertNotEmpty($migration->getMigrationDependencies(), 'Migration dependencies is not empty');
}
/**
* Tests Migration::getDestinationIds()
*
* @covers ::getDestinationIds
*/
public function testGetDestinationIds() {
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration(['destinationIds' => ['foo' => 'bar']]);
$destination_ids = $migration->getDestinationIds();
$this->assertNotEmpty($destination_ids, 'Destination ids are not empty');
$this->assertEquals(['foo' => 'bar'], $destination_ids, 'Destination ids match the expected values.');
}
/**
* Tests Migration::getTrackLastImported()
*
* @covers ::getTrackLastImported
* @covers ::isTrackLastImported
*/
public function testGetTrackLastImported() {
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([]);
$migration->setTrackLastImported(TRUE);
$this->assertEquals(TRUE, $migration->getTrackLastImported());
$this->assertEquals(TRUE, $migration->isTrackLastImported());
}
}

View file

@ -170,6 +170,25 @@ class MigrateSourceTest extends MigrateTestCase {
$this->assertEquals(-1, $source->count());
}
/**
* Test that the key can be set for the count cache.
*
* @covers ::count
*/
public function testCountCacheKey() {
// Mock the cache to validate set() receives appropriate arguments.
$container = new ContainerBuilder();
$cache = $this->getMock(CacheBackendInterface::class);
$cache->expects($this->any())->method('set')
->with('test_key', $this->isType('int'), $this->isType('int'));
$container->set('cache.migrate', $cache);
\Drupal::setContainer($container);
// Test caching the count with a configured key works.
$source = $this->getSource(['cache_counts' => TRUE, 'cache_key' => 'test_key']);
$this->assertEquals(1, $source->count());
}
/**
* Test that we don't get a row if prepareRow() is false.
*/

View file

@ -199,9 +199,11 @@ abstract class MigrateTestCase extends UnitTestCase {
*/
protected function retrievalAssertHelper($expected_value, $actual_value, $message) {
if (is_array($expected_value)) {
foreach ($expected_value as $k => $v) {
$this->retrievalAssertHelper($v, $actual_value[$k], $message . '[' . $k . ']');
// If the expected and actual values are empty, no need to array compare.
if (empty($expected_value && $actual_value)) {
return;
}
$this->assertArrayEquals($expected_value, $actual_value, $message);
}
else {
$this->assertSame((string) $expected_value, (string) $actual_value, $message);