Migrate in core #2125717 by mikeryan, chx, marvil07, bdone, jessehs, mpgeek, BTMash, fastangel, mongolito404, Moshe Weitzman, eliza411, YesCT, dawehner, cosmicdreams
parent
40e903f897
commit
67f30bc0a1
|
@ -0,0 +1,13 @@
|
|||
id: d6_system_cron
|
||||
source:
|
||||
plugin: drupal6_variable
|
||||
variables:
|
||||
- cron_threshold_warning
|
||||
- cron_threshold_error
|
||||
- cron_last
|
||||
process:
|
||||
'threshold:warning': cron_threshold_warning
|
||||
'threshold:error': cron_threshold_error
|
||||
destination:
|
||||
plugin: d8_config
|
||||
config_name: system.cron
|
|
@ -0,0 +1,10 @@
|
|||
id: d6_system_rss
|
||||
source:
|
||||
plugin: drupal6_variable
|
||||
variables:
|
||||
- feed_default_items
|
||||
process:
|
||||
'items:limit': feed_default_items
|
||||
destination:
|
||||
plugin: d8_config
|
||||
config_name: system.rss
|
|
@ -0,0 +1,24 @@
|
|||
id: d6_system_site
|
||||
source:
|
||||
plugin: drupal6_variable
|
||||
variables:
|
||||
- site_name
|
||||
- site_mail
|
||||
- site_slogan
|
||||
- site_frontpage
|
||||
- site_403
|
||||
- site_404
|
||||
- drupal_weight_select_max
|
||||
- admin_compact_mode
|
||||
process:
|
||||
name: site_name
|
||||
mail: site_mail
|
||||
slogan: site_slogan
|
||||
'page:front': site_frontpage
|
||||
'page:403': site_403
|
||||
'page:404': site_404
|
||||
weight_select_max: drupal_weight_select_max
|
||||
admin_compact_mode: admin_compact_mode
|
||||
destination:
|
||||
plugin: d8_config
|
||||
config_name: system.site
|
|
@ -0,0 +1,288 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Entity\Migration.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Defines the Migration entity.
|
||||
*
|
||||
* The migration entity stores the information about a single migration, like
|
||||
* the source, process and destination plugins.
|
||||
*
|
||||
* @EntityType(
|
||||
* id = "migration",
|
||||
* label = @Translation("Migration"),
|
||||
* module = "migrate",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
|
||||
* "list" = "Drupal\Core\Config\Entity\DraggableListController",
|
||||
* "access" = "Drupal\Core\Entity\EntityAccessController",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\Core\Entity\EntityFormController",
|
||||
* "edit" = "Drupal\Core\Entity\EntityFormController",
|
||||
* "delete" = "Drupal\Core\Entity\EntityFormController"
|
||||
* }
|
||||
* },
|
||||
* config_prefix = "migrate.migration",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "weight" = "weight",
|
||||
* "uuid" = "uuid"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Migration extends ConfigEntityBase implements MigrationInterface {
|
||||
|
||||
/**
|
||||
* The migration ID (machine name).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The migration UUID.
|
||||
*
|
||||
* This is assigned automatically when the migration is created.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $uuid;
|
||||
|
||||
/**
|
||||
* The human-readable label for the migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The plugin ID for the row.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $row;
|
||||
|
||||
/**
|
||||
* The source configuration, with at least a 'plugin' key.
|
||||
*
|
||||
* Used to initialize the $sourcePlugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $source;
|
||||
|
||||
/**
|
||||
* The source plugin.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
*/
|
||||
protected $sourcePlugin;
|
||||
|
||||
/**
|
||||
* The configuration describing the process plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $process;
|
||||
|
||||
/**
|
||||
* The destination configuration, with at least a 'plugin' key.
|
||||
*
|
||||
* Used to initialize $destinationPlugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $destination;
|
||||
|
||||
/**
|
||||
* The destination plugin.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateDestinationInterface
|
||||
*/
|
||||
protected $destinationPlugin;
|
||||
|
||||
/**
|
||||
* The identifier map data.
|
||||
*
|
||||
* Used to initialize $idMapPlugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $idMap = array();
|
||||
|
||||
/**
|
||||
* The identifier map.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateIdMapInterface
|
||||
*/
|
||||
protected $idMapPlugin;
|
||||
|
||||
/**
|
||||
* The source identifiers.
|
||||
*
|
||||
* An array of source identifiers: the keys are the name of the properties,
|
||||
* the values are dependent on the ID map plugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $sourceIds = array();
|
||||
|
||||
/**
|
||||
* The destination identifiers.
|
||||
*
|
||||
* An array of destination identifiers: the keys are the name of the
|
||||
* properties, the values are dependent on the ID map plugin.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $destinationIds = array();
|
||||
|
||||
/**
|
||||
* Information on the highwater mark.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $highwaterProperty;
|
||||
|
||||
/**
|
||||
* Indicate whether the primary system of record for this migration is the
|
||||
* source, or the destination (Drupal). In the source case, migration of
|
||||
* an existing object will completely replace the Drupal object with data from
|
||||
* the source side. In the destination case, the existing Drupal object will
|
||||
* be loaded, then changes from the source applied; also, rollback will not be
|
||||
* supported.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $systemOfRecord = self::SOURCE;
|
||||
|
||||
/**
|
||||
* Specify value of needs_update for current map row. Usually set by
|
||||
* MigrateFieldHandler implementations.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $needsUpdate = MigrateIdMapInterface::STATUS_IMPORTED;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected $highwaterStorage;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $trackLastImported = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourcePlugin() {
|
||||
if (!isset($this->sourcePlugin)) {
|
||||
$this->sourcePlugin = \Drupal::service('plugin.manager.migrate.source')->createInstance($this->source['plugin'], $this->source, $this);
|
||||
}
|
||||
return $this->sourcePlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProcessPlugins(array $process = NULL) {
|
||||
if (!isset($process)) {
|
||||
$process = $this->process;
|
||||
}
|
||||
$process_plugins = array();
|
||||
foreach ($this->getProcessNormalized($process) as $property => $configurations) {
|
||||
$process_plugins[$property] = array();
|
||||
foreach ($configurations as $configuration) {
|
||||
if (isset($configuration['source'])) {
|
||||
$process_plugins[$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance('get', $configuration, $this);
|
||||
}
|
||||
// Get is already handled.
|
||||
if ($configuration['plugin'] != 'get') {
|
||||
$process_plugins[$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance($configuration['plugin'], $configuration, $this);
|
||||
}
|
||||
if (!$process_plugins[$property]) {
|
||||
throw new MigrateException("Invalid process configuration for $property");
|
||||
}
|
||||
}
|
||||
}
|
||||
return $process_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve shorthands into a list of plugin configurations.
|
||||
*
|
||||
* @param array $process
|
||||
* A process configuration array.
|
||||
*
|
||||
* @return array
|
||||
* The normalized process configuration.
|
||||
*/
|
||||
protected function getProcessNormalized(array $process) {
|
||||
$normalized_configurations = array();
|
||||
foreach ($process as $destination => $configuration) {
|
||||
if (is_string($configuration)) {
|
||||
$configuration = array(
|
||||
'plugin' => 'get',
|
||||
'source' => $configuration,
|
||||
);
|
||||
}
|
||||
if (isset($configuration['plugin'])) {
|
||||
$configuration = array($configuration);
|
||||
}
|
||||
$normalized_configurations[$destination] = $configuration;
|
||||
}
|
||||
return $normalized_configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDestinationPlugin() {
|
||||
if (!isset($this->destinationPlugin)) {
|
||||
$this->destinationPlugin = \Drupal::service('plugin.manager.migrate.destination')->createInstance($this->destination['plugin'], $this->destination, $this);
|
||||
}
|
||||
return $this->destinationPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdMap() {
|
||||
if (!isset($this->idMapPlugin)) {
|
||||
$configuration = $this->idMap;
|
||||
$plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql';
|
||||
$this->idMapPlugin = \Drupal::service('plugin.manager.migrate.id_map')->createInstance($plugin, $configuration, $this);
|
||||
}
|
||||
return $this->idMapPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected function getHighWaterStorage() {
|
||||
if (!isset($this->highwaterStorage)) {
|
||||
$this->highwaterStorage = \Drupal::keyValue('migrate:highwater');
|
||||
}
|
||||
return $this->highwaterStorage;
|
||||
}
|
||||
|
||||
public function getHighwater() {
|
||||
return $this->getHighWaterStorage()->get($this->id());
|
||||
}
|
||||
|
||||
public function saveHighwater($highwater) {
|
||||
$this->getHighWaterStorage()->set($this->id(), $highwater);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Entity\MigrationInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Interface for migrations.
|
||||
*/
|
||||
interface MigrationInterface extends ConfigEntityInterface {
|
||||
|
||||
const SOURCE = 'source';
|
||||
const DESTINATION = 'destination';
|
||||
|
||||
/**
|
||||
* Codes representing the current status of a migration, and stored in the
|
||||
* migrate_status table.
|
||||
*/
|
||||
const STATUS_IDLE = 0;
|
||||
const STATUS_IMPORTING = 1;
|
||||
const STATUS_ROLLING_BACK = 2;
|
||||
const STATUS_STOPPING = 3;
|
||||
const STATUS_DISABLED = 4;
|
||||
|
||||
/**
|
||||
* Message types to be passed to saveMessage() and saved in message tables.
|
||||
* MESSAGE_INFORMATIONAL represents a condition that did not prevent the
|
||||
* operation from succeeding - all others represent different severities of
|
||||
* conditions resulting in a source record not being imported.
|
||||
*/
|
||||
const MESSAGE_ERROR = 1;
|
||||
const MESSAGE_WARNING = 2;
|
||||
const MESSAGE_NOTICE = 3;
|
||||
const MESSAGE_INFORMATIONAL = 4;
|
||||
|
||||
/**
|
||||
* Codes representing the result of a rollback or import process.
|
||||
*/
|
||||
const RESULT_COMPLETED = 1; // All records have been processed
|
||||
const RESULT_INCOMPLETE = 2; // The process has interrupted itself (e.g., the
|
||||
// memory limit is approaching)
|
||||
const RESULT_STOPPED = 3; // The process was stopped externally (e.g., via
|
||||
// drush migrate-stop)
|
||||
const RESULT_FAILED = 4; // The process had a fatal error
|
||||
const RESULT_SKIPPED = 5; // Dependencies are unfulfilled - skip the process
|
||||
const RESULT_DISABLED = 6; // This migration is disabled, skipping
|
||||
|
||||
/**
|
||||
* Returns the initialized source plugin.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
*/
|
||||
public function getSourcePlugin();
|
||||
|
||||
/**
|
||||
* Returns the process plugins.
|
||||
*
|
||||
* @param array $process
|
||||
* A process configuration array.
|
||||
*
|
||||
* @return array
|
||||
* A list of process plugins.
|
||||
*/
|
||||
public function getProcessPlugins(array $process = NULL);
|
||||
|
||||
/**
|
||||
* Returns the initialized destination plugin.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateDestinationInterface
|
||||
*/
|
||||
public function getDestinationPlugin();
|
||||
|
||||
/**
|
||||
* Returns the initialized id_map plugin.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateIdMapInterface
|
||||
*/
|
||||
public function getIdMap();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHighwater();
|
||||
|
||||
public function saveHighwater($highwater);
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\MigrateException.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Defines the migrate exception class.
|
||||
*/
|
||||
class MigrateException extends \Exception {
|
||||
|
||||
/**
|
||||
* The level of the error being reported.
|
||||
*
|
||||
* The value is a Migration::MESSAGE_* constant.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $level;
|
||||
|
||||
/**
|
||||
* The status to record in the map table for the current item.
|
||||
*
|
||||
* The value is a MigrateMap::STATUS_* constant.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
* Constructs a MigrateException object.
|
||||
*
|
||||
* @param string $message
|
||||
* The message for the exception.
|
||||
* @param int $code
|
||||
* The Exception code.
|
||||
* @param \Exception $previous
|
||||
* The previous exception used for the exception chaining.
|
||||
* @param int $level
|
||||
* The level of the error, a Migration::MESSAGE_* constant.
|
||||
* @param int $status
|
||||
* The status of the item for the map table, a MigrateMap::STATUS_*
|
||||
* constant.
|
||||
*/
|
||||
public function __construct($message = null, $code = 0, \Exception $previous = null, $level = MigrationInterface::MESSAGE_ERROR, $status = MigrateIdMapInterface::STATUS_FAILED) {
|
||||
$this->level = $level;
|
||||
$this->status = $status;
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of the current item.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStatus() {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,538 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\MigrateExecutable.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Defines a migrate executable class.
|
||||
*/
|
||||
class MigrateExecutable {
|
||||
|
||||
/**
|
||||
* The migration to do.
|
||||
*
|
||||
* @var \Drupal\migrate\Entity\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The number of successfully imported rows since feedback was given.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $successes_since_feedback;
|
||||
|
||||
/**
|
||||
* The number of rows that were successfully processed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $total_successes;
|
||||
|
||||
/**
|
||||
* Status of one row.
|
||||
*
|
||||
* The value is a MigrateIdMapInterface::STATUS_* constant, for example:
|
||||
* STATUS_IMPORTED.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $needsUpdate;
|
||||
|
||||
/**
|
||||
* The number of rows processed.
|
||||
*
|
||||
* The total attempted, whether or not they were successful.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $total_processed;
|
||||
|
||||
/**
|
||||
* The queued messages not yet saved.
|
||||
*
|
||||
* Each element in the array is an array with two keys:
|
||||
* - 'message': The message string.
|
||||
* - 'level': The level, a MigrationInterface::MESSAGE_* constant.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $queuedMessages = array();
|
||||
|
||||
/**
|
||||
* The options that can be set when executing the migration.
|
||||
*
|
||||
* Values can be set for:
|
||||
* - 'limit': Sets a time limit.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* The fraction of the memory limit at which an operation will be interrupted.
|
||||
* Can be overridden by a Migration subclass if one would like to push the
|
||||
* envelope. Defaults to 85%.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $memoryThreshold = 0.85;
|
||||
|
||||
/**
|
||||
* The PHP memory_limit expressed in bytes.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $memoryLimit;
|
||||
|
||||
/**
|
||||
* The fraction of the time limit at which an operation will be interrupted.
|
||||
* Can be overridden by a Migration subclass if one would like to push the
|
||||
* envelope. Defaults to 90%.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $timeThreshold = 0.90;
|
||||
|
||||
/**
|
||||
* The PHP max_execution_time.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $timeLimit;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceIdValues;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $processed_since_feedback = 0;
|
||||
|
||||
/**
|
||||
* The translation manager.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationInterface
|
||||
*/
|
||||
protected $translationManager;
|
||||
|
||||
/**
|
||||
* @param MigrationInterface $migration
|
||||
* @param MigrateMessageInterface $message
|
||||
*
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
public function __construct(MigrationInterface $migration, MigrateMessageInterface $message) {
|
||||
$this->migration = $migration;
|
||||
$this->message = $message;
|
||||
$this->migration->getIdMap()->setMessage($message);
|
||||
// Record the memory limit in bytes
|
||||
$limit = trim(ini_get('memory_limit'));
|
||||
if ($limit == '-1') {
|
||||
$this->memoryLimit = PHP_INT_MAX;
|
||||
}
|
||||
else {
|
||||
if (!is_numeric($limit)) {
|
||||
$last = strtolower(substr($limit, -1));
|
||||
switch ($last) {
|
||||
case 'g':
|
||||
$limit *= 1024;
|
||||
case 'm':
|
||||
$limit *= 1024;
|
||||
case 'k':
|
||||
$limit *= 1024;
|
||||
break;
|
||||
default:
|
||||
throw new MigrateException($this->t('Invalid PHP memory_limit !limit',
|
||||
array('!limit' => $limit)));
|
||||
}
|
||||
}
|
||||
$this->memoryLimit = $limit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\migrate\Source
|
||||
*/
|
||||
public function getSource() {
|
||||
if (!isset($this->source)) {
|
||||
$this->source = new Source($this->migration, $this);
|
||||
}
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rollback action to be saved for the current row.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $rollbackAction;
|
||||
|
||||
/**
|
||||
* An array of counts. Initially used for cache hit/miss tracking.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $counts = array();
|
||||
|
||||
/**
|
||||
* When performing a bulkRollback(), the maximum number of items to pass in
|
||||
* a single call. Can be overridden in derived class constructor.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $rollbackBatchSize = 50;
|
||||
|
||||
/**
|
||||
* The object currently being constructed
|
||||
* @var \stdClass
|
||||
*/
|
||||
protected $destinationValues;
|
||||
|
||||
/**
|
||||
* The current data row retrieved from the source.
|
||||
* @var \stdClass
|
||||
*/
|
||||
protected $sourceValues;
|
||||
|
||||
/**
|
||||
* Perform an import operation - migrate items from source to destination.
|
||||
*/
|
||||
public function import() {
|
||||
$return = MigrationInterface::RESULT_COMPLETED;
|
||||
$source = $this->getSource();
|
||||
$destination = $this->migration->getDestinationPlugin();
|
||||
$id_map = $this->migration->getIdMap();
|
||||
|
||||
try {
|
||||
$source->rewind();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->message->display(
|
||||
$this->t('Migration failed with source plugin exception: !e',
|
||||
array('!e' => $e->getMessage())));
|
||||
return MigrationInterface::RESULT_FAILED;
|
||||
}
|
||||
while ($source->valid()) {
|
||||
$row = $source->current();
|
||||
if ($this->sourceIdValues = $row->getSourceIdValues()) {
|
||||
// Wipe old messages, and save any new messages.
|
||||
$id_map->delete($row->getSourceIdValues(), TRUE);
|
||||
$this->saveQueuedMessages();
|
||||
}
|
||||
|
||||
$this->processRow($row);
|
||||
|
||||
try {
|
||||
$destination_id_values = $destination->import($row);
|
||||
// @TODO handle the successful but no ID case like config.
|
||||
if ($destination_id_values) {
|
||||
$id_map->saveIdMapping($row, $destination_id_values, $this->needsUpdate, $this->rollbackAction);
|
||||
$this->successes_since_feedback++;
|
||||
$this->total_successes++;
|
||||
}
|
||||
else {
|
||||
$id_map->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_FAILED, $this->rollbackAction);
|
||||
if ($id_map->messageCount() == 0) {
|
||||
$message = $this->t('New object was not saved, no error provided');
|
||||
$this->saveMessage($message);
|
||||
$this->message->display($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
$this->migration->getIdMap()->saveIdMapping($row, array(), $e->getStatus(), $this->rollbackAction);
|
||||
$this->saveMessage($e->getMessage(), $e->getLevel());
|
||||
$this->message->display($e->getMessage());
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->migration->getIdMap()->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_FAILED, $this->rollbackAction);
|
||||
$this->handleException($e);
|
||||
}
|
||||
$this->total_processed++;
|
||||
$this->processed_since_feedback++;
|
||||
if ($highwater_property = $this->migration->get('highwaterProperty')) {
|
||||
$this->migration->saveHighwater($row->getSourceProperty($highwater_property['name']));
|
||||
}
|
||||
|
||||
// Reset row properties.
|
||||
unset($sourceValues, $destinationValues);
|
||||
$this->needsUpdate = MigrateIdMapInterface::STATUS_IMPORTED;
|
||||
|
||||
// TODO: Temporary. Remove when http://drupal.org/node/375494 is committed.
|
||||
// TODO: Should be done in MigrateDestinationEntity
|
||||
if (!empty($destination->entityType)) {
|
||||
entity_get_controller($destination->entityType)->resetCache();
|
||||
}
|
||||
|
||||
if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) {
|
||||
break;
|
||||
}
|
||||
if ($this->timeOptionExceeded()) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
$source->next();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->message->display(
|
||||
$this->t('Migration failed with source plugin exception: !e',
|
||||
array('!e' => $e->getMessage())));
|
||||
return MigrationInterface::RESULT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO uncomment this
|
||||
*/
|
||||
#$this->progressMessage($return);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Row $row
|
||||
* The $row to be processed.
|
||||
* @param array $process
|
||||
* A process pipeline configuration. If not set, the top level process
|
||||
* configuration in the migration entity is used.
|
||||
* @param mixed $value
|
||||
* Optional initial value of the pipeline for the first destination.
|
||||
* Usually setting this is not necessary as $process typically starts with
|
||||
* a 'get'. This is useful only when the $process contains a single
|
||||
* destination and needs to access a value outside of the source. See
|
||||
* \Drupal\migrate\Plugin\migrate\process\Iterator::transformKey for an
|
||||
* example.
|
||||
*/
|
||||
public function processRow(Row $row, array $process = NULL, $value = NULL) {
|
||||
foreach ($this->migration->getProcessPlugins($process) as $destination => $plugins) {
|
||||
foreach ($plugins as $plugin) {
|
||||
$value = $plugin->transform($value, $this, $row, $destination);
|
||||
}
|
||||
$row->setDestinationProperty($destination, $value);
|
||||
// Reset the value.
|
||||
$value = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the key array for the current source record.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function currentSourceIds() {
|
||||
return $this->getSource()->getCurrentIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we've exceeded the designated time limit.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the threshold is exceeded, FALSE if not.
|
||||
*/
|
||||
protected function timeOptionExceeded() {
|
||||
if (!$time_limit = $this->getTimeLimit()) {
|
||||
return FALSE;
|
||||
}
|
||||
$time_elapsed = time() - REQUEST_TIME;
|
||||
if ($time_elapsed >= $time_limit) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
public function getTimeLimit() {
|
||||
if (isset($this->options['limit']) &&
|
||||
($this->options['limit']['unit'] == 'seconds' || $this->options['limit']['unit'] == 'second')) {
|
||||
return $this->options['limit']['value'];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass messages through to the map class.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to record.
|
||||
* @param int $level
|
||||
* Optional message severity (defaults to MESSAGE_ERROR).
|
||||
*/
|
||||
public function saveMessage($message, $level = MigrationInterface::MESSAGE_ERROR) {
|
||||
$this->migration->getIdMap()->saveMessage($this->sourceIdValues, $message, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue messages to be later saved through the map class.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to record.
|
||||
* @param int $level
|
||||
* Optional message severity (defaults to MESSAGE_ERROR).
|
||||
*/
|
||||
public function queueMessage($message, $level = MigrationInterface::MESSAGE_ERROR) {
|
||||
$this->queuedMessages[] = array('message' => $message, 'level' => $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any messages we've queued up to the message table.
|
||||
*/
|
||||
public function saveQueuedMessages() {
|
||||
foreach ($this->queuedMessages as $queued_message) {
|
||||
$this->saveMessage($queued_message['message'], $queued_message['level']);
|
||||
}
|
||||
$this->queuedMessages = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard top-of-loop stuff, common between rollback and import - check
|
||||
* for exceptional conditions, and display feedback.
|
||||
*/
|
||||
protected function checkStatus() {
|
||||
if ($this->memoryExceeded()) {
|
||||
return MigrationInterface::RESULT_INCOMPLETE;
|
||||
}
|
||||
if ($this->timeExceeded()) {
|
||||
return MigrationInterface::RESULT_INCOMPLETE;
|
||||
}
|
||||
/*
|
||||
* @TODO uncomment this
|
||||
if ($this->getStatus() == MigrationInterface::STATUS_STOPPING) {
|
||||
return MigrationBase::RESULT_STOPPED;
|
||||
}
|
||||
*/
|
||||
// If feedback is requested, produce a progress message at the proper time
|
||||
/*
|
||||
* @TODO uncomment this
|
||||
if (isset($this->feedback)) {
|
||||
if (($this->feedback_unit == 'seconds' && time() - $this->lastfeedback >= $this->feedback) ||
|
||||
($this->feedback_unit == 'items' && $this->processed_since_feedback >= $this->feedback)) {
|
||||
$this->progressMessage(MigrationInterface::RESULT_INCOMPLETE);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return MigrationInterface::RESULT_COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we've exceeded the desired memory threshold. If so, output a message.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the threshold is exceeded, FALSE if not.
|
||||
*/
|
||||
protected function memoryExceeded() {
|
||||
$usage = memory_get_usage();
|
||||
$pct_memory = $usage / $this->memoryLimit;
|
||||
if ($pct_memory > $this->memoryThreshold) {
|
||||
$this->message->display(
|
||||
$this->t('Memory usage is !usage (!pct% of limit !limit), resetting statics',
|
||||
array('!pct' => round($pct_memory*100),
|
||||
'!usage' => format_size($usage),
|
||||
'!limit' => format_size($this->memoryLimit))),
|
||||
'warning');
|
||||
// First, try resetting Drupal's static storage - this frequently releases
|
||||
// plenty of memory to continue
|
||||
drupal_static_reset();
|
||||
$usage = memory_get_usage();
|
||||
$pct_memory = $usage/$this->memoryLimit;
|
||||
// Use a lower threshold - we don't want to be in a situation where we keep
|
||||
// coming back here and trimming a tiny amount
|
||||
if ($pct_memory > (.90 * $this->memoryThreshold)) {
|
||||
$this->message->display(
|
||||
$this->t('Memory usage is now !usage (!pct% of limit !limit), not enough reclaimed, starting new batch',
|
||||
array('!pct' => round($pct_memory*100),
|
||||
'!usage' => format_size($usage),
|
||||
'!limit' => format_size($this->memoryLimit))),
|
||||
'warning');
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
$this->message->display(
|
||||
$this->t('Memory usage is now !usage (!pct% of limit !limit), reclaimed enough, continuing',
|
||||
array('!pct' => round($pct_memory*100),
|
||||
'!usage' => format_size($usage),
|
||||
'!limit' => format_size($this->memoryLimit))),
|
||||
'warning');
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether we're approaching the PHP time limit.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the threshold is exceeded, FALSE if not.
|
||||
*/
|
||||
protected function timeExceeded() {
|
||||
if ($this->timeLimit == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
$time_elapsed = time() - REQUEST_TIME;
|
||||
$pct_time = $time_elapsed / $this->timeLimit;
|
||||
if ($pct_time > $this->timeThreshold) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an Exception object and both saves and displays it, pulling additional
|
||||
* information on the location triggering the exception.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* Object representing the exception.
|
||||
* @param boolean $save
|
||||
* Whether to save the message in the migration's mapping table. Set to FALSE
|
||||
* in contexts where this doesn't make sense.
|
||||
*/
|
||||
public function handleException($exception, $save = TRUE) {
|
||||
$result = _drupal_decode_exception($exception);
|
||||
$message = $result['!message'] . ' (' . $result['%file'] . ':' . $result['%line'] . ')';
|
||||
if ($save) {
|
||||
$this->saveMessage($message);
|
||||
}
|
||||
$this->message->display($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string to the current language or to a given language.
|
||||
*
|
||||
* See the t() documentation for details.
|
||||
*/
|
||||
protected function t($string, array $args = array(), array $options = array()) {
|
||||
return $this->translationManager()->translate($string, $args, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translation manager.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslationInterface
|
||||
* The translation manager.
|
||||
*/
|
||||
protected function translationManager() {
|
||||
if (!$this->translationManager) {
|
||||
$this->translationManager = \Drupal::translation();
|
||||
}
|
||||
return $this->translationManager;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\MigrateMessage.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
/**
|
||||
* Defines a migrate message class.
|
||||
*/
|
||||
class MigrateMessage implements MigrateMessageInterface {
|
||||
|
||||
/**
|
||||
* Displays a migrate message.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to display.
|
||||
* @param string $type
|
||||
* The type of message, for example: status or warning.
|
||||
*/
|
||||
function display($message, $type = 'status') {
|
||||
drupal_set_message($message, $type);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
|
||||
interface MigrateMessageInterface {
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
* @param string $type
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function display($message, $type = 'status');
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\MigrateDestinationInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\migrate\Entity\Migration;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Destinations are responsible for persisting source data into the destination
|
||||
* Drupal.
|
||||
*/
|
||||
interface MigrateDestinationInterface extends PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* To support MigrateIdMap maps, derived destination classes should return
|
||||
* schema field definition(s) corresponding to the primary key of the destination
|
||||
* being implemented. These are used to construct the destination key fields
|
||||
* of the map table for a migration using this destination.
|
||||
*/
|
||||
public function getIdsSchema();
|
||||
|
||||
/**
|
||||
* Derived classes must implement fields(), returning a list of available
|
||||
* destination fields.
|
||||
*
|
||||
* @todo Review the cases where we need the Migration parameter, can we avoid that?
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
* @return array
|
||||
* - Keys: machine names of the fields
|
||||
* - Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields(Migration $migration = NULL);
|
||||
|
||||
/**
|
||||
* Derived classes may implement preImport() and/or postImport(), to do any
|
||||
* processing they need done before or after looping over all source rows.
|
||||
* Similarly, preRollback() or postRollback() may be implemented.
|
||||
*/
|
||||
public function preImport();
|
||||
public function preRollback();
|
||||
public function postImport();
|
||||
public function postRollback();
|
||||
|
||||
/**
|
||||
* Derived classes must implement import(), to construct one new object (pre-populated
|
||||
* using ID mappings in the Migration).
|
||||
*/
|
||||
public function import(Row $row);
|
||||
|
||||
/**
|
||||
* Delete the specified IDs from the target Drupal.
|
||||
* @param array $destination_identifiers
|
||||
*/
|
||||
public function rollbackMultiple(array $destination_identifiers);
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\MigrateIdMapInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Defines an interface for migrate ID mappings.
|
||||
*
|
||||
* Migrate ID mappings maintain a relation between source ID and destination ID
|
||||
* for audit and rollback purposes.
|
||||
*/
|
||||
interface MigrateIdMapInterface extends PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* Codes reflecting the current status of a map row.
|
||||
*/
|
||||
const STATUS_IMPORTED = 0;
|
||||
const STATUS_NEEDS_UPDATE = 1;
|
||||
const STATUS_IGNORED = 2;
|
||||
const STATUS_FAILED = 3;
|
||||
|
||||
/**
|
||||
* Codes reflecting how to handle the destination item on rollback.
|
||||
*/
|
||||
const ROLLBACK_DELETE = 0;
|
||||
const ROLLBACK_PRESERVE = 1;
|
||||
|
||||
/**
|
||||
* Saves a mapping from the source identifiers to the destination identifiers.
|
||||
*
|
||||
* Called upon import of one row, we record a mapping from the source ID
|
||||
* to the destination ID. Also may be called, setting the third parameter to
|
||||
* NEEDS_UPDATE, to signal an existing record should be re-migrated.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The raw source data. We use the ID map derived from the source object
|
||||
* to get the source identifier values.
|
||||
* @param array $destination_id_values
|
||||
* An array of destination identifier values.
|
||||
* @param int $status
|
||||
* Status of the source row in the map.
|
||||
* @param int $rollback_action
|
||||
* How to handle the destination object on rollback.
|
||||
*/
|
||||
public function saveIdMapping(Row $row, array $destination_id_values, $status = self::STATUS_IMPORTED, $rollback_action = self::ROLLBACK_DELETE);
|
||||
|
||||
/**
|
||||
* Saves a message related to a source record in the migration message table.
|
||||
*
|
||||
* @param array $source_id_values
|
||||
* The source identifier values of the record in error.
|
||||
* @param string $message
|
||||
* The message to record.
|
||||
* @param int $level
|
||||
* Optional message severity (defaults to MESSAGE_ERROR).
|
||||
*/
|
||||
public function saveMessage(array $source_id_values, $message, $level = MigrationInterface::MESSAGE_ERROR);
|
||||
|
||||
/**
|
||||
* Prepares to run a full update.
|
||||
*
|
||||
* Prepares this migration to run as an update - that is, in addition to
|
||||
* unmigrated content (source records not in the map table) being imported,
|
||||
* previously-migrated content will also be updated in place by marking all
|
||||
* previously-imported content as ready to be re-imported.
|
||||
*/
|
||||
public function prepareUpdate();
|
||||
|
||||
/**
|
||||
* Returns the number of processed items in the map.
|
||||
*
|
||||
* @return int
|
||||
* The count of records in the map table.
|
||||
*/
|
||||
public function processedCount();
|
||||
|
||||
/**
|
||||
* Returns the number of imported items in the map.
|
||||
*
|
||||
* @return int
|
||||
* The number of imported items.
|
||||
*/
|
||||
public function importedCount();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a count of items which are marked as needing update.
|
||||
*
|
||||
* @return int
|
||||
* The number of items which need updating.
|
||||
*/
|
||||
public function updateCount();
|
||||
|
||||
/**
|
||||
* Returns the number of items that failed to import.
|
||||
*
|
||||
* @return int
|
||||
* The number of items that errored out.
|
||||
*/
|
||||
public function errorCount();
|
||||
|
||||
/**
|
||||
* Returns the number of messages saved.
|
||||
*
|
||||
* @return int
|
||||
* The number of messages.
|
||||
*/
|
||||
public function messageCount();
|
||||
|
||||
/**
|
||||
* Deletes the map and message entries for a given source record.
|
||||
*
|
||||
* @param array $source_id_values
|
||||
* The source identifier values of the record to delete.
|
||||
* @param bool $messages_only
|
||||
* TRUE to only delete the migrate messages.
|
||||
*/
|
||||
public function delete(array $source_id_values, $messages_only = FALSE);
|
||||
|
||||
/**
|
||||
* Deletes the map and message table entries for a given destination row.
|
||||
*
|
||||
* @param array $destination_id_values
|
||||
* The destination identifier values we should do the deletes for.
|
||||
*/
|
||||
public function deleteDestination(array $destination_id_values);
|
||||
|
||||
/**
|
||||
* Deletes the map and message entries for a set of given source records.
|
||||
*
|
||||
* @param array $source_id_values
|
||||
* The identifier values of the sources we should do the deletes for. Each
|
||||
* array member is an array of identifier values for one source row.
|
||||
*/
|
||||
public function deleteBulk(array $source_id_values);
|
||||
|
||||
/**
|
||||
* Clears all messages from the map.
|
||||
*/
|
||||
public function clearMessages();
|
||||
|
||||
/**
|
||||
* Retrieves a row from the map table based on source identifier values.
|
||||
*
|
||||
* @param array $source_id_values
|
||||
* The source identifier values of the record to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* The raw row data as an associative array.
|
||||
*/
|
||||
public function getRowBySource(array $source_id_values);
|
||||
|
||||
/**
|
||||
* Retrieves a row by the destination identifiers.
|
||||
*
|
||||
* @param array $destination_id_values
|
||||
* The destination identifier values of the record to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* The row(s) of data.
|
||||
*/
|
||||
public function getRowByDestination(array $destination_id_values);
|
||||
|
||||
/**
|
||||
* Retrieves an array of map rows marked as needing update.
|
||||
*
|
||||
* @param int $count
|
||||
* The maximum number of rows to return.
|
||||
*
|
||||
* @return array
|
||||
* Array of map row objects that need updating.
|
||||
*/
|
||||
public function getRowsNeedingUpdate($count);
|
||||
|
||||
/**
|
||||
* Looks up the source identifier.
|
||||
*
|
||||
* Given a (possibly multi-field) destination identifier value, return the
|
||||
* (possibly multi-field) source identifier value mapped to it.
|
||||
*
|
||||
* @param array $destination_id_values
|
||||
* The destination identifier values of the record.
|
||||
*
|
||||
* @return array
|
||||
* The source identifier values of the record, or NULL on failure.
|
||||
*/
|
||||
public function lookupSourceID(array $destination_id_values);
|
||||
|
||||
/**
|
||||
* Looks up the destination identifier.
|
||||
*
|
||||
* Given a (possibly multi-field) source identifier value, return the
|
||||
* (possibly multi-field) destination identifier value it is mapped to.
|
||||
*
|
||||
* @param array $destination_id_values
|
||||
* The source identifier values of the record.
|
||||
*
|
||||
* @return array
|
||||
* The destination identifier values of the record, or NULL on failure.
|
||||
*/
|
||||
public function lookupDestinationID(array $source_id_values);
|
||||
|
||||
/**
|
||||
* Removes any persistent storage used by this map.
|
||||
*
|
||||
* For example, remove the map and message tables.
|
||||
*/
|
||||
public function destroy();
|
||||
|
||||
/**
|
||||
* Gets the qualified map table.
|
||||
*
|
||||
* @todo Remove this as this is SQL only and so doesn't belong to the interface.
|
||||
*/
|
||||
public function getQualifiedMapTable();
|
||||
|
||||
/**
|
||||
* Sets the migrate message.
|
||||
*
|
||||
* @param \Drupal\migrate\MigrateMessageInterface $message
|
||||
* The message to display.
|
||||
*/
|
||||
public function setMessage(MigrateMessageInterface $message);
|
||||
|
||||
/**
|
||||
* Sets a specified record to be updated, if it exists.
|
||||
*
|
||||
* @param $source_id_values
|
||||
* The source identifier values of the record.
|
||||
*/
|
||||
public function setUpdate(array $source_id_values);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\MigraterPluginManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Manages migrate sources and steps.
|
||||
*
|
||||
* @see hook_migrate_info_alter()
|
||||
*/
|
||||
class MigratePluginManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* Constructs a MigraterPluginManager object.
|
||||
*
|
||||
* @param string $type
|
||||
* The type of the plugin: row, source, process, destination, entity_field, id_map.
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to invoke the alter hook with.
|
||||
*/
|
||||
public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct("Plugin/migrate/$type", $namespaces, 'Drupal\Component\Annotation\PluginID');
|
||||
$this->alterInfo($module_handler, 'migrate_' . $type . '_info');
|
||||
$this->setCacheBackend($cache_backend, $language_manager, 'migrate_plugins_' . $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* A specific createInstance method is necessary to pass the migration on.
|
||||
*/
|
||||
public function createInstance($plugin_id, array $configuration = array(), MigrationInterface $migration = NULL) {
|
||||
$plugin_definition = $this->discovery->getDefinition($plugin_id);
|
||||
$plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
|
||||
// If the plugin provides a factory method, pass the container to it.
|
||||
if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
|
||||
return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition, $migration);
|
||||
}
|
||||
return new $plugin_class($configuration, $plugin_id, $plugin_definition, $migration);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\MigrateProcessInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* An interface for migrate processes.
|
||||
*/
|
||||
interface MigrateProcessInterface extends PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* Performs the associated process.
|
||||
*
|
||||
* @param $value
|
||||
* The value to be transformed.
|
||||
* @param \Drupal\migrate\MigrateExecutable $migrate_executable
|
||||
* The migration in which this process is being executed.
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The row from the source to process. Normally, just transforming the
|
||||
* value is adequate but very rarely you might need to change two columns
|
||||
* at the same time or something like that.
|
||||
* @param string $destination_property
|
||||
* The destination property currently worked on. This is only used
|
||||
* together with the $row above.
|
||||
*/
|
||||
public function transform($value, MigrateExecutable $migrate_executable, Row $row, $destination_property);
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\MigrateSourceInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Defines an interface for migrate sources.
|
||||
*/
|
||||
interface MigrateSourceInterface extends \Countable, PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* Returns available fields on the source.
|
||||
*
|
||||
* @return array
|
||||
* Available fields in the source, keys are the field machine names as used
|
||||
* in field mappings, values are descriptions.
|
||||
*/
|
||||
public function fields();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the iterator that will yield the row arrays to be processed.
|
||||
*
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getIterator();
|
||||
|
||||
/**
|
||||
* Add additional data to the row.
|
||||
*
|
||||
* @param \Drupal\Migrate\Row $row
|
||||
*
|
||||
* @return bool
|
||||
* FALSE if this row needs to be skipped.
|
||||
*/
|
||||
public function prepareRow(Row $row);
|
||||
|
||||
public function __toString();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\RequirementsInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin;
|
||||
|
||||
/**
|
||||
* An interface to check for a migrate plugin requirements.
|
||||
*/
|
||||
interface RequirementsInterface {
|
||||
|
||||
/**
|
||||
* Checks if requirements for this plugin are OK.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if it is possible to use the plugin, FALSE if not.
|
||||
*/
|
||||
public function checkRequirements();
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Provides Configuration Management destination plugin.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\Entity\Migration;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Row;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Config\Config as ConfigObject;
|
||||
|
||||
/**
|
||||
* Persist data to the config system.
|
||||
*
|
||||
* @PluginID("d8_config")
|
||||
*/
|
||||
class Config extends DestinationBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param array $configuration
|
||||
* @param string $plugin_id
|
||||
* @param array $plugin_definition
|
||||
* @param ConfigObject $config
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ConfigObject $config) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('config.factory')->get($configuration['config_name'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import(Row $row) {
|
||||
$this->config
|
||||
->setData($row->getDestination())
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $destination_keys
|
||||
*
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
public function rollbackMultiple(array $destination_keys) {
|
||||
throw new MigrateException('Configuration can not be rolled back');
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes must implement fields(), returning a list of available
|
||||
* destination fields.
|
||||
*
|
||||
* @todo Review the cases where we need the Migration parameter, can we avoid that?
|
||||
*
|
||||
* @param Migration $migration
|
||||
* Optionally, the migration containing this destination.
|
||||
* @return array
|
||||
* - Keys: machine names of the fields
|
||||
* - Values: Human-friendly descriptions of the fields.
|
||||
*/
|
||||
public function fields(Migration $migration = NULL) {
|
||||
// @todo Dynamically fetch fields using Config Schema API.
|
||||
}
|
||||
|
||||
public function getIdsSchema() {
|
||||
return array($this->config->getName() => array());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\destination\DestinationBase.
|
||||
*/
|
||||
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationInterface;
|
||||
|
||||
abstract class DestinationBase extends PluginBase implements MigrateDestinationInterface {
|
||||
|
||||
/**
|
||||
* Modify the Row before it is imported.
|
||||
*/
|
||||
public function preImport() {
|
||||
// TODO: Implement preImport() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the Row before it is rolled back.
|
||||
*/
|
||||
public function preRollback() {
|
||||
// TODO: Implement preRollback() method.
|
||||
}
|
||||
|
||||
public function postImport() {
|
||||
// TODO: Implement postImport() method.
|
||||
}
|
||||
|
||||
public function postRollback() {
|
||||
// TODO: Implement postRollback() method.
|
||||
}
|
||||
|
||||
public function rollbackMultiple(array $destination_identifiers) {
|
||||
// TODO: Implement rollbackMultiple() method.
|
||||
}
|
||||
|
||||
public function getCreated() {
|
||||
// TODO: Implement getCreated() method.
|
||||
}
|
||||
|
||||
public function getUpdated() {
|
||||
// TODO: Implement getUpdated() method.
|
||||
}
|
||||
|
||||
public function resetStats() {
|
||||
// TODO: Implement resetStats() method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,705 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\id_map\Sql.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\id_map;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Defines the sql based ID map implementation.
|
||||
*
|
||||
* It creates one map and one message table per migration entity to store the
|
||||
* relevant information.
|
||||
*
|
||||
* @PluginID("sql")
|
||||
*/
|
||||
class Sql extends PluginBase implements MigrateIdMapInterface {
|
||||
|
||||
/**
|
||||
* Names of tables created for tracking the migration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mapTable, $messageTable;
|
||||
|
||||
/**
|
||||
* The migrate message.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateMessageInterface
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* The database connection for the map/message tables on the destination.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* The migration being done.
|
||||
*
|
||||
* @var \Drupal\migrate\Entity\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The source ID fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceIdFields;
|
||||
|
||||
/**
|
||||
* The destination ID fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $destinationIdFields;
|
||||
|
||||
/**
|
||||
* Stores whether the the tables (map/message) already exist.
|
||||
*
|
||||
* This is determined just once per request/instance of the class.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $ensured;
|
||||
|
||||
/**
|
||||
* The result.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
protected $result = NULL;
|
||||
|
||||
/**
|
||||
* The current row.
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
protected $currentRow = NULL;
|
||||
|
||||
/**
|
||||
* The current key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $currentKey = array();
|
||||
|
||||
/**
|
||||
* Constructs an SQL object.
|
||||
*
|
||||
* Sets up the tables and builds the maps,
|
||||
*
|
||||
* @param array $configuration
|
||||
* The configuration.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the migration process to do.
|
||||
* @param array $plugin_definition
|
||||
* The configuration for the plugin.
|
||||
* @param \Drupal\migrate\Entity\MigrationInterface $migration
|
||||
* The migration to do.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, MigrationInterface $migration) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->migration = $migration;
|
||||
$machine_name = $migration->id();
|
||||
|
||||
// Default generated table names, limited to 63 characters.
|
||||
$prefixLength = strlen($this->getDatabase()->tablePrefix()) ;
|
||||
$this->mapTable = 'migrate_map_' . Unicode::strtolower($machine_name);
|
||||
$this->mapTable = Unicode::substr($this->mapTable, 0, 63 - $prefixLength);
|
||||
$this->messageTable = 'migrate_message_' . Unicode::strtolower($machine_name);
|
||||
$this->messageTable = Unicode::substr($this->messageTable, 0, 63 - $prefixLength);
|
||||
$this->sourceIds = $migration->get('sourceIds');
|
||||
$this->destinationIds = $migration->get('destinationIds');
|
||||
|
||||
// Build the source and destination identifier maps.
|
||||
$this->sourceIdFields = array();
|
||||
$count = 1;
|
||||
foreach ($this->sourceIds as $field => $schema) {
|
||||
$this->sourceIdFields[$field] = 'sourceid' . $count++;
|
||||
}
|
||||
$this->destinationIdFields = array();
|
||||
$count = 1;
|
||||
foreach ($this->destinationIds as $field => $schema) {
|
||||
$this->destinationIdFields[$field] = 'destid' . $count++;
|
||||
}
|
||||
$this->ensureTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Qualifying the map table name with the database name makes cross-db joins
|
||||
* possible. Note that, because prefixes are applied after we do this (i.e.,
|
||||
* it will prefix the string we return), we do not qualify the table if it has
|
||||
* a prefix. This will work fine when the source data is in the default
|
||||
* (prefixed) database (in particular, for simpletest), but not if the primary
|
||||
* query is in an external database.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQualifiedMapTable() {
|
||||
$options = $this->getDatabase()->getConnectionOptions();
|
||||
$prefix = $this->getDatabase()->tablePrefix($this->mapTable);
|
||||
if ($prefix) {
|
||||
return $this->mapTable;
|
||||
}
|
||||
else {
|
||||
return $options['database'] . '.' . $this->mapTable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected function getDatabase() {
|
||||
if (!isset($this->database)) {
|
||||
$this->database = SqlBase::getDatabaseConnection($this->migration->id(), $this->configuration);
|
||||
}
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMessage(MigrateMessageInterface $message) {
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the map and message tables if they don't already exist.
|
||||
*/
|
||||
protected function ensureTables() {
|
||||
if (!$this->ensured) {
|
||||
if (!$this->getDatabase()->schema()->tableExists($this->mapTable)) {
|
||||
// Generate appropriate schema info for the map and message tables,
|
||||
// and map from the source field names to the map/msg field names.
|
||||
$count = 1;
|
||||
$source_id_schema = array();
|
||||
$pks = array();
|
||||
foreach ($this->sourceIds as $field_schema) {
|
||||
$mapkey = 'sourceid' . $count++;
|
||||
$source_id_schema[$mapkey] = $field_schema;
|
||||
$pks[] = $mapkey;
|
||||
}
|
||||
|
||||
$fields = $source_id_schema;
|
||||
|
||||
// Add destination identifiers to map table.
|
||||
// TODO: How do we discover the destination schema?
|
||||
$count = 1;
|
||||
foreach ($this->destinationIds as $field_schema) {
|
||||
// Allow dest identifier fields to be NULL (for IGNORED/FAILED
|
||||
// cases).
|
||||
$field_schema['not null'] = FALSE;
|
||||
$mapkey = 'destid' . $count++;
|
||||
$fields[$mapkey] = $field_schema;
|
||||
}
|
||||
$fields['needs_update'] = array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => MigrateIdMapInterface::STATUS_IMPORTED,
|
||||
'description' => 'Indicates current status of the source row',
|
||||
);
|
||||
$fields['rollback_action'] = array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => MigrateIdMapInterface::ROLLBACK_DELETE,
|
||||
'description' => 'Flag indicating what to do for this item on rollback',
|
||||
);
|
||||
$fields['last_imported'] = array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'UNIX timestamp of the last time this row was imported',
|
||||
);
|
||||
$fields['hash'] = array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
);
|
||||
$schema = array(
|
||||
'description' => 'Mappings from source identifier value(s) to destination identifier value(s).',
|
||||
'fields' => $fields,
|
||||
);
|
||||
if ($pks) {
|
||||
$schema['primary key'] = $pks;
|
||||
}
|
||||
$this->getDatabase()->schema()->createTable($this->mapTable, $schema);
|
||||
|
||||
// Now do the message table.
|
||||
$fields = array();
|
||||
$fields['msgid'] = array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
);
|
||||
$fields += $source_id_schema;
|
||||
|
||||
$fields['level'] = array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
);
|
||||
$fields['message'] = array(
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'not null' => TRUE,
|
||||
);
|
||||
$schema = array(
|
||||
'description' => 'Messages generated during a migration process',
|
||||
'fields' => $fields,
|
||||
'primary key' => array('msgid'),
|
||||
);
|
||||
if ($pks) {
|
||||
$schema['indexes']['sourcekey'] = $pks;
|
||||
}
|
||||
$this->getDatabase()->schema()->createTable($this->messageTable, $schema);
|
||||
}
|
||||
else {
|
||||
// Add any missing columns to the map table.
|
||||
if (!$this->getDatabase()->schema()->fieldExists($this->mapTable,
|
||||
'rollback_action')) {
|
||||
$this->getDatabase()->schema()->addField($this->mapTable,
|
||||
'rollback_action', array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Flag indicating what to do for this item on rollback',
|
||||
));
|
||||
}
|
||||
if (!$this->getDatabase()->schema()->fieldExists($this->mapTable, 'hash')) {
|
||||
$this->getDatabase()->schema()->addField($this->mapTable, 'hash', array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
));
|
||||
}
|
||||
}
|
||||
$this->ensured = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRowBySource(array $source_id_value) {
|
||||
$query = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map');
|
||||
foreach ($this->sourceIdFields as $source_id) {
|
||||
$query = $query->condition("map.$source_id", array_shift($source_id_value), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
return $result->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRowByDestination(array $destination_id_values) {
|
||||
$query = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map');
|
||||
foreach ($this->destinationIdFields as $destination_id) {
|
||||
$query = $query->condition("map.$destination_id", array_shift($destination_id_values), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
return $result->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRowsNeedingUpdate($count) {
|
||||
$rows = array();
|
||||
$result = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map')
|
||||
->condition('needs_update', MigrateIdMapInterface::STATUS_NEEDS_UPDATE)
|
||||
->range(0, $count)
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lookupSourceID(array $destination_id) {
|
||||
$query = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map', $this->sourceIdFields);
|
||||
foreach ($this->destinationIdFields as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($destination_id), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
$source_id = $result->fetchAssoc();
|
||||
return $source_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lookupDestinationID(array $source_id) {
|
||||
$query = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map', $this->destinationIdFields);
|
||||
foreach ($this->sourceIdFields as $key_name) {
|
||||
$query = $query->condition("map.$key_name", array_shift($source_id), '=');
|
||||
}
|
||||
$result = $query->execute();
|
||||
$destination_id = $result->fetchAssoc();
|
||||
return $destination_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveIdMapping(Row $row, array $destination_id_values, $needs_update = MigrateIdMapInterface::STATUS_IMPORTED, $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE) {
|
||||
// Construct the source key.
|
||||
$source_id_values = $row->getSourceIdValues();
|
||||
// Construct the source key and initialize to empty variable keys.
|
||||
$keys = array();
|
||||
foreach ($this->sourceIdFields as $field_name => $key_name) {
|
||||
// A NULL key value will fail.
|
||||
if (!isset($source_id_values[$field_name])) {
|
||||
$this->message->display(t(
|
||||
'Could not save to map table due to NULL value for key field !field',
|
||||
array('!field' => $field_name)));
|
||||
return;
|
||||
}
|
||||
$keys[$key_name] = $source_id_values[$field_name];
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
'needs_update' => (int) $needs_update,
|
||||
'rollback_action' => (int) $rollback_action,
|
||||
'hash' => $row->getHash(),
|
||||
);
|
||||
$count = 1;
|
||||
foreach ($destination_id_values as $dest_id) {
|
||||
$fields['destid' . $count++] = $dest_id;
|
||||
}
|
||||
if ($this->migration->get('trackLastImported')) {
|
||||
$fields['last_imported'] = time();
|
||||
}
|
||||
if ($keys) {
|
||||
$this->getDatabase()->merge($this->mapTable)
|
||||
->key($keys)
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveMessage(array $source_id_values, $message, $level = MigrationInterface::MESSAGE_ERROR) {
|
||||
$count = 1;
|
||||
foreach ($source_id_values as $id_value) {
|
||||
$fields['sourceid' . $count++] = $id_value;
|
||||
// If any key value is empty, we can't save - print out and abort.
|
||||
if (empty($id_value)) {
|
||||
print($message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$fields['level'] = $level;
|
||||
$fields['message'] = $message;
|
||||
$this->getDatabase()->insert($this->messageTable)
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareUpdate() {
|
||||
$this->getDatabase()->update($this->mapTable)
|
||||
->fields(array('needs_update' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processedCount() {
|
||||
return $this->getDatabase()->select($this->mapTable)
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importedCount() {
|
||||
return $this->getDatabase()->select($this->mapTable)
|
||||
->condition('needs_update', array(MigrateIdMapInterface::STATUS_IMPORTED, MigrateIdMapInterface::STATUS_NEEDS_UPDATE), 'IN')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateCount() {
|
||||
return $this->countHelper(MigrateIdMapInterface::STATUS_NEEDS_UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function errorCount() {
|
||||
return $this->countHelper(MigrateIdMapInterface::STATUS_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function messageCount() {
|
||||
return $this->countHelper(NULL, $this->messageTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts records in a table.
|
||||
*
|
||||
* @param $status
|
||||
* An integer for the needs_update column.
|
||||
* @param $table
|
||||
* The table to work
|
||||
* @return int
|
||||
* The number of records.
|
||||
*/
|
||||
protected function countHelper($status, $table = NULL) {
|
||||
$query = $this->getDatabase()->select($table ?: $this->mapTable);
|
||||
if (isset($status)) {
|
||||
$query->condition('needs_update', $status);
|
||||
}
|
||||
return $query->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(array $source_id_values, $messages_only = FALSE) {
|
||||
if (empty($source_id_values)) {
|
||||
throw new MigrateException('Without source identifier values it is impossible to find the row to delete.');
|
||||
}
|
||||
if (!$messages_only) {
|
||||
$map_query = $this->getDatabase()->delete($this->mapTable);
|
||||
}
|
||||
$message_query = $this->getDatabase()->delete($this->messageTable);
|
||||
$count = 1;
|
||||
foreach ($source_id_values as $id_value) {
|
||||
if (!$messages_only) {
|
||||
$map_query->condition('sourceid' . $count, $id_value);
|
||||
}
|
||||
$message_query->condition('sourceid' . $count, $id_value);
|
||||
$count++;
|
||||
}
|
||||
|
||||
if (!$messages_only) {
|
||||
$map_query->execute();
|
||||
}
|
||||
$message_query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteDestination(array $destination_id) {
|
||||
$map_query = $this->getDatabase()->delete($this->mapTable);
|
||||
$message_query = $this->getDatabase()->delete($this->messageTable);
|
||||
$source_id = $this->lookupSourceID($destination_id);
|
||||
if (!empty($source_id)) {
|
||||
$count = 1;
|
||||
foreach ($destination_id as $key_value) {
|
||||
$map_query->condition('destid' . $count, $key_value);
|
||||
$count++;
|
||||
}
|
||||
$map_query->execute();
|
||||
$count = 1;
|
||||
foreach ($source_id as $key_value) {
|
||||
$message_query->condition('sourceid' . $count, $key_value);
|
||||
$count++;
|
||||
}
|
||||
$message_query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUpdate(array $source_id) {
|
||||
if (empty($source_ids)) {
|
||||
throw new MigrateException('No source identifiers provided to update.');
|
||||
}
|
||||
$query = $this->getDatabase()
|
||||
->update($this->mapTable)
|
||||
->fields(array('needs_update' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE));
|
||||
$count = 1;
|
||||
foreach ($source_id as $key_value) {
|
||||
$query->condition('sourceid' . $count++, $key_value);
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteBulk(array $source_id_values) {
|
||||
// If we have a single-column key, we can shortcut it.
|
||||
if (count($this->sourceIds) == 1) {
|
||||
$sourceids = array();
|
||||
foreach ($source_id_values as $source_id) {
|
||||
$sourceids[] = $source_id;
|
||||
}
|
||||
$this->getDatabase()->delete($this->mapTable)
|
||||
->condition('sourceid1', $sourceids, 'IN')
|
||||
->execute();
|
||||
$this->getDatabase()->delete($this->messageTable)
|
||||
->condition('sourceid1', $sourceids, 'IN')
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
foreach ($source_id_values as $source_id) {
|
||||
$map_query = $this->getDatabase()->delete($this->mapTable);
|
||||
$message_query = $this->getDatabase()->delete($this->messageTable);
|
||||
$count = 1;
|
||||
foreach ($source_id as $key_value) {
|
||||
$map_query->condition('sourceid' . $count, $key_value);
|
||||
$message_query->condition('sourceid' . $count++, $key_value);
|
||||
}
|
||||
$map_query->execute();
|
||||
$message_query->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearMessages() {
|
||||
$this->getDatabase()->truncate($this->messageTable)->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy() {
|
||||
$this->getDatabase()->schema()->dropTable($this->mapTable);
|
||||
$this->getDatabase()->schema()->dropTable($this->messageTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::rewind().
|
||||
*
|
||||
* This is called before beginning a foreach loop.
|
||||
*
|
||||
* @todo Support idlist, itemlimit.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->currentRow = NULL;
|
||||
$fields = array();
|
||||
foreach ($this->sourceIdFields as $field) {
|
||||
$fields[] = $field;
|
||||
}
|
||||
foreach ($this->destinationIdFields as $field) {
|
||||
$fields[] = $field;
|
||||
}
|
||||
|
||||
// @todo Make this work.
|
||||
/*
|
||||
if (isset($this->options['itemlimit'])) {
|
||||
$query = $query->range(0, $this->options['itemlimit']);
|
||||
}
|
||||
*/
|
||||
$this->result = $this->getDatabase()->select($this->mapTable, 'map')
|
||||
->fields('map', $fields)
|
||||
->execute();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::current().
|
||||
*
|
||||
* This is called when entering a loop iteration, returning the current row.
|
||||
*/
|
||||
public function current() {
|
||||
return $this->currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::key().
|
||||
*
|
||||
* This is called when entering a loop iteration, returning the key of the
|
||||
* current row. It must be a scalar - we will serialize to fulfill the
|
||||
* requirement, but using getCurrentKey() is preferable.
|
||||
*/
|
||||
public function key() {
|
||||
return serialize($this->currentKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::next().
|
||||
*
|
||||
* This is called at the bottom of the loop implicitly, as well as explicitly
|
||||
* from rewind().
|
||||
*/
|
||||
public function next() {
|
||||
$this->currentRow = $this->result->fetchObject();
|
||||
$this->currentKey = array();
|
||||
if (!is_object($this->currentRow)) {
|
||||
$this->currentRow = NULL;
|
||||
}
|
||||
else {
|
||||
foreach ($this->sourceIdFields as $map_field) {
|
||||
$this->currentKey[$map_field] = $this->currentRow->$map_field;
|
||||
// Leave only destination fields.
|
||||
unset($this->currentRow->$map_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::valid().
|
||||
*
|
||||
* This is called at the top of the loop, returning TRUE to process the loop
|
||||
* and FALSE to terminate it.
|
||||
*/
|
||||
public function valid() {
|
||||
// @todo Check numProcessed against itemlimit.
|
||||
return !is_null($this->currentRow);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\process\DefaultValue.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateProcessInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
|
||||
/**
|
||||
* This plugin sets missing values on the destination.
|
||||
*
|
||||
* @PluginId("default_value")
|
||||
*/
|
||||
class DefaultValue extends PluginBase implements MigrateProcessInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutable $migrate_executable, Row $row, $destination_property) {
|
||||
return isset($value) ? $value : $this->configuration['default_value'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\process\CopyFromSource.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateProcessInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* This plugin copies from the source to the destination.
|
||||
*
|
||||
* @PluginId("get")
|
||||
*/
|
||||
class Get extends PluginBase implements MigrateProcessInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutable $migrate_executable, Row $row, $destination_property) {
|
||||
$source = $this->configuration['source'];
|
||||
$properties = is_string($source) ? array($source) : $source;
|
||||
$return = array();
|
||||
foreach ($properties as $property) {
|
||||
if (empty($property)) {
|
||||
$return[] = $value;
|
||||
}
|
||||
else {
|
||||
$is_source = TRUE;
|
||||
if ($property[0] == '@') {
|
||||
$property = preg_replace_callback('/^(@?)((?:@@)*)([^@]|$)/', function ($matches) use (&$is_source) {
|
||||
// If there are an odd number of @ in the beginning, it's a
|
||||
// destination.
|
||||
$is_source = empty($matches[1]);
|
||||
// Remove the possible escaping and do not lose the terminating
|
||||
// non-@ either.
|
||||
return str_replace('@@', '@', $matches[2]) . $matches[3];
|
||||
}, $property);
|
||||
}
|
||||
if ($is_source) {
|
||||
$return[] = $row->getSourceProperty($property);
|
||||
}
|
||||
else {
|
||||
$return[] = $row->getDestinationProperty($property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return is_string($source) ? $return[0] : $return;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\source\d6\Variable.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\source\d6\Drupal6SqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6 variable source from database.
|
||||
*
|
||||
* @PluginID("drupal6_variable")
|
||||
*/
|
||||
class D6Variable extends Drupal6SqlBase {
|
||||
|
||||
/**
|
||||
* The variable names to fetch.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function __construct(array $configuration, $plugin_id, array $plugin_definition, MigrationInterface $migration) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
|
||||
$this->variables = $this->configuration['variables'];
|
||||
}
|
||||
|
||||
protected function runQuery() {
|
||||
return new \ArrayIterator(array(array_map('unserialize', $this->query()->execute()->fetchAllKeyed())));
|
||||
}
|
||||
|
||||
public function count() {
|
||||
return intval($this->query()->countQuery()->execute()->fetchField() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return drupal_map_assoc($this->variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function query() {
|
||||
return $this->getDatabase()
|
||||
->select('variable', 'v')
|
||||
->fields('v', array('name', 'value'))
|
||||
->condition('name', $this->variables, 'IN');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\source\SourcePluginBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
abstract class SourcePluginBase extends PluginBase implements MigrateSourceInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Entity\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function __construct(array $configuration, $plugin_id, array $plugin_definition, MigrationInterface $migration) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->migration = $migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected function getModuleHandler() {
|
||||
if (!isset($this->moduleHandler)) {
|
||||
$this->moduleHandler = \Drupal::moduleHandler();
|
||||
}
|
||||
return $this->moduleHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$this->getModuleHandler()->invokeAll('migrate_prepare_row', $row, $this, $this->migration);
|
||||
$this->getModuleHandler()->invokeAll('migrate_ '. $this->migration->id() . '_prepare_row', $row, $this, $this->migration);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\source\SqlBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Sources whose data may be fetched via DBTNG.
|
||||
*/
|
||||
abstract class SqlBase extends SourcePluginBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Entity\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function __construct(array $configuration, $plugin_id, array $plugin_definition, MigrationInterface $migration) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
|
||||
$this->mapJoinable = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
function __toString() {
|
||||
return (string) $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
public function getDatabase() {
|
||||
if (!isset($this->database)) {
|
||||
$this->database = static::getDatabaseConnection($this->migration->id(), $this->configuration);
|
||||
}
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
public static function getDatabaseConnection($id, array $configuration) {
|
||||
if (isset($configuration['database'])) {
|
||||
$key = 'migrate_' . $id;
|
||||
Database::addConnectionInfo($key, 'default', $configuration['database']);
|
||||
}
|
||||
else {
|
||||
$key = 'default';
|
||||
}
|
||||
return Database::getConnection('default', $key);
|
||||
}
|
||||
|
||||
protected function select($table, $alias = NULL, array $options = array()) {
|
||||
$options['fetch'] = \PDO::FETCH_ASSOC;
|
||||
return $this->getDatabase()->select($table, $alias, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of MigrateSource::performRewind().
|
||||
*
|
||||
* We could simply execute the query and be functionally correct, but
|
||||
* we will take advantage of the PDO-based API to optimize the query up-front.
|
||||
*/
|
||||
protected function runQuery() {
|
||||
$this->query = clone $this->query();
|
||||
$this->query->addTag('migrate');
|
||||
$this->query->addTag('migrate_' . $this->migration->id());
|
||||
$this->query->addMetaData('migration', $this->migration);
|
||||
$highwaterProperty = $this->migration->get('highwaterProperty');
|
||||
|
||||
// Get the key values, for potential use in joining to the map table, or
|
||||
// enforcing idlist.
|
||||
$keys = array();
|
||||
foreach ($this->migration->get('sourceIds') as $field_name => $field_schema) {
|
||||
if (isset($field_schema['alias'])) {
|
||||
$field_name = $field_schema['alias'] . '.' . $field_name;
|
||||
}
|
||||
$keys[] = $field_name;
|
||||
}
|
||||
|
||||
// The rules for determining what conditions to add to the query are as
|
||||
// follows (applying first applicable rule)
|
||||
// 1. If idlist is provided, then only process items in that list (AND key
|
||||
// IN (idlist)). Only applicable with single-value keys.
|
||||
if ($id_list = $this->migration->get('idlist')) {
|
||||
$this->query->condition($keys[0], $id_list, 'IN');
|
||||
}
|
||||
else {
|
||||
// 2. If the map is joinable, join it. We will want to accept all rows
|
||||
// which are either not in the map, or marked in the map as NEEDS_UPDATE.
|
||||
// Note that if highwater fields are in play, we want to accept all rows
|
||||
// above the highwater mark in addition to those selected by the map
|
||||
// conditions, so we need to OR them together (but AND with any existing
|
||||
// conditions in the query). So, ultimately the SQL condition will look
|
||||
// like (original conditions) AND (map IS NULL OR map needs update
|
||||
// OR above highwater).
|
||||
$conditions = $this->query->orConditionGroup();
|
||||
$condition_added = FALSE;
|
||||
if ($this->mapJoinable) {
|
||||
// Build the join to the map table. Because the source key could have
|
||||
// multiple fields, we need to build things up.
|
||||
$count = 1;
|
||||
$map_join = '';
|
||||
$delimiter = '';
|
||||
foreach ($this->migration->get('sourceIds') as $field_name => $field_schema) {
|
||||
if (isset($field_schema['alias'])) {
|
||||
$field_name = $field_schema['alias'] . '.' . $field_name;
|
||||
}
|
||||
$map_join .= "$delimiter$field_name = map.sourceid" . $count++;
|
||||
$delimiter = ' AND ';
|
||||
}
|
||||
|
||||
$alias = $this->query->leftJoin($this->migration->getIdMap()->getQualifiedMapTable(), 'map', $map_join);
|
||||
$conditions->isNull($alias . '.sourceid1');
|
||||
$conditions->condition($alias . '.needs_update', MigrateIdMapInterface::STATUS_NEEDS_UPDATE);
|
||||
$condition_added = TRUE;
|
||||
|
||||
// And as long as we have the map table, add its data to the row.
|
||||
$n = count($this->migration->get('sourceIds'));
|
||||
for ($count = 1; $count <= $n; $count++) {
|
||||
$map_key = 'sourceid' . $count;
|
||||
$this->query->addField($alias, $map_key, "migrate_map_$map_key");
|
||||
}
|
||||
$n = count($this->migration->get('destinationIds'));
|
||||
for ($count = 1; $count <= $n; $count++) {
|
||||
$map_key = 'destid' . $count++;
|
||||
$this->query->addField($alias, $map_key, "migrate_map_$map_key");
|
||||
}
|
||||
$this->query->addField($alias, 'needs_update', 'migrate_map_needs_update');
|
||||
}
|
||||
// 3. If we are using highwater marks, also include rows above the mark.
|
||||
// But, include all rows if the highwater mark is not set.
|
||||
if (isset($highwaterProperty['name']) && ($highwater = $this->migration->getHighwater()) !== '') {
|
||||
if (isset($highwaterProperty['alias'])) {
|
||||
$highwater = $highwaterProperty['alias'] . '.' . $highwaterProperty['name'];
|
||||
}
|
||||
else {
|
||||
$highwater = $highwaterProperty['name'];
|
||||
}
|
||||
$conditions->condition($highwater, $highwater, '>');
|
||||
$condition_added = TRUE;
|
||||
}
|
||||
if ($condition_added) {
|
||||
$this->query->condition($conditions);
|
||||
}
|
||||
}
|
||||
|
||||
return new \IteratorIterator($this->query->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
abstract function query();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return $this->query()->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the iterator that will yield the row arrays to be processed.
|
||||
*
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getIterator() {
|
||||
if (!isset($this->iterator)) {
|
||||
$this->iterator = $this->runQuery();
|
||||
}
|
||||
return $this->iterator;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\source\d6\Drupal6SqlBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* A base source class for Drupal 6 migrate sources.
|
||||
*
|
||||
* Mainly to let children retrieve information from the origin system in an
|
||||
* easier way.
|
||||
*/
|
||||
abstract class Drupal6SqlBase extends SqlBase {
|
||||
|
||||
/**
|
||||
* Retrieves all system data information from origin system.
|
||||
*
|
||||
* @return array
|
||||
* List of system table information keyed by type and name.
|
||||
*/
|
||||
public function getSystemData() {
|
||||
static $system_data;
|
||||
if (isset($system_data)) {
|
||||
return $system_data;
|
||||
}
|
||||
$results = $this->database
|
||||
->select('system', 's')
|
||||
->fields('s')
|
||||
->execute();
|
||||
foreach ($results as $result) {
|
||||
$system_data[$result['type']][$result['name']] = $result;
|
||||
}
|
||||
return $system_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a module schema_version value in the source installation.
|
||||
*
|
||||
* @param string $module
|
||||
* Name of module.
|
||||
*
|
||||
* @return mixed
|
||||
* The current module schema version on the origin system table or FALSE if
|
||||
* not found.
|
||||
*/
|
||||
protected function getModuleSchemaVersion($module) {
|
||||
$system_data = $this->getSystemData();
|
||||
return isset($system_data['module'][$module]['schema_version']) ? $system_data['module'][$module]['schema_version'] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given module is enabled in the source installation.
|
||||
*
|
||||
* @param string $module
|
||||
* Name of module to check.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if module is enabled on the origin system, FALSE if not.
|
||||
*/
|
||||
protected function moduleExists($module) {
|
||||
return isset($system_data['module'][$module]['status']) ? (bool) $system_data['module'][$module]['status'] : FALSE;
|
||||
}
|
||||
|
||||
protected function variableGet($name, $default) {
|
||||
try {
|
||||
$result = $this->database
|
||||
->query('SELECT value FROM {variable} WHERE name = :name', array(':name' => $name))
|
||||
->fetchField();
|
||||
}
|
||||
// The table might not exist.
|
||||
catch (\Exception $e) {
|
||||
$result = FALSE;
|
||||
}
|
||||
return $result !== FALSE ? unserialize($result) : $default;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Row.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Stores a row.
|
||||
*/
|
||||
class Row {
|
||||
|
||||
/**
|
||||
* The actual values of the source row.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $source = array();
|
||||
|
||||
/**
|
||||
* The source identifiers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceIds = array();
|
||||
|
||||
/**
|
||||
* The destination values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $destination = array();
|
||||
|
||||
/**
|
||||
* The mapping between source and destination identifiers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $idMap = array(
|
||||
'original_hash' => '',
|
||||
'hash' => '',
|
||||
'needs_update' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether the source has been frozen already.
|
||||
*
|
||||
* Once frozen the source can not be changed any more.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $frozen = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\Migrate\Row object.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of values to add as properties on the object.
|
||||
* @param array $source_ids
|
||||
* An array containing the IDs of the source using the keys as the field
|
||||
* names.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown when a source ID property does not exist.
|
||||
*/
|
||||
public function __construct(array $values, array $source_ids) {
|
||||
$this->source = $values;
|
||||
$this->sourceIds = $source_ids;
|
||||
foreach (array_keys($source_ids) as $id) {
|
||||
if (!$this->hasSourceProperty($id)) {
|
||||
throw new \InvalidArgumentException("$id has no value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the values of the source identifiers.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the values of the source identifiers.
|
||||
*/
|
||||
public function getSourceIdValues() {
|
||||
return array_intersect_key($this->source, $this->sourceIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a source has a property.
|
||||
*
|
||||
* @param string $property
|
||||
* A property on the source.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the source has property; FALSE otherwise.
|
||||
*/
|
||||
public function hasSourceProperty($property) {
|
||||
return isset($this->source[$property]) || array_key_exists($property, $this->source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a source property.
|
||||
*
|
||||
* @param string $property
|
||||
* A property on the source.
|
||||
*
|
||||
* @return mixed|null
|
||||
* The found returned property or NULL if not found.
|
||||
*/
|
||||
public function getSourceProperty($property) {
|
||||
if (isset($this->source[$property])) {
|
||||
return $this->source[$property];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whole source array.
|
||||
*
|
||||
* @return array
|
||||
* An array of source plugins.
|
||||
*/
|
||||
public function getSource() {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a source property.
|
||||
*
|
||||
* This can only be called from the source plugin.
|
||||
*
|
||||
* @param string $property
|
||||
* A property on the source.
|
||||
* @param mixed $data
|
||||
* The property value to set on the source.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setSourceProperty($property, $data) {
|
||||
if ($this->frozen) {
|
||||
throw new \Exception("The source is frozen and can't be changed any more");
|
||||
}
|
||||
else {
|
||||
$this->source[$property] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes the source.
|
||||
*/
|
||||
public function freezeSource() {
|
||||
$this->frozen = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if destination property exists.
|
||||
*
|
||||
* @param array|string $property
|
||||
* An array of properties on the destination.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the destination property exists.
|
||||
*/
|
||||
public function hasDestinationProperty($property) {
|
||||
return NestedArray::keyExists($this->destination, explode(':', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets destination properties.
|
||||
*
|
||||
* @param string $property
|
||||
* The name of the destination property.
|
||||
* @param mixed $value
|
||||
* The property value to set on the destination.
|
||||
*/
|
||||
public function setDestinationProperty($property, $value) {
|
||||
NestedArray::setValue($this->destination, explode(':', $property), $value, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whole destination array.
|
||||
*
|
||||
* @return array
|
||||
* An array of destination values.
|
||||
*/
|
||||
public function getDestination() {
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a destination property.
|
||||
*
|
||||
* @param array|string $property
|
||||
* An array of properties on the destination.
|
||||
*
|
||||
* @return mixed
|
||||
* The destination value.
|
||||
*/
|
||||
public function getDestinationProperty($property) {
|
||||
return NestedArray::getValue($this->destination, explode(':', $property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Migrate ID mappings.
|
||||
*
|
||||
* @param array $id_map
|
||||
* An array of mappings between source ID and destination ID.
|
||||
*/
|
||||
public function setIdMap(array $id_map) {
|
||||
$this->idMap = $id_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Migrate ID mappings.
|
||||
*
|
||||
* @return array
|
||||
* An array of mapping between source and destination identifiers.
|
||||
*/
|
||||
public function getIdMap() {
|
||||
return $this->idMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the hash for the row.
|
||||
*/
|
||||
public function rehash() {
|
||||
$this->idMap['original_hash'] = $this->idMap['hash'];
|
||||
$this->idMap['hash'] = hash('sha256', serialize($this->source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the row has changed compared to the original ID map.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the row has changed, FALSE otherwise. If setIdMap() was not
|
||||
* called, this always returns FALSE.
|
||||
*/
|
||||
public function changed() {
|
||||
return $this->idMap['original_hash'] != $this->idMap['hash'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this row needs an update.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the row needs updating, FALSE otherwise.
|
||||
*/
|
||||
public function needsUpdate() {
|
||||
return $this->idMap['needs_update'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash for the source values..
|
||||
*
|
||||
* @return mixed
|
||||
* The hash of the source values.
|
||||
*/
|
||||
public function getHash() {
|
||||
return $this->idMap['hash'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Plugin\migrate\source\SourceBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate;
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Source is a caching / decision making wrapper around the source plugin.
|
||||
*
|
||||
* Derived classes are expected to define __toString(), returning a string
|
||||
* describing the source and significant options, i.e. the query.
|
||||
*
|
||||
* @see \Drupal\migrate\MigrateSourceInterface
|
||||
*/
|
||||
class Source implements \Iterator, \Countable {
|
||||
|
||||
/**
|
||||
* The current row from the quey
|
||||
*
|
||||
* @var \Drupal\Migrate\Row
|
||||
*/
|
||||
protected $currentRow;
|
||||
|
||||
/**
|
||||
* The primary key of the current row
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $currentIds;
|
||||
|
||||
/**
|
||||
* Number of rows intentionally ignored (prepareRow() returned FALSE)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $numIgnored = 0;
|
||||
|
||||
/**
|
||||
* Number of rows we've at least looked at.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $numProcessed = 0;
|
||||
|
||||
/**
|
||||
* The highwater mark at the beginning of the import operation.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $originalHighwater = '';
|
||||
|
||||
/**
|
||||
* List of source IDs to process.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $idList = array();
|
||||
|
||||
/**
|
||||
* Whether this instance should cache the source count.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $cacheCounts = FALSE;
|
||||
|
||||
/**
|
||||
* Key to use for caching counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheKey;
|
||||
|
||||
/**
|
||||
* Whether this instance should not attempt to count the source.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $skipCount = FALSE;
|
||||
|
||||
/**
|
||||
* If TRUE, we will maintain hashed source rows to determine whether incoming
|
||||
* data has changed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $trackChanges = FALSE;
|
||||
|
||||
/**
|
||||
* By default, next() will directly read the map row and add it to the data
|
||||
* row. A source plugin implementation may do this itself (in particular, the
|
||||
* SQL source can incorporate the map table into the query) - if so, it should
|
||||
* set this TRUE so we don't duplicate the effort.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $mapRowAdded = FALSE;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceIds;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\MigrateIdMapInterface
|
||||
*/
|
||||
protected $idMap;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $highwaterProperty;
|
||||
|
||||
public function getCurrentIds() {
|
||||
return $this->currentIds;
|
||||
}
|
||||
|
||||
public function getIgnored() {
|
||||
return $this->numIgnored;
|
||||
}
|
||||
|
||||
public function getProcessed() {
|
||||
return $this->numProcessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset numIgnored back to 0.
|
||||
*/
|
||||
public function resetStats() {
|
||||
$this->numIgnored = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a count of available source records, from the cache if appropriate.
|
||||
* Returns -1 if the source is not countable.
|
||||
*
|
||||
* @param boolean $refresh
|
||||
* @return int
|
||||
*/
|
||||
public function count($refresh = FALSE) {
|
||||
if ($this->skipCount) {
|
||||
return -1;
|
||||
}
|
||||
$source = $this->migration->getSourcePlugin();
|
||||
|
||||
if (!isset($this->cacheKey)) {
|
||||
$this->cacheKey = hash('sha256', (string) $source);
|
||||
}
|
||||
|
||||
// If a refresh is requested, or we're not caching counts, ask the derived
|
||||
// class to get the count from the source.
|
||||
if ($refresh || !$this->cacheCounts) {
|
||||
$count = $source->count();
|
||||
$this->cache->set($this->cacheKey, $count, 'cache');
|
||||
}
|
||||
else {
|
||||
// Caching is in play, first try to retrieve a cached count.
|
||||
$cache_object = $this->cache->get($this->cacheKey, 'cache');
|
||||
if (is_object($cache_object)) {
|
||||
// Success
|
||||
$count = $cache_object->data;
|
||||
}
|
||||
else {
|
||||
// No cached count, ask the derived class to count 'em up, and cache
|
||||
// the result
|
||||
$count = $source->count();
|
||||
$this->cache->set($this->cacheKey, $count, 'cache');
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param \Drupal\migrate\Entity\MigrationInterface $migration
|
||||
* @param \Drupal\migrate\MigrateExecutable $migrate_executable
|
||||
*/
|
||||
function __construct(MigrationInterface $migration, MigrateExecutable $migrate_executable) {
|
||||
$this->migration = $migration;
|
||||
$this->migrateExecutable = $migrate_executable;
|
||||
$configuration = $migration->get('source');
|
||||
if (!empty($configuration['cache_counts'])) {
|
||||
$this->cacheCounts = TRUE;
|
||||
}
|
||||
if (!empty($configuration['skip_count'])) {
|
||||
$this->skipCount = TRUE;
|
||||
}
|
||||
if (!empty($configuration['cache_key'])) {
|
||||
$this->cacheKey = $configuration['cache_key'];
|
||||
}
|
||||
if (!empty($configuration['track_changes'])) {
|
||||
$this->trackChanges = $configuration['track_changes'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected function getCache() {
|
||||
if (!isset($this->cache)) {
|
||||
$this->cache = \Drupal::cache('migrate');
|
||||
}
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Iterator
|
||||
*/
|
||||
protected function getIterator() {
|
||||
if (!isset($this->iterator)) {
|
||||
$this->iterator = $this->migration->getSourcePlugin()->getIterator();
|
||||
}
|
||||
return $this->iterator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function current() {
|
||||
return $this->currentRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::key - called when entering a loop iteration, returning
|
||||
* the key of the current row. It must be a scalar - we will serialize
|
||||
* to fulfill the requirement, but using getCurrentIds() is preferable.
|
||||
*/
|
||||
public function key() {
|
||||
return serialize($this->currentIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::valid() - called at the top of the loop, returning
|
||||
* TRUE to process the loop and FALSE to terminate it
|
||||
*/
|
||||
public function valid() {
|
||||
return isset($this->currentRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Iterator::rewind() - subclasses of MigrateSource should
|
||||
* implement performRewind() to do any class-specific setup for iterating
|
||||
* source records.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->idMap = $this->migration->getIdMap();
|
||||
$this->numProcessed = 0;
|
||||
$this->numIgnored = 0;
|
||||
$this->originalHighwater = $this->migration->getHighwater();
|
||||
$this->highwaterProperty = $this->migration->get('highwaterProperty');
|
||||
if ($id_list = $this->migration->get('idlist')) {
|
||||
$this->idList = $id_list;
|
||||
}
|
||||
$this->getIterator()->rewind();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function next() {
|
||||
$this->currentIds = NULL;
|
||||
$this->currentRow = NULL;
|
||||
|
||||
while ($this->getIterator()->valid()) {
|
||||
$row_data = $this->getIterator()->current();
|
||||
$this->getIterator()->next();
|
||||
$row = new Row($row_data, $this->migration->get('sourceIds'), $this->migration->get('destinationIds'));
|
||||
|
||||
// Populate the source key for this row
|
||||
$this->currentIds = $row->getSourceIdValues();
|
||||
|
||||
// Pick up the existing map row, if any, unless getNextRow() did it.
|
||||
if (!$this->mapRowAdded && ($id_map = $this->idMap->getRowBySource($this->currentIds))) {
|
||||
$row->setIdMap($id_map);
|
||||
}
|
||||
|
||||
// First, determine if this row should be passed to prepareRow(), or
|
||||
// skipped entirely. The rules are:
|
||||
// 1. If there's an explicit idlist, that's all we care about (ignore
|
||||
// highwaters and map rows).
|
||||
$prepared = FALSE;
|
||||
if (!empty($this->idList)) {
|
||||
if (in_array(reset($this->currentIds), $this->idList)) {
|
||||
// In the list, fall through.
|
||||
}
|
||||
else {
|
||||
// Not in the list, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 2. If the row is not in the map (we have never tried to import it
|
||||
// before), we always want to try it.
|
||||
elseif (!$row->getIdMap()) {
|
||||
// Fall through
|
||||
}
|
||||
// 3. If the row is marked as needing update, pass it.
|
||||
elseif ($row->needsUpdate()) {
|
||||
// Fall through
|
||||
}
|
||||
// 4. At this point, we have a row which has previously been imported and
|
||||
// not marked for update. If we're not using highwater marks, then we
|
||||
// will not take this row. Except, if we're looking for changes in the
|
||||
// data, we need to go through prepareRow() before we can decide to
|
||||
// skip it.
|
||||
elseif (!empty($highwater['field'])) {
|
||||
if ($this->trackChanges) {
|
||||
if ($this->prepareRow($row) !== FALSE) {
|
||||
if ($row->changed()) {
|
||||
// This is a keeper
|
||||
$this->currentRow = $row;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// No change, skip it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// prepareRow() told us to skip it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No highwater and not tracking changes, skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 5. The initial highwater mark, before anything is migrated, is ''. We
|
||||
// want to make sure we don't mistakenly skip rows with a highwater
|
||||
// field value of 0, so explicitly handle '' here.
|
||||
elseif ($this->originalHighwater === '') {
|
||||
// Fall through
|
||||
}
|
||||
// 6. So, we are using highwater marks. Take the row if its highwater
|
||||
// field value is greater than the saved mark, otherwise skip it.
|
||||
else {
|
||||
// Call prepareRow() here, in case the highwaterField needs preparation
|
||||
if ($this->prepareRow($row) !== FALSE) {
|
||||
if ($row->getSourceProperty($this->highwaterProperty['name']) > $this->originalHighwater) {
|
||||
$this->currentRow = $row;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$prepared = TRUE;
|
||||
}
|
||||
|
||||
// Allow the Migration to prepare this row. prepareRow() can return boolean
|
||||
// FALSE to ignore this row.
|
||||
if (!$prepared) {
|
||||
if ($this->prepareRow($row) !== FALSE) {
|
||||
// Finally, we've got a keeper.
|
||||
$this->currentRow = $row;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
$this->currentRow = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->currentRow) {
|
||||
$this->currentRow->freezeSource();
|
||||
}
|
||||
else {
|
||||
$this->currentIds = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source classes should override this as necessary and manipulate $keep.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
*/
|
||||
protected function prepareRow(Row $row) {
|
||||
// We're explicitly skipping this row - keep track in the map table
|
||||
if ($this->migration->getSourcePlugin()->prepareRow($row) === FALSE) {
|
||||
// Make sure we replace any previous messages for this item with any
|
||||
// new ones.
|
||||
$id_map = $this->migration->getIdMap();
|
||||
$id_map->delete($this->currentIds, TRUE);
|
||||
$this->migrateExecutable->saveQueuedMessages();
|
||||
$id_map->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_IGNORED, $this->migrateExecutable->rollbackAction);
|
||||
$this->numIgnored++;
|
||||
$this->currentRow = NULL;
|
||||
$this->currentIds = NULL;
|
||||
}
|
||||
else {
|
||||
// When tracking changed data, We want to quietly skip (rather than
|
||||
// "ignore") rows with changes. The caller needs to make that decision,
|
||||
// so we need to provide them with the necessary information (before and
|
||||
// after hashes).
|
||||
if ($this->trackChanges) {
|
||||
$row->rehash();
|
||||
}
|
||||
}
|
||||
$this->numProcessed++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\Drupal6SystemCron.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests\Dump;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Database dump for testing system.cron.yml migration.
|
||||
*/
|
||||
class Drupal6SystemCron {
|
||||
|
||||
/**
|
||||
* Sample database schema and values.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
*/
|
||||
public static function load(Connection $database) {
|
||||
$database->schema()->createTable('variable', array(
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'value' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'name',
|
||||
),
|
||||
'module' => 'system',
|
||||
'name' => 'variable',
|
||||
));
|
||||
$database->insert('variable')->fields(array(
|
||||
'name',
|
||||
'value',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'cron_threshold_warning',
|
||||
'value' => 'i:172800;',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'cron_threshold_error',
|
||||
'value' => 'i:1209600;',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\Drupal6SystemRss.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests\Dump;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Database dump for testing system.settings.yml migration.
|
||||
*/
|
||||
class Drupal6SystemRss {
|
||||
|
||||
/**
|
||||
* Sample database schema and values.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
*/
|
||||
public static function load(Connection $database) {
|
||||
$database->schema()->createTable('variable', array(
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'value' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'name',
|
||||
),
|
||||
'module' => 'system',
|
||||
'name' => 'variable',
|
||||
));
|
||||
$database->insert('variable')->fields(array(
|
||||
'name',
|
||||
'value',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'feed_default_items',
|
||||
'value' => 'i:10;',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\migrate\Tests\Dump;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Database dump for testing system.site.yml migration.
|
||||
*/
|
||||
class Drupal6SystemSite {
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
*/
|
||||
public static function load(Connection $database) {
|
||||
$database->schema()->createTable('variable', array(
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'value' => array(
|
||||
'type' => 'blob',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array(
|
||||
'name',
|
||||
),
|
||||
'module' => 'system',
|
||||
'name' => 'variable',
|
||||
));
|
||||
$database->insert('variable')->fields(array(
|
||||
'name',
|
||||
'value',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_name',
|
||||
'value' => 's:6:"drupal";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_mail',
|
||||
'value' => 's:17:"admin@example.com";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_slogan',
|
||||
'value' => 's:13:"Migrate rocks";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_frontpage',
|
||||
'value' => 's:12:"anonymous-hp";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_403',
|
||||
'value' => 's:4:"user";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'site_404',
|
||||
'value' => 's:14:"page-not-found";',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'drupal_weight_select_max',
|
||||
'value' => 'i:99;',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'admin_compact_mode',
|
||||
'value' => 'b:0;',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Upgrade\MigrateSystemSiteTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\migrate\MigrateMessage;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
|
||||
class MigrateSystemConfigsTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Migrate variables to system.*.yml',
|
||||
'description' => 'Upgrade variables to system.*.yml',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
function testSystemSite() {
|
||||
$migration = entity_load('migration', 'd6_system_site');
|
||||
$dumps = array(
|
||||
drupal_get_path('module', 'migrate') . '/lib/Drupal/migrate/Tests/Dump/Drupal6SystemSite.php',
|
||||
);
|
||||
$this->prepare($migration, $dumps);
|
||||
$executable = new MigrateExecutable($migration, new MigrateMessage);
|
||||
$executable->import();
|
||||
$config = \Drupal::config('system.site');
|
||||
$this->assertIdentical($config->get('name'), 'drupal');
|
||||
$this->assertIdentical($config->get('mail'), 'admin@example.com');
|
||||
$this->assertIdentical($config->get('slogan'), 'Migrate rocks');
|
||||
$this->assertIdentical($config->get('page.front'), 'anonymous-hp');
|
||||
$this->assertIdentical($config->get('page.403'), 'user');
|
||||
$this->assertIdentical($config->get('page.404'), 'page-not-found');
|
||||
$this->assertIdentical($config->get('weight_select_max'), 99);
|
||||
$this->assertIdentical($config->get('admin_compact_mode'), FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of system (cron) variables to system.cron.yml.
|
||||
*/
|
||||
public function testSystemCron() {
|
||||
$migration = entity_load('migration', 'd6_system_cron');
|
||||
$dumps = array(
|
||||
drupal_get_path('module', 'migrate') . '/lib/Drupal/migrate/Tests/Dump/Drupal6SystemCron.php',
|
||||
);
|
||||
$this->prepare($migration, $dumps);
|
||||
$executable = new MigrateExecutable($migration, new MigrateMessage());
|
||||
$executable->import();
|
||||
$config = \Drupal::config('system.cron');
|
||||
$this->assertIdentical($config->get('threshold.warning'), 172800);
|
||||
$this->assertIdentical($config->get('threshold.error'), 1209600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of system (rss) variables to system.rss.yml.
|
||||
*/
|
||||
public function testSystemRss() {
|
||||
$migration = entity_load('migration', 'd6_system_rss');
|
||||
$dumps = array(
|
||||
drupal_get_path('module', 'migrate') . '/lib/Drupal/migrate/Tests/Dump/Drupal6SystemRss.php',
|
||||
);
|
||||
$this->prepare($migration, $dumps);
|
||||
$executable = new MigrateExecutable($migration, new MigrateMessage());
|
||||
$executable->import();
|
||||
$config = \Drupal::config('system.rss');
|
||||
$this->assertIdentical($config->get('items.limit'), 10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Upgrade\MigrateTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
class MigrateTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The file path(s) to the dumped database(s) to load into the child site.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $databaseDumpFiles = array();
|
||||
|
||||
public static $modules = array('migrate');
|
||||
|
||||
/**
|
||||
* @param MigrationInterface $migration
|
||||
* @param array $files
|
||||
*
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected function prepare(MigrationInterface $migration, array $files = array()) {
|
||||
$databasePrefix = 'm_';
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
foreach ($connection_info as $target => $value) {
|
||||
$connection_info[$target]['prefix'] = array(
|
||||
'default' => $value['prefix']['default'] . $databasePrefix,
|
||||
);
|
||||
}
|
||||
$database = SqlBase::getDatabaseConnection($migration->id(), array('database' => $connection_info['default']));
|
||||
foreach (array('source', 'destination', 'idMap') as $key) {
|
||||
$configuration = $migration->get($key);
|
||||
$configuration['database'] = $database;
|
||||
$migration->set($key, $configuration);
|
||||
}
|
||||
|
||||
// Load the database from the portable PHP dump.
|
||||
// The files may be gzipped.
|
||||
foreach ($files as $file) {
|
||||
if (substr($file, -3) == '.gz') {
|
||||
$file = "compress.zlib://$file";
|
||||
require $file;
|
||||
}
|
||||
preg_match('/^namespace (.*);$/m', file_get_contents($file), $matches);
|
||||
$class = $matches[1] . '\\' . basename($file, '.php');
|
||||
$class::load($database);
|
||||
}
|
||||
return $database;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Migrate module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows adding data to a row before processing it.
|
||||
*
|
||||
* For example, filter module used to store filter format settings in the
|
||||
* variables table which now needs to be inside the filter format config
|
||||
* file. So, it needs to be added here.
|
||||
*
|
||||
* hook_migrate_MIGRATION_ID_prepare_row is also available.
|
||||
*/
|
||||
function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
|
||||
if ($migration->id() == 'drupal6_filter_formats') {
|
||||
$value = $source->getDatabase()->query('SELECT value FROM {variable} WHERE name = :name', array(':name' => 'mymodule_filter_foo_' . $row->getSourceProperty('format')))->fetchField();
|
||||
if ($value) {
|
||||
$row->setSourceProperty('settings:mymodule:foo', unserialize($value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
|
@ -0,0 +1,7 @@
|
|||
name: Migrate
|
||||
type: module
|
||||
description: 'Handles migrations'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
;configure: admin/structure/migrate
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
cache.migrate:
|
||||
class: Drupal\Core\Cache\CacheBackendInterface
|
||||
tags:
|
||||
- { name: cache.bin }
|
||||
factory_method: get
|
||||
factory_service: cache_factory
|
||||
arguments: [migrate]
|
||||
plugin.manager.migrate.source:
|
||||
class: Drupal\migrate\Plugin\MigratePluginManager
|
||||
arguments: [source, '@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
||||
plugin.manager.migrate.process:
|
||||
class: Drupal\migrate\Plugin\MigratePluginManager
|
||||
arguments: [process, '@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
||||
plugin.manager.migrate.destination:
|
||||
class: Drupal\migrate\Plugin\MigratePluginManager
|
||||
arguments: [destination, '@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
||||
plugin.manager.migrate.id_map:
|
||||
class: Drupal\migrate\Plugin\MigratePluginManager
|
||||
arguments: [id_map, '@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
||||
plugin.manager.migrate.entity_field:
|
||||
class: Drupal\migrate\Plugin\MigratePluginManager
|
||||
arguments: [entity_field, '@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\ConditionResolver.
|
||||
*/
|
||||
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
|
||||
class ConditionResolver {
|
||||
|
||||
/**
|
||||
* Match a row against a group of conditions.
|
||||
*
|
||||
* @param \Drupal\migrate\tests\DatabaseRowInterface $row
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\Condition $condition_group
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function matchGroup(DatabaseRowInterface $row, Condition $condition_group) {
|
||||
$conditions = $condition_group->conditions();
|
||||
$and = $conditions['#conjunction'] == 'AND';
|
||||
unset($conditions['#conjunction']);
|
||||
$match = TRUE;
|
||||
foreach ($conditions as $condition) {
|
||||
$match = $condition['field'] instanceof Condition ? static::matchGroup($row, $condition['field']) : static::matchSingle($row, $condition);
|
||||
// For AND, finish matching on the first fail. For OR, finish on first
|
||||
// success.
|
||||
if ($and != $match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a single row and its condition.
|
||||
*
|
||||
* @param \Drupal\migrate\tests\DatabaseRowInterface $row
|
||||
* The row to match.
|
||||
*
|
||||
* @param array $condition
|
||||
* An array representing a single condition.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the condition matches.
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
*/
|
||||
protected static function matchSingle(DatabaseRowInterface $row, array $condition) {
|
||||
$row_value = $row->getValue($condition['field']);
|
||||
switch ($condition['operator']) {
|
||||
case '=':
|
||||
return $row_value == $condition['value'];
|
||||
|
||||
case '<=':
|
||||
return $row_value <= $condition['value'];
|
||||
|
||||
case '>=':
|
||||
return $row_value >= $condition['value'];
|
||||
|
||||
case '!=':
|
||||
return $row_value != $condition['value'];
|
||||
|
||||
case '<>':
|
||||
return $row_value != $condition['value'];
|
||||
|
||||
case '<':
|
||||
return $row_value < $condition['value'];
|
||||
|
||||
case '>':
|
||||
return $row_value > $condition['value'];
|
||||
|
||||
case 'IN':
|
||||
return in_array($row_value, $condition['value']);
|
||||
|
||||
case 'IS NULL':
|
||||
return !isset($row_value);
|
||||
|
||||
case 'IS NOT NULL':
|
||||
return isset($row_value);
|
||||
|
||||
default:
|
||||
throw new \Exception(sprintf('operator %s is not supported', $condition['operator']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\D6VariableSourceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
/**
|
||||
* @group migrate
|
||||
* @group Drupal
|
||||
*/
|
||||
class D6VariableTest extends MigrateSqlSourceTestCase {
|
||||
|
||||
const PLUGIN_CLASS = 'Drupal\migrate\Plugin\migrate\source\D6Variable';
|
||||
|
||||
protected $migrationConfiguration = array(
|
||||
'id' => 'test',
|
||||
'highwaterProperty' => array('field' => 'test'),
|
||||
'idlist' => array(),
|
||||
'source' => array(
|
||||
'plugin' => 'drupal6_variable',
|
||||
'variables' => array(
|
||||
'foo',
|
||||
'bar',
|
||||
),
|
||||
),
|
||||
'sourceIds' => array(),
|
||||
'destinationIds' => array(),
|
||||
);
|
||||
|
||||
protected $mapJoinable = FALSE;
|
||||
|
||||
protected $expectedResults = array(
|
||||
array(
|
||||
'foo' => 1,
|
||||
'bar' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
protected $databaseContents = array(
|
||||
'variable' => array(
|
||||
array('name' => 'foo', 'value' => 'i:1;'),
|
||||
array('name' => 'bar', 'value' => 'b:0;'),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'D6 variable source functionality',
|
||||
'description' => 'Tests D6 variable source plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Drupal\migrate\Tests\source;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\migrate\Plugin\migrate\source\D6Variable;
|
||||
|
||||
class TestD6Variable extends D6Variable {
|
||||
function setDatabase(Connection $database) {
|
||||
$this->database = $database;
|
||||
}
|
||||
function setModuleHandler(ModuleHandlerInterface $module_handler) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\DatabaseRow.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
class DatabaseRow implements DatabaseRowInterface {
|
||||
|
||||
public function __construct(array $row) {
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
public function getValue($field) {
|
||||
return $this->row[$field];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\DatabaseRowInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
interface DatabaseRowInterface {
|
||||
|
||||
function getValue($field);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\DatabaseRowSelect.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
class DatabaseRowSelect extends DatabaseRow {
|
||||
|
||||
public function __construct(array $row, array $fieldsWithTable, array $fields) {
|
||||
$this->fieldsWithTable = $fieldsWithTable;
|
||||
$this->fields = $fields;
|
||||
parent::__construct($row);
|
||||
}
|
||||
|
||||
public function getValue($field) {
|
||||
$field_info = isset($this->fieldsWithTable[$field]) ? $this->fieldsWithTable[$field] : $this->fields[$field];
|
||||
if (array_key_exists($field_info['field'], $this->row[$field_info['table']]['result'])) {
|
||||
$index = 'result';
|
||||
}
|
||||
else {
|
||||
$index = 'all';
|
||||
}
|
||||
return $this->row[$field_info['table']][$index][$field_info['field']];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\FakeSelect.
|
||||
*/
|
||||
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Database\Schema;
|
||||
|
||||
class FakeDatabaseSchema extends Schema {
|
||||
|
||||
/**
|
||||
* As set on MigrateSqlSourceTestCase::databaseContents.
|
||||
*/
|
||||
protected $databaseContents;
|
||||
|
||||
public function __construct($database_contents) {
|
||||
$this->uniqueIdentifier = uniqid('', TRUE);
|
||||
// @todo Maybe we can generate an internal representation.
|
||||
$this->databaseContents = $database_contents;
|
||||
}
|
||||
|
||||
public function tableExists($table) {
|
||||
return in_array($table, array_keys($this->databaseContents));
|
||||
}
|
||||
|
||||
public function prefixNonTable($table) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function addField($table, $field, $spec, $keys_new = array()) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function addIndex($table, $name, $fields) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function addPrimaryKey($table, $fields) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function addUniqueKey($table, $name, $fields) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function changeField($table, $field, $field_new, $spec, $keys_new = array()) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function __clone() {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function copyTable($source, $destination) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function createTable($name, $table) {
|
||||
#throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function dropField($table, $field) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function dropIndex($table, $name) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function dropPrimaryKey($table) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function dropTable($table) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function dropUniqueKey($table, $name) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function fieldExists($table, $column) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function fieldNames($fields) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function fieldSetDefault($table, $field, $default) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function fieldSetNoDefault($table, $field) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function findTables($table_expression) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function getFieldTypeMap() {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function indexExists($table, $name) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function nextPlaceholder() {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function prepareComment($comment, $length = NULL) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function renameTable($table, $new_name) {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
public function uniqueIdentifier() {
|
||||
throw new \Exception(sprintf('Unsupported method "%s"', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Fake database schema',
|
||||
'description' => 'Tests for fake database schema plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,511 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\FakeSelect.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Database\Query\PlaceholderInterface;
|
||||
use Drupal\Core\Database\Query\Select;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
|
||||
class FakeSelect extends Select {
|
||||
|
||||
/**
|
||||
* Contents of the pseudo-database.
|
||||
*
|
||||
* Keys are table names and values are arrays of rows in the table.
|
||||
* Every row there contains all table fields keyed by field name.
|
||||
*
|
||||
* @code
|
||||
* array(
|
||||
* 'user' => array(
|
||||
* array(
|
||||
* 'uid' => 1,
|
||||
* 'name' => 'admin',
|
||||
* ),
|
||||
* array(
|
||||
* 'uid' => 2,
|
||||
* 'name' => 'alice',
|
||||
* ),
|
||||
* ),
|
||||
* 'node' => array(
|
||||
* array(
|
||||
* 'nid' => 1,
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* @endcode
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseContents;
|
||||
|
||||
protected $countQuery = FALSE;
|
||||
protected $fieldsWithTable = array();
|
||||
|
||||
/**
|
||||
* Constructs a new FakeSelect.
|
||||
*
|
||||
* @param string $table
|
||||
* The base table name used within fake select.
|
||||
*
|
||||
* @param string $alias
|
||||
* The base table alias used within fake select.
|
||||
*
|
||||
* @param array $database_contents
|
||||
* An array of mocked database content.
|
||||
*
|
||||
* @param string $conjunction
|
||||
* The operator to use to combine conditions: 'AND' or 'OR'.
|
||||
*/
|
||||
public function __construct($table, $alias, array $database_contents, $conjunction = 'AND') {
|
||||
$this->addJoin(NULL, $table, $alias);
|
||||
$this->where = new Condition($conjunction);
|
||||
$this->having = new Condition($conjunction);
|
||||
$this->databaseContents = $database_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->addJoin('LEFT', $table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
if ($table instanceof SelectInterface) {
|
||||
// @todo implement this.
|
||||
throw new \Exception('Subqueries are not supported at this moment.');
|
||||
}
|
||||
$alias = parent::addJoin($type, $table, $alias, $condition, $arguments);
|
||||
if (isset($type)) {
|
||||
if ($type != 'INNER' && $type != 'LEFT') {
|
||||
throw new \Exception(sprintf('%s type not supported, only INNER and LEFT.', $type));
|
||||
}
|
||||
if (!preg_match('/(\w+)\.(\w+)\s*=\s*(\w+)\.(\w+)/', $condition, $matches)) {
|
||||
throw new \Exception('Only x.field1 = y.field2 conditions are supported.' . $condition);
|
||||
}
|
||||
if ($matches[1] == $alias) {
|
||||
$this->tables[$alias] += array(
|
||||
'added_field' => $matches[2],
|
||||
'original_table_alias' => $matches[3],
|
||||
'original_field' => $matches[4],
|
||||
);
|
||||
}
|
||||
elseif ($matches[3] == $alias) {
|
||||
$this->tables[$alias] += array(
|
||||
'added_field' => $matches[4],
|
||||
'original_table_alias' => $matches[1],
|
||||
'original_field' => $matches[2],
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new \Exception('The JOIN condition does not contain the alias of the joined table.');
|
||||
}
|
||||
}
|
||||
return $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute() {
|
||||
// @todo: Implement distinct() handling.
|
||||
|
||||
// Single table count queries often do not contain fields which this class
|
||||
// does not support otherwise, so add a shortcut.
|
||||
if (count($this->tables) == 1 && $this->countQuery) {
|
||||
$table_info = reset($this->tables);
|
||||
$where = $this->where;
|
||||
if (!empty($this->databaseContents[$table_info['table']])) {
|
||||
$results = array_filter($this->databaseContents[$table_info['table']], function ($row_array) use ($where) {
|
||||
return ConditionResolver::matchGroup(new DatabaseRow($row_array), $where);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$results = array();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$all_rows = $this->executeJoins();
|
||||
$all_rows = $this->resolveConditions($this->where, $all_rows);
|
||||
if (!empty($this->order)) {
|
||||
usort($all_rows, array($this, 'sortCallback'));
|
||||
}
|
||||
// Now flatten the rows so that each row becomes a field alias => value
|
||||
// array.
|
||||
$results = array();
|
||||
foreach ($all_rows as $table_rows) {
|
||||
$result_row = array();
|
||||
foreach ($table_rows as $row) {
|
||||
$result_row += $row['result'];
|
||||
}
|
||||
$results[] = $result_row;
|
||||
}
|
||||
}
|
||||
if (!empty($this->range)) {
|
||||
$results = array_slice($results, $this->range['start'], $this->range['length']);
|
||||
}
|
||||
if ($this->countQuery) {
|
||||
$results = array(array(count($results)));
|
||||
}
|
||||
return new FakeStatement($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an initial result set by executing the joins and picking fields.
|
||||
*
|
||||
* @return array
|
||||
* A multidimensional array, the first key are table aliases, the second
|
||||
* are field aliases, the values are the database contents or NULL in case
|
||||
* of JOINs.
|
||||
*/
|
||||
protected function executeJoins() {
|
||||
// @TODO add support for all_fields.
|
||||
$fields = array();
|
||||
foreach ($this->fields as $field_info) {
|
||||
$this->fieldsWithTable[$field_info['table'] . '.' . $field_info['field']] = $field_info;
|
||||
$fields[$field_info['table']][$field_info['field']] = NULL;
|
||||
}
|
||||
foreach ($this->tables as $alias => $table_info) {
|
||||
if ($table = reset($this->databaseContents[$table_info['table']])) {
|
||||
foreach (array_keys($table) as $field) {
|
||||
if (!isset($this->fields[$field])) {
|
||||
$this->fieldsWithTable[$field] = array(
|
||||
'table' => $alias,
|
||||
'field' => $field,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This will contain a multiple dimensional array. The first key will be a
|
||||
// table alias, the second either result or all, the third will be a field
|
||||
// alias. all contains every field in the table with the original field
|
||||
// names while result contains only the fields requested. This allows for
|
||||
// filtering on fields that were not added via addField().
|
||||
$results = array();
|
||||
foreach ($this->tables as $table_alias => $table_info) {
|
||||
// The base table for this query.
|
||||
if (empty($table_info['join type'])) {
|
||||
foreach ($this->databaseContents[$table_info['table']] as $candidate_row) {
|
||||
$results[] = $this->getNewRow($table_alias, $fields, $candidate_row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$new_rows = array();
|
||||
|
||||
// Dynamically build a set of joined rows. Check existing rows and see
|
||||
// if they can be joined with incoming rows.
|
||||
foreach ($results as $row) {
|
||||
$joined = FALSE;
|
||||
foreach ($this->databaseContents[$table_info['table']] as $candidate_row) {
|
||||
if ($row[$table_info['original_table_alias']]['result'][$table_info['original_field']] == $candidate_row[$table_info['added_field']]) {
|
||||
$joined = TRUE;
|
||||
$new_rows[] = $this->getNewRow($table_alias, $fields, $candidate_row, $row);
|
||||
}
|
||||
}
|
||||
if (!$joined && $table_info['join type'] == 'LEFT') {
|
||||
// Because PHP doesn't scope their foreach statements,
|
||||
// $candidate_row may contain the last value assigned to it from the
|
||||
// previous statement.
|
||||
// @TODO: empty tables? Those are a problem.
|
||||
$keys = array_keys($candidate_row);
|
||||
$values = array_fill(0, count($keys), NULL);
|
||||
$new_row = array(
|
||||
'result' => $fields[$table_alias],
|
||||
'all' => array_combine($keys, $values),
|
||||
);
|
||||
$new_rows[] = array($table_alias => $new_row) + $row;
|
||||
}
|
||||
}
|
||||
$results = $new_rows;
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a new row.
|
||||
*
|
||||
* @param string $table_alias
|
||||
* @param array $fields
|
||||
* @param array $candidate_row
|
||||
* @param array $row
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getNewRow($table_alias, $fields, $candidate_row, $row = array()) {
|
||||
$new_row[$table_alias]['all'] = $candidate_row;
|
||||
foreach ($fields[$table_alias] as $field => $v) {
|
||||
$new_row[$table_alias]['result'][$field] = $candidate_row[$field];
|
||||
}
|
||||
return $new_row + $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countQuery() {
|
||||
$query = clone $this;
|
||||
return $query->setCountQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this query to be a count query.
|
||||
*/
|
||||
protected function setCountQuery() {
|
||||
$this->countQuery = TRUE;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* usort callback to order the results.
|
||||
*/
|
||||
protected function sortCallback($a, $b) {
|
||||
$a_row = new DatabaseRowSelect($a, $this->fieldsWithTable, $this->fields);
|
||||
$b_row = new DatabaseRowSelect($b, $this->fieldsWithTable, $this->fields);
|
||||
foreach ($this->order as $field => $direction) {
|
||||
$a_value = $a_row->getValue($field);
|
||||
$b_value = $b_row->getValue($field);
|
||||
if ($a_value != $b_value) {
|
||||
return (($a_value < $b_value) == ($direction == 'ASC')) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves conditions by removing non-matching rows.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\Condition $condition_group
|
||||
* The condition group to check.
|
||||
* @param array $rows
|
||||
* An array of rows excluding non-matching rows.
|
||||
*/
|
||||
protected function resolveConditions(Condition $condition_group, array &$rows) {
|
||||
$fields_with_table = $this->fieldsWithTable;
|
||||
$fields = $this->fields;
|
||||
return array_filter($rows, function ($row_array) use ($condition_group, $fields_with_table, $fields) {
|
||||
$row = new DatabaseRowSelect($row_array, $fields_with_table, $fields);
|
||||
return ConditionResolver::matchGroup($row, $condition_group);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orderBy($field, $direction = 'ASC') {
|
||||
$this->order[$field] = strtoupper($direction);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// ================== we could support these.
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function groupBy($field) {
|
||||
// @todo: Implement groupBy() method.
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function havingCondition($field, $value = NULL, $operator = NULL) {
|
||||
// @todo: Implement havingCondition() method.
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uniqueIdentifier() {
|
||||
// TODO: Implement uniqueIdentifier() method.
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
// ================== the rest won't be supported, ever.
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextPlaceholder() {
|
||||
// TODO: Implement nextPlaceholder() method.
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPrepared() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preExecute(SelectInterface $query = NULL) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extend($extender_name) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getExpressions() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getGroupBy() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getUnion() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function forUpdate($set = TRUE) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &conditions() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orderRandom() {
|
||||
// We could implement this but why bother.
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function union(SelectInterface $query, $type = '') {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addExpression($expression, $alias = NULL, $arguments = array()) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getTables() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments(PlaceholderInterface $query_place_holder = NULL) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getOrderBy() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getFields() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function arguments() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $query_place_holder) {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compiled() {
|
||||
throw new \Exception(sprintf('Method "%s" is not supported', __METHOD__));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Fake select test',
|
||||
'description' => 'Tests for fake select plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\FakeStatement.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Database\StatementInterface;
|
||||
|
||||
/**
|
||||
* Represents a fake prepared statement.
|
||||
*/
|
||||
class FakeStatement extends \ArrayIterator implements StatementInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($args = array(), $options = array()) {
|
||||
throw new \Exception('This method is not supported');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryString() {
|
||||
throw new \Exception('This method is not supported');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rowCount() {
|
||||
return $this->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchField($index = 0) {
|
||||
$row = array_values($this->current());
|
||||
$return = $row[$index];
|
||||
$this->next();
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAssoc() {
|
||||
$return = $this->current();
|
||||
$this->next();
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchCol($index = 0) {
|
||||
$return = array();
|
||||
foreach ($this as $row) {
|
||||
$row = array_values($row);
|
||||
$return[] = $row[$index];
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllKeyed($key_index = 0, $value_index = 1) {
|
||||
$return = array();
|
||||
foreach ($this as $row) {
|
||||
$row = array_values($row);
|
||||
$return[$row[$key_index]] = $row[$value_index];
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAllAssoc($key, $fetch = NULL) {
|
||||
$return = array();
|
||||
foreach ($this as $row) {
|
||||
$return[$row[$key]] = $row;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Fake statement test',
|
||||
'description' => 'Tests for fake statement plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\MigrateExecutableTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\migrate\Entity\MigrationInterface;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
|
||||
/**
|
||||
* Tests the migrate executable.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group migrate
|
||||
*
|
||||
* @covers \Drupal\migrate\Tests\MigrateExecutableTest
|
||||
*/
|
||||
class MigrateExecutableTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The mocked migration entity.
|
||||
*
|
||||
* @var \Drupal\migrate\Entity\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The mocked migrate message.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateMessageInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* The tested migrate executable.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateExecutable
|
||||
*/
|
||||
protected $executable;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Migrate executable',
|
||||
'description' => 'Tests the migrate executable.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->migration = $this->getMock('Drupal\migrate\Entity\MigrationInterface');
|
||||
$this->message = $this->getMock('Drupal\migrate\MigrateMessageInterface');
|
||||
$id_map = $this->getMock('Drupal\migrate\Plugin\MigrateIdMapInterface');
|
||||
|
||||
$this->migration->expects($this->any())
|
||||
->method('getIdMap')
|
||||
->will($this->returnValue($id_map));
|
||||
|
||||
$this->executable = new TestMigrateExecutable($this->migration, $this->message);
|
||||
$this->executable->setTranslationManager($this->getStringTranslationStub());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import with an incomplete rewinding.
|
||||
*/
|
||||
public function testImportWithFailingRewind() {
|
||||
$iterator = $this->getMock('\Iterator');
|
||||
$iterator->expects($this->once())
|
||||
->method('valid')
|
||||
->will($this->returnCallback(function() {
|
||||
throw new \Exception('invalid source iteration');
|
||||
}));
|
||||
$source = $this->getMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$source->expects($this->any())
|
||||
->method('getIterator')
|
||||
->will($this->returnValue($iterator));
|
||||
|
||||
$this->migration->expects($this->any())
|
||||
->method('getSourcePlugin')
|
||||
->will($this->returnValue($source));
|
||||
|
||||
// Ensure that a message with the proper message was added.
|
||||
$this->message->expects($this->once())
|
||||
->method('display')
|
||||
->with('Migration failed with source plugin exception: invalid source iteration');
|
||||
|
||||
$result = $this->executable->import();
|
||||
$this->assertEquals(MigrationInterface::RESULT_FAILED, $result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestMigrateExecutable extends MigrateExecutable {
|
||||
|
||||
public function setTranslationManager(TranslationInterface $translation_manager) {
|
||||
$this->translationManager = $translation_manager;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\MigrateSqlSourceTestCase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
/**
|
||||
* Provides setup and helper methods for Migrate module source tests.
|
||||
*/
|
||||
abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The tested source plugin.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\migrate\source\d6\Comment.
|
||||
*/
|
||||
protected $source;
|
||||
|
||||
protected $databaseContents = array();
|
||||
|
||||
const PLUGIN_CLASS = '';
|
||||
|
||||
const ORIGINAL_HIGHWATER = '';
|
||||
|
||||
protected $expectedResults = array();
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandlerInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$migration = $this->getMigration();
|
||||
$migration->expects($this->any())
|
||||
->method('getHighwater')
|
||||
->will($this->returnValue(static::ORIGINAL_HIGHWATER));
|
||||
// Need the test class, not the original because we need a setDatabase method. This is not pretty :/
|
||||
$plugin_class = preg_replace('/^(Drupal\\\\\w+\\\\)Plugin\\\\migrate(\\\\source(\\\\.+)?\\\\)([^\\\\]+)$/', '\1Tests\2Test\4', static::PLUGIN_CLASS);
|
||||
$plugin = new $plugin_class($this->migrationConfiguration['source'], $this->migrationConfiguration['source']['plugin'], array(), $migration);
|
||||
$plugin->setDatabase($this->getDatabase($this->databaseContents + array('test_map' => array())));
|
||||
$plugin->setModuleHandler($module_handler);
|
||||
$migration->expects($this->any())
|
||||
->method('getSourcePlugin')
|
||||
->will($this->returnValue($plugin));
|
||||
$migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->source = new TestSource($migration, $migrateExecutable);
|
||||
|
||||
$cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$this->source->setCache($cache);
|
||||
}
|
||||
|
||||
public function testRetrieval() {
|
||||
$this->queryResultTest($this->source, $this->expectedResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getValue($row, $key) {
|
||||
return $row->getSourceProperty($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'SQL source test',
|
||||
'description' => 'Tests for SQL source plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\MigrateTestCase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Provides setup and helper methods for Migrate module tests.
|
||||
*/
|
||||
abstract class MigrateTestCase extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @TODO: does this need to be derived from the source/destination plugin?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $mapJoinable = TRUE;
|
||||
|
||||
protected $migrationConfiguration = array();
|
||||
|
||||
/**
|
||||
* Retrieve a mocked migration.
|
||||
*
|
||||
* @return \Drupal\migrate\Entity\MigrationInterface
|
||||
* The mocked migration.
|
||||
*/
|
||||
protected function getMigration() {
|
||||
$idmap = $this->getMock('Drupal\migrate\Plugin\MigrateIdMapInterface');
|
||||
if ($this->mapJoinable) {
|
||||
$idmap->expects($this->once())
|
||||
->method('getQualifiedMapTable')
|
||||
->will($this->returnValue('test_map'));
|
||||
}
|
||||
|
||||
$migration = $this->getMock('Drupal\migrate\Entity\MigrationInterface');
|
||||
$migration->expects($this->any())
|
||||
->method('getIdMap')
|
||||
->will($this->returnValue($idmap));
|
||||
$configuration = $this->migrationConfiguration;
|
||||
$migration->expects($this->any())->method('get')->will($this->returnCallback(function ($argument) use ($configuration) {
|
||||
return isset($configuration[$argument]) ? $configuration[$argument] : '';
|
||||
}));
|
||||
$migration->expects($this->any())
|
||||
->method('id')
|
||||
->will($this->returnValue($configuration['id']));
|
||||
return $migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected function getDatabase($database_contents) {
|
||||
$database = $this->getMockBuilder('Drupal\Core\Database\Connection')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$database->databaseContents = &$database_contents;
|
||||
$database->expects($this->any())
|
||||
->method('select')->will($this->returnCallback(function ($base_table, $base_alias) use ($database_contents) {
|
||||
return new FakeSelect($base_table, $base_alias, $database_contents);
|
||||
}));
|
||||
$database->expects($this->any())
|
||||
->method('schema')
|
||||
->will($this->returnCallback(function () use (&$database_contents) {
|
||||
return new FakeDatabaseSchema($database_contents);
|
||||
}));
|
||||
$database->expects($this->any())
|
||||
->method('insert')
|
||||
->will($this->returnCallback(function ($table) use (&$database_contents) {
|
||||
return new FakeInsert($database_contents, $table);
|
||||
}));
|
||||
$database->expects($this->any())
|
||||
->method('update')
|
||||
->will($this->returnCallback(function ($table) use (&$database_contents) {
|
||||
return new FakeUpdate($database_contents, $table);
|
||||
}));
|
||||
$database->expects($this->any())
|
||||
->method('merge')
|
||||
->will($this->returnCallback(function ($table) use (&$database_contents) {
|
||||
return new FakeMerge($database_contents, $table);
|
||||
}));
|
||||
$database->expects($this->any())
|
||||
->method('query')
|
||||
->will($this->throwException(new \Exception('Query is not supported')));
|
||||
return $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a query
|
||||
*
|
||||
* @param array|\Traversable
|
||||
* The countable. foreach-able actual results if a query is being run.
|
||||
*/
|
||||
public function queryResultTest($iter, $expected_results) {
|
||||
$this->assertSame(count($expected_results), count($iter), 'Number of results match');
|
||||
$count = 0;
|
||||
foreach ($iter as $data_row) {
|
||||
$expected_row = $expected_results[$count];
|
||||
$count++;
|
||||
foreach ($expected_row as $key => $expected_value) {
|
||||
$this->retrievalAssertHelper($expected_value, $this->getValue($data_row, $key), sprintf('Value matches for key "%s"', $key));
|
||||
}
|
||||
}
|
||||
$this->assertSame(count($expected_results), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getValue($row, $key) {
|
||||
return $row[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts tested values during test retrieval.
|
||||
*
|
||||
* @param mixed $expected_value
|
||||
* The incoming expected value to test.
|
||||
* @param mixed $actual_value
|
||||
* The incoming value itself.
|
||||
* @param string $message
|
||||
* The tested result as a formatted string.
|
||||
*/
|
||||
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 . ']');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertSame((string) $expected_value, (string) $actual_value, $message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\TestSource.
|
||||
*/
|
||||
|
||||
|
||||
namespace Drupal\migrate\Tests;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\migrate\Source;
|
||||
|
||||
class TestSource extends Source {
|
||||
function setCache(CacheBackendInterface $cache) {
|
||||
$this->cache = $cache;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests\process;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\process\TestGet;
|
||||
|
||||
/**
|
||||
* @group migrate
|
||||
* @group Drupal
|
||||
*/
|
||||
class GetTest extends MigrateProcessTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Get process plugin',
|
||||
'description' => 'Tests the get process plugin.',
|
||||
'group' => 'Migrate',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
$this->plugin = new TestGet();
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
function testTransformSourceString() {
|
||||
$this->row->expects($this->once())
|
||||
->method('getSourceProperty')
|
||||
->with('test')
|
||||
->will($this->returnValue('source_value'));
|
||||
$this->plugin->setSource('test');
|
||||
$value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($value, 'source_value');
|
||||
}
|
||||
|
||||
function testTransformSourceArray() {
|
||||
$map = array(
|
||||
'test1' => 'source_value1',
|
||||
'test2' => 'source_value2',
|
||||
);
|
||||
$this->plugin->setSource(array('test1', 'test2'));
|
||||
$this->row->expects($this->exactly(2))
|
||||
->method('getSourceProperty')
|
||||
->will($this->returnCallback(function ($argument) use ($map) { return $map[$argument]; } ));
|
||||
$value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($value, array('source_value1', 'source_value2'));
|
||||
}
|
||||
|
||||
function testTransformSourceStringAt() {
|
||||
$this->row->expects($this->once())
|
||||
->method('getSourceProperty')
|
||||
->with('@test')
|
||||
->will($this->returnValue('source_value'));
|
||||
$this->plugin->setSource('@@test');
|
||||
$value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($value, 'source_value');
|
||||
}
|
||||
|
||||
function testTransformSourceArrayAt() {
|
||||
$map = array(
|
||||
'test1' => 'source_value1',
|
||||
'@test2' => 'source_value2',
|
||||
'@test3' => 'source_value3',
|
||||
'test4' => 'source_value4',
|
||||
);
|
||||
$this->plugin->setSource(array('test1', '@@test2', '@@test3', 'test4'));
|
||||
$this->row->expects($this->exactly(4))
|
||||
->method('getSourceProperty')
|
||||
->will($this->returnCallback(function ($argument) use ($map) { return $map[$argument]; } ));
|
||||
$value = $this->plugin->transform(NULL, $this->migrateExecutable, $this->row, 'destinationproperty');
|
||||
$this->assertSame($value, array('source_value1', 'source_value2', 'source_value3', 'source_value4'));
|
||||
}
|
||||
}
|
||||
|
||||
namespace Drupal\migrate\Plugin\migrate\process;
|
||||
|
||||
class TestGet extends Get {
|
||||
function __construct() {
|
||||
}
|
||||
function setSource($source) {
|
||||
$this->configuration['source'] = $source;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\migrate\Tests\process\MigrateProcessTestCase.
|
||||
*/
|
||||
|
||||
namespace Drupal\migrate\Tests\process;
|
||||
|
||||
use Drupal\migrate\Tests\MigrateTestCase;
|
||||
|
||||
abstract class MigrateProcessTestCase extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\migrate\process\TestGet
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Row
|
||||
*/
|
||||
protected $row;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\MigrateExecutable
|
||||
*/
|
||||
protected $migrateExecutable;
|
||||
|
||||
function setUp() {
|
||||
$this->row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue