Update to Drupal 8.0-dev-2015-11-17. Commits through da81cd220, Tue Nov 17 15:53:49 2015 +0000, Issue #2617224 by Wim Leers: Move around/fix some documentation.

This commit is contained in:
Pantheon Automation 2015-11-17 13:42:33 -08:00 committed by Greg Anderson
parent 4afb23bbd3
commit 7784f4c23d
929 changed files with 19798 additions and 5304 deletions

View file

@ -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.

View file

@ -44,10 +44,14 @@ interface MigrateSourceInterface extends \Countable, \Iterator, PluginInspection
public function __toString();
/**
* Get the source ids.
* Defines the source fields uniquely identifying a source row. None of these
* fields should contain a NULL value - if necessary, use prepareRow() or
* hook_migrate_prepare_row() to rewrite NULL values to appropriate empty
* values (such as '' or 0).
*
* @return array
* The source ids.
* Array keyed by source field name, with values being a schema array
* describing the field (such as ['type' => 'string]).
*/
public function getIds();

View file

@ -136,19 +136,6 @@ abstract class Entity extends DestinationBase implements ContainerFactoryPluginI
return $row->getDestinationProperty($this->getKey('id'));
}
/**
* 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.
*

View file

@ -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;
@ -29,6 +34,13 @@ class EntityContentBase extends Entity {
*/
protected $entityManager;
/**
* Field type plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypeManager;
/**
* Constructs a content entity.
*
@ -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')
);
}
@ -74,6 +90,9 @@ class EntityContentBase extends Entity {
public function import(Row $row, array $old_destination_id_values = array()) {
$this->rollbackAction = MigrateIdMapInterface::ROLLBACK_DELETE;
$entity = $this->getEntity($row, $old_destination_id_values);
if (!$entity) {
throw new MigrateException('Unable to get entity');
}
return $this->save($entity, $old_destination_id_values);
}
@ -132,4 +151,47 @@ 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))) {
if (empty($this->bundles)) {
throw new MigrateException('Stubbing failed, no bundles available for entity type: ' . $this->storage->getEntityTypeId());
}
$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);
}
}
}
}

View file

@ -287,6 +287,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP
foreach ($this->migration->getSourcePlugin()->getIds() as $id_definition) {
$mapkey = 'sourceid' . $count++;
$source_id_schema[$mapkey] = $this->getFieldSchema($id_definition);
$source_id_schema[$mapkey]['not null'] = TRUE;
// With InnoDB, utf8mb4-based primary keys can't be over 191 characters.
// Use ASCII-based primary keys instead.

View file

@ -136,6 +136,7 @@ abstract class SourcePluginBase extends PluginBase implements MigrateSourceInter
$this->skipCount = !empty($configuration['skip_count']);
$this->cacheKey = !empty($configuration['cache_key']) ? !empty($configuration['cache_key']) : NULL;
$this->trackChanges = !empty($configuration['track_changes']) ? $configuration['track_changes'] : FALSE;
$this->idMap = $this->migration->getIdMap();
// Pull out the current highwater mark if we have a highwater property.
if ($this->highWaterProperty = $this->migration->get('highWaterProperty')) {
@ -256,7 +257,6 @@ abstract class SourcePluginBase extends PluginBase implements MigrateSourceInter
* source records.
*/
public function rewind() {
$this->idMap = $this->migration->getIdMap();
$this->getIterator()->rewind();
$this->next();
}

View file

