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

This commit is contained in:
Pantheon Automation 2016-06-02 15:56:09 -07:00 committed by Greg Anderson
parent 9eae24d844
commit 28556d630e
1322 changed files with 6699 additions and 2064 deletions

View file

@ -44,4 +44,5 @@ class MigrateProcessPlugin extends Plugin {
* @var bool (optional)
*/
public $handle_multiples = FALSE;
}

View file

@ -18,4 +18,5 @@ interface MigrateBuildDependencyInterface {
* An array of migrations.
*/
public function buildDependencyMigration(array $migrations, array $dynamic_ids);
}

View file

@ -378,6 +378,7 @@ class MigrateExecutable implements MigrateExecutableInterface {
$new_value[] = $plugin->transform($scalar_value, $this, $row, $destination);
}
catch (MigrateSkipProcessException $e) {
$new_value[] = NULL;
$break = TRUE;
}
}
@ -391,6 +392,7 @@ class MigrateExecutable implements MigrateExecutableInterface {
$value = $plugin->transform($value, $this, $row, $destination);
}
catch (MigrateSkipProcessException $e) {
$value = NULL;
break;
}
$multiple = $multiple || $plugin->multiple();

View file

@ -14,4 +14,5 @@ interface MigrateMessageInterface {
* The type of message, for example: status or warning.
*/
public function display($message, $type = 'status');
}

View file

@ -209,10 +209,32 @@ interface MigrateIdMapInterface extends \Iterator, PluginInspectionInterface {
* The source identifier keyed values of the record, e.g. ['nid' => 5].
*
* @return array
* The destination identifier values of the record, or NULL on failure.
* The destination identifier values of the record, or empty on failure.
*
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use
* lookupDestinationIds() instead.
*/
public function lookupDestinationId(array $source_id_values);
/**
* Looks up the destination identifiers corresponding to a source key.
*
* This can look up a subset of source keys if only some are provided, and
* will return all destination keys that match.
*
* @param array $source_id_values
* The source identifier keyed values of the records, e.g. ['nid' => 5].
* If unkeyed, the first count($source_id_values) keys will be assumed.
*
* @return array
* An array of arrays of destination identifier values.
*
* @throws \Drupal\migrate\MigrateException
* Thrown when $source_id_values contains unknown keys, or is the wrong
* length.
*/
public function lookupDestinationIds(array $source_id_values);
/**
* Looks up the destination identifier currently being iterated.
*

View file

@ -217,7 +217,7 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
protected $migrationPluginManager;
/**
* The source plugin manager.
* The source plugin manager.
*
* @var \Drupal\migrate\Plugin\MigratePluginManager
*/
@ -712,4 +712,5 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
public function getDestinationIds() {
$this->destinationIds;
}
}

View file

@ -279,7 +279,7 @@ interface MigrationInterface extends PluginInspectionInterface, DerivativeInspec
* @return $this
* The migration entity.
*
* @see Drupal\migrate_drupal\Plugin\migrate\load\LoadEntity::processLinkField().
* @see Drupal\migrate_drupal\Plugin\migrate\load\LoadEntity::processLinkField()
*/
public function mergeProcessOfProperty($property, array $process_of_property);

View file

