Skip to content
<?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);
}
}
<?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);
}
<?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();
}
<?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();
}
<?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());
}
}
<?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.
}
}
<?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'];
}
}
<?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;
}
}
<?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');
}
}
<?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;
}
}
<?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;
}
}
This diff is collapsed.
This diff is collapsed.
<?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();
}
}