@ -73,7 +73,7 @@ class Row {
*
* @see getRawDestination()
*/
protected $rawDestination;
protected $rawDestination = [];
/**
* TRUE when this row is a stub.
@ -222,6 +222,17 @@ class Row {
NestedArray::setValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property), $value, TRUE);
}
/**
* Removes destination property.
*
* @param string $property
* The name of the destination property.
*/
public function removeDestinationProperty($property) {
unset($this->rawDestination[$property]);
NestedArray::unsetValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property));
}
/**
* Returns the whole destination array.
*

View file

@ -9,8 +9,12 @@ namespace Drupal\Tests\migrate\Unit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Row;
/**
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
@ -128,6 +132,7 @@ class MigrateSourceTest extends MigrateTestCase {
}
/**
* @covers ::__construct
* @expectedException \Drupal\migrate\MigrateException
*/
public function testHighwaterTrackChangesIncompatible() {
@ -138,6 +143,8 @@ class MigrateSourceTest extends MigrateTestCase {
/**
* Test that the source count is correct.
*
* @covers ::count
*/
public function testCount() {
// Mock the cache to validate set() receives appropriate arguments.
@ -221,6 +228,144 @@ class MigrateSourceTest extends MigrateTestCase {
$this->assertTrue(is_a($source->current(), 'Drupal\migrate\Row'), 'Incoming row timestamp is greater than current highwater mark so we have a row.');
}
/**
* Test basic row preparation.
*
* @covers ::prepareRow
*/
public function testPrepareRow() {
$this->migrationConfiguration['id'] = 'test_migration';
// Get a new migration with an id.
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
$row = new Row([], []);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$source->setModuleHandler($module_handler->reveal());
// Ensure we don't log this to the mapping table.
$this->idMap->expects($this->never())
->method('saveIdMapping');
$this->assertTrue($source->prepareRow($row));
// Track_changes...
$source = new StubSourcePlugin(['track_changes' => TRUE], '', [], $migration);
$row2 = $this->prophesize(Row::class);
$row2->rehash()
->shouldBeCalled();
$module_handler->invokeAll('migrate_prepare_row', [$row2, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row2, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$source->setModuleHandler($module_handler->reveal());
$this->assertTrue($source->prepareRow($row2->reveal()));
}
/**
* Test that global prepare hooks can skip rows.
*
* @covers ::prepareRow
*/
public function testPrepareRowGlobalPrepareSkip() {
$this->migrationConfiguration['id'] = 'test_migration';
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
$row = new Row([], []);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, FALSE, TRUE])
->shouldBeCalled();
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$source->setModuleHandler($module_handler->reveal());
$this->idMap->expects($this->once())
->method('saveIdMapping')
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
$this->assertFalse($source->prepareRow($row));
}
/**
* Test that migrate specific prepare hooks can skip rows.
*
* @covers ::prepareRow
*/
public function testPrepareRowMigratePrepareSkip() {
$this->migrationConfiguration['id'] = 'test_migration';
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
$row = new Row([], []);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, FALSE, TRUE])
->shouldBeCalled();
$source->setModuleHandler($module_handler->reveal());
$this->idMap->expects($this->once())
->method('saveIdMapping')
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
$this->assertFalse($source->prepareRow($row));
}
/**
* Test that a skip exception during prepare hooks correctly skips.
*
* @covers ::prepareRow
*/
public function testPrepareRowPrepareException() {
$this->migrationConfiguration['id'] = 'test_migration';
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
$row = new Row([], []);
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
->willReturn([TRUE, TRUE])
->shouldBeCalled();
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
->willThrow(new MigrateSkipRowException())
->shouldBeCalled();
$source->setModuleHandler($module_handler->reveal());
// This will only be called on the first prepare because the second
// explicitly avoids it.
$this->idMap->expects($this->once())
->method('saveIdMapping')
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
$this->assertFalse($source->prepareRow($row));
// Throw an exception the second time that avoids mapping.
$e = new MigrateSkipRowException('', FALSE);
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
->willThrow($e)
->shouldBeCalled();
$this->assertFalse($source->prepareRow($row));
}
/**
* Get a mock executable for the test.
*
@ -239,3 +384,46 @@ class MigrateSourceTest extends MigrateTestCase {
}
}
/**
* Stubbed source plugin for testing base class implementations.
*/
class StubSourcePlugin extends SourcePluginBase {
/**
* Helper for setting internal module handler implementation.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
*/
function setModuleHandler(ModuleHandlerInterface $module_handler) {
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public function fields() {
return [];
}
/**
* {@inheritdoc}
*/
public function __toString() {
return '';
}
/**
* {@inheritdoc}
*/
public function getIds() {
return [];
}
/**
* {@inheritdoc}
*/
protected function initializeIterator() {
return [];
}
}

View file

@ -19,6 +19,11 @@ abstract class MigrateTestCase extends UnitTestCase {
protected $migrationConfiguration = [];
/**
* @var \Drupal\migrate\Plugin\MigrateIdMapInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $idMap;
/**
* Local store for mocking setStatus()/getStatus().
*

View file

@ -0,0 +1,118 @@
<?php
/**
* @file
* Contains \Drupal\Tests\migrate\Unit\Plugin\migrate\destination\EntityContentBaseTest
*/
namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Row;
use Drupal\Tests\UnitTestCase;
/**
* Tests base entity migration destination functionality.
*
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
* @group migrate
*/
class EntityContentBaseTest extends UnitTestCase {
/**
* @var \Drupal\migrate\Entity\MigrationInterface
*/
protected $migration;
/**
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->migration = $this->prophesize(MigrationInterface::class);
$this->storage = $this->prophesize(EntityStorageInterface::class);
$this->entityManager = $this->prophesize(EntityManagerInterface::class);
}
/**
* Test basic entity save.
*
* @covers ::import
*/
public function testImport() {
$bundles = [];
$destination = new EntityTestDestination([], '', [],
$this->migration->reveal(),
$this->storage->reveal(),
$bundles,
$this->entityManager->reveal(),
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal());
$entity = $this->prophesize(ContentEntityInterface::class);
// Assert that save is called.
$entity->save()
->shouldBeCalledTimes(1);
// Set an id for the entity
$entity->id()
->willReturn(5);
$destination->setEntity($entity->reveal());
// Ensure the id is saved entity id is returned from import.
$this->assertEquals([5], $destination->import(new Row([], [])));
// Assert that import set the rollback action.
$this->assertEquals(MigrateIdMapInterface::ROLLBACK_DELETE, $destination->rollbackAction());
}
/**
* Test row skipping when we can't get an entity to save.
*
* @covers ::import
* @expectedException \Drupal\migrate\MigrateException
* @expectedExceptionMessage Unable to get entity
*/
public function testImportEntityLoadFailure() {
$bundles = [];
$destination = new EntityTestDestination([], '', [],
$this->migration->reveal(),
$this->storage->reveal(),
$bundles,
$this->entityManager->reveal(),
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal());
$destination->setEntity(FALSE);
$destination->import(new Row([], []));
}
}
/**
* Stub class for testing EntityContentBase methods.
*
* We want to test things without testing the base class implementations.
*/
class EntityTestDestination extends EntityContentBase {
private $entity = NULL;
public function setEntity($entity) {
$this->entity = $entity;
}
protected function getEntity(Row $row, array $old_destination_id_values) {
return $this->entity;
}
}

View file

@ -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()
);
}
}