@ -157,6 +157,7 @@ class MigrationPluginManager extends DefaultPluginManager implements MigrationPl
// current migration.
$dependency_graph = [];
$required_dependency_graph = [];
$have_optional = FALSE;
foreach ($migrations as $migration) {
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$id = $migration->id();
@ -172,14 +173,15 @@ class MigrationPluginManager extends DefaultPluginManager implements MigrationPl
$this->addDependency($dependency_graph, $id, $dependency, $dynamic_ids);
}
}
if (isset($migration_dependencies['optional'])) {
if (!empty($migration_dependencies['optional'])) {
foreach ($migration_dependencies['optional'] as $dependency) {
$this->addDependency($dependency_graph, $id, $dependency, $dynamic_ids);
}
$have_optional = TRUE;
}
}
$dependency_graph = (new Graph($dependency_graph))->searchAndSort();
if (!empty($migration_dependencies['optional'])) {
if ($have_optional) {
$required_dependency_graph = (new Graph($required_dependency_graph))->searchAndSort();
}
else {

View file

@ -517,16 +517,56 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP
* {@inheritdoc}
*/
public function lookupDestinationId(array $source_id_values) {
$results = $this->lookupDestinationIds($source_id_values);
return $results ? reset($results) : array();
}
/**
* {@inheritdoc}
*/
public function lookupDestinationIds(array $source_id_values) {
if (empty($source_id_values)) {
return array();
}
$query = $this->getDatabase()->select($this->mapTableName(), 'map')
->fields('map', $this->destinationIdFields());
$query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
$result = $query->execute();
$destination_id = $result->fetchAssoc();
return array_values($destination_id ?: array());
// Canonicalize the keys into a hash of DB-field => value.
$is_associative = !isset($source_id_values[0]);
$conditions = [];
foreach ($this->sourceIdFields() as $field_name => $db_field) {
if ($is_associative) {
// Associative $source_id_values can have fields out of order.
if (isset($source_id_values[$field_name])) {
$conditions[$db_field] = $source_id_values[$field_name];
unset($source_id_values[$field_name]);
}
}
else {
// For non-associative $source_id_values, we assume they're the first
// few fields.
if (empty($source_id_values)) {
break;
}
$conditions[$db_field] = array_shift($source_id_values);
}
}
if (!empty($source_id_values)) {
throw new MigrateException("Extra unknown items in source IDs");
}
$query = $this->getDatabase()->select($this->mapTableName(), 'map')
->fields('map', $this->destinationIdFields());
if (count($this->sourceIdFields()) === count($conditions)) {
// Optimization: Use the primary key.
$query->condition(self::SOURCE_IDS_HASH, $this->getSourceIDsHash(array_values($conditions)));
}
else {
foreach ($conditions as $db_field => $value) {
$query->condition($db_field, $value);
}
}
return $query->execute()->fetchAll(\PDO::FETCH_NUM);
}
/**

View file

@ -335,4 +335,5 @@ class Row {
public function isStub() {
return $this->isStub;
}
}

View file

@ -47,4 +47,5 @@ class RequirementsExceptionTest extends UnitTestCase {
),
);
}
}

View file

@ -430,6 +430,123 @@ class MigrateSqlIdMapTest extends MigrateTestCase {
$this->assertSame(0, count($destination_id));
}
/**
* Setup a database with the given rows.
*
* @param array $source_keys
* The source keys for the ID map table.
* @param array $dest_keys
* The destination keys for the ID map table.
* @param array $rows
* An array of source and destination value arrays for the ID map table.
*
* @return \Drupal\Tests\migrate\Unit\TestSqlIdMap
* An ID map instance for testing.
*/
protected function setupRows($source_keys, $dest_keys, $rows) {
$this->database = $this->getDatabase([]);
$this->sourceIds = array_fill_keys($source_keys, []);
$this->destinationIds = array_fill_keys($dest_keys, []);
$db_keys = [];
foreach (array_keys($source_keys) as $i) {
$db_keys[] = 'sourceid' . ($i + 1);
}
foreach (array_keys($dest_keys) as $i) {
$db_keys[] = 'destid' . ($i + 1);
}
foreach ($rows as $row) {
$values = array_combine($db_keys, $row);
$source_values = array_slice($row, 0, count($source_keys));
$values['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash($source_values);
$this->saveMap($values);
}
return $this->getIdMap();
}
/**
* Tests lookupDestinationIds().
*/
public function testLookupDestinationIds() {
// Simple map with one source and one destination ID.
$id_map = $this->setupRows(['nid'], ['nid'], [
[1, 101],
[2, 102],
[3, 103],
]);
// Lookup nothing, gives nothing.
$this->assertEquals([], $id_map->lookupDestinationIds([]));
// Lookup by complete non-associative list.
$this->assertEquals([[101]], $id_map->lookupDestinationIds([1]));
$this->assertEquals([[102]], $id_map->lookupDestinationIds([2]));
$this->assertEquals([], $id_map->lookupDestinationIds([99]));
// Lookup by complete associative list.
$this->assertEquals([[101]], $id_map->lookupDestinationIds(['nid' => 1]));
$this->assertEquals([[102]], $id_map->lookupDestinationIds(['nid' => 2]));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99]));
// Map with multiple source and destination IDs.
$id_map = $this->setupRows(['nid', 'language'], ['nid', 'langcode'], [
[1, 'en', 101, 'en'],
[1, 'fr', 101, 'fr'],
[1, 'de', 101, 'de'],
[2, 'en', 102, 'en'],
]);
// Lookup nothing, gives nothing.
$this->assertEquals([], $id_map->lookupDestinationIds([]));
// Lookup by complete non-associative list.
$this->assertEquals([[101, 'en']], $id_map->lookupDestinationIds([1, 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds([1, 'fr']));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds([2, 'en']));
$this->assertEquals([], $id_map->lookupDestinationIds([2, 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds([99, 'en']));
// Lookup by complete associative list.
$this->assertEquals([[101, 'en']], $id_map->lookupDestinationIds(['nid' => 1, 'language' => 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds(['nid' => 1, 'language' => 'fr']));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds(['nid' => 2, 'language' => 'en']));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 2, 'language' => 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99, 'language' => 'en']));
// Lookup by partial non-associative list.
$this->assertEquals([[101, 'en'], [101, 'fr'], [101, 'de']], $id_map->lookupDestinationIds([1]));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds([2]));
$this->assertEquals([], $id_map->lookupDestinationIds([99]));
// Lookup by partial associative list.
$this->assertEquals([[101, 'en'], [101, 'fr'], [101, 'de']], $id_map->lookupDestinationIds(['nid' => 1]));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds(['nid' => 2]));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99]));
// Out-of-order partial associative list.
$this->assertEquals([[101, 'en'], [102, 'en']], $id_map->lookupDestinationIds(['language' => 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds(['language' => 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds(['language' => 'zh']));
// Error conditions.
try {
$id_map->lookupDestinationIds([1, 2, 3]);
$this->fail('Too many source IDs should throw');
}
catch (MigrateException $e) {
$this->assertEquals("Extra unknown items in source IDs", $e->getMessage());
}
try {
$id_map->lookupDestinationIds(['nid' => 1, 'aaa' => '2']);
$this->fail('Unknown source ID key should throw');
}
catch (MigrateException $e) {
$this->assertEquals("Extra unknown items in source IDs", $e->getMessage());
}
// Verify that we are looking up by source_id_hash when all source IDs are
// passed in.
$id_map->getDatabase()->update($id_map->mapTableName())
->condition('sourceid1', 1)
->condition('sourceid2', 'en')
->fields([TestSqlIdMap::SOURCE_IDS_HASH => uniqid()])
->execute();
$this->assertNotEquals([[101, 'en']], $id_map->lookupDestinationIds([1, 'en']));
}
/**
* Tests the getRowByDestination method.
*/

View file

@ -0,0 +1,220 @@
<?php
namespace Drupal\Tests\migrate\Unit;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate\Plugin\MigrationPluginManager;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\migrate\Plugin\MigrationPluginManager
* @group migrate
*/
class MigrationPluginManagerTest extends UnitTestCase {
/**
* A plugin manager.
*
* @param \Drupal\migrate\Plugin\MigrationPluginManager $pluginManager
*/
protected $pluginManager;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Get a plugin manager for testing.
$module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->pluginManager = new MigrationPluginManager($module_handler, $cache_backend, $language_manager);
}
/**
* Tests building dependencies for multiple migrations.
*
* @dataProvider dependencyProvider
*/
public function testDependencyBuilding($migrations_data, $result_ids) {
$migrations = [];
foreach ($migrations_data as $migration_id => $migration_data) {
$migrations[$migration_id] = new TestMigrationMock($migration_id, $migration_data['dependencies']);
}
$ordered_migrations = $this->pluginManager->buildDependencyMigration($migrations, []);
// Verify results.
$this->assertEquals($result_ids, array_keys($ordered_migrations));
foreach ($migrations_data as $migration_id => $migration_data) {
$migration = $migrations[$migration_id];
$requirements = $migration_data['result_requirements'];
if (empty($requirements)) {
$this->assertEquals([], $migration->set);
}
else {
$requirements = array_combine($requirements, $requirements);
$this->assertEquals(1, count($migration->set));
list($set_prop, $set_requirements) = reset($migration->set);
$this->assertEquals('requirements', $set_prop);
$this->assertEquals($requirements, $set_requirements);
}
}
}
/**
* Provide dependency data for testing.
*/
public function dependencyProvider() {
return [
// Just one migration, with no dependencies.
[
[
'm1' => [
'dependencies' => [],
'result_requirements' => [],
],
],
['m1'],
],
// Just one migration, with required dependencies.
[
[
'm1' => [
'dependencies' => [
'required' => ['required1', 'required2'],
],
'result_requirements' => ['required1', 'required2'],
],
],
['m1'],
],
// Just one migration, with optional dependencies.
[
[
'm1' => [
'dependencies' => [
'optional' => ['optional1'],
],
'result_requirements' => [],
],
],
['m1'],
],
// Multiple migrations.
[
[
'm1' => [
'dependencies' => [
'required' => ['required1', 'required2'],
],
'result_requirements' => ['required1', 'required2'],
],
'm2' => [
'dependencies' => [
'optional' => ['optional1'],
],
'result_requirements' => [],
],
],
['m1', 'm2'],
],
// Multiple migrations, reordered due to optional requirement.
[
[
'm1' => [
'dependencies' => [
'optional' => ['m2'],
],
'result_requirements' => [],
],
'm2' => [
'dependencies' => [
'optional' => ['optional1'],
],
'result_requirements' => [],
],
],
['m2', 'm1'],
],
// Ensure that optional requirements aren't turned into required ones,
// if the last migration has no optional deps.
[
[
'm1' => [
'dependencies' => [
'optional' => ['m2'],
],
'result_requirements' => [],
],
'm2' => [
'dependencies' => [],
'result_requirements' => [],
],
],
['m2', 'm1'],
],
];
}
}
/**
* A mock migration plugin.
*
* Why are we using a custom class here?
*
* 1. The function buildDependencyMigration() calls $migration->set(), which
* is not actually in MigrationInterface.
*
* 2. The function buildDependencyMigration() calls array_multisort on an
* array with mocks in it. PHPUnit mocks are really complex, and if PHP tries
* to compare them it will die with "Nesting level too deep".
*/
class TestMigrationMock extends Migration {
/**
* The values passed into set().
*
* @var array $set
*/
public $set = [];
/**
* TestMigrationMock constructor.
*/
public function __construct($id, $dependencies) {
// Intentionally ignore parent constructor.
$this->id = $id;
$this->dependencies = $dependencies;
}
/**
* {@inheritdoc}
*/
public function id() {
return $this->id;
}
/**
* {@inheritdoc}
*/
public function getMigrationDependencies() {
return $this->dependencies;
}
/**
* {@inheritdoc}
*/
public function set($prop, $value) {
$this->set[] = func_get_args();
}
}

View file

@ -115,4 +115,5 @@ class EntityTestDestination extends EntityContentBase {
protected function getEntity(Row $row, array $old_destination_id_values) {
return $this->entity;
}
}

View file

@ -63,4 +63,5 @@ class TestPerComponentEntityDisplay extends ComponentEntityDisplayBase {
public function getTestValues() {
return $this->testValues;
}
}

View file

@ -63,4 +63,5 @@ class TestPerComponentEntityFormDisplay extends PerComponentEntityFormDisplay {
public function getTestValues() {
return $this->testValues;
}
}

View file

@ -49,6 +49,7 @@ class ConcatTest extends MigrateProcessTestCase {
$value = $this->plugin->transform(array('foo', 'bar'), $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame($value, 'foo_bar');
}
}
class TestConcat extends Concat {

View file

@ -160,4 +160,5 @@ class DedupeEntityTest extends MigrateProcessTestCase {
->method('execute')
->will($this->returnCallback(function () use (&$count) { return $count--;}));
}
}

View file

@ -114,4 +114,5 @@ class TestGet extends Get {
public function setSource($source) {
$this->configuration['source'] = $source;
}
}

View file

@ -78,4 +78,5 @@ class IteratorTest extends MigrateTestCase {
$this->assertSame($new_value[42]['foo'], 'test');
$this->assertSame($new_value[42]['id'], 42);
}
}

View file

@ -44,7 +44,7 @@ class MachineNameTest extends MigrateProcessTestCase {
// - Uppercase -> lowercase,
// - Multiple consecutive underscore -> single underscore.
$human_name_ascii = 'foo2, the.bar;2*&the%baz!YEE____HaW ';
$human_name = $human_name_ascii .'áéő';
$human_name = $human_name_ascii . 'áéő';
$expected_result = 'foo2_the_bar_2_the_baz_yee_haw_aeo';
// Test for calling transliterate on mock object.
$this->transliteration