summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php
blob: cd64f935ca1e6e6ec9309a3ebba006cd569f651d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
<?php

namespace Drupal\migrate_drupal;

use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\Statement\FetchAs;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\Plugin\RequirementsInterface;

/**
 * Configures the appropriate migrations for a given source Drupal database.
 */
trait MigrationConfigurationTrait {

  /**
   * The config factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The migration plugin manager service.
   *
   * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
   */
  protected $migrationPluginManager;

  /**
   * The state service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * The follow-up migration tags.
   *
   * @var string[]
   */
  protected $followUpMigrationTags;

  /**
   * Gets the database connection for the source Drupal database.
   *
   * @param array $database
   *   Database array representing the source Drupal database.
   *
   * @return \Drupal\Core\Database\Connection
   *   The database connection for the source Drupal database.
   */
  protected function getConnection(array $database) {
    // Set up the connection.
    Database::addConnectionInfo('upgrade', 'default', $database);
    $connection = Database::getConnection('default', 'upgrade');
    return $connection;
  }

  /**
   * Gets the system data from the system table of the source Drupal database.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   Database connection to the source Drupal database.
   *
   * @return array
   *   The system data from the system table of the source Drupal database.
   */
  protected function getSystemData(Connection $connection) {
    $system_data = [];
    try {
      $results = $connection->select('system', 's', [
        'fetch' => FetchAs::Associative,
      ])
        ->fields('s')
        ->execute();
      foreach ($results as $result) {
        $system_data[$result['type']][$result['name']] = $result;
      }
    }
    catch (DatabaseExceptionWrapper) {
      // The table might not exist for example in tests.
    }
    return $system_data;
  }

  /**
   * Creates the necessary state entries for SqlBase::getDatabase() to work.
   *
   * The state entities created here have to exist before migration plugin
   * instances are created so that derivers such as
   * \Drupal\taxonomy\Plugin\migrate\D6TermNodeDeriver can access the source
   * database.
   *
   * @param array $database
   *   The source database settings.
   * @param string $drupal_version
   *   The Drupal version.
   *
   * @see \Drupal\migrate\Plugin\migrate\source\SqlBase::getDatabase()
   */
  protected function createDatabaseStateSettings(array $database, $drupal_version) {
    $database_state['key'] = 'upgrade';
    $database_state['database'] = $database;
    $database_state_key = 'migrate_drupal_' . $drupal_version;
    $state = $this->getState();
    $state->set($database_state_key, $database_state);
    $state->set('migrate.fallback_state_key', $database_state_key);
  }

  /**
   * Gets the migrations for import.
   *
   * @param string $database_state_key
   *   The state key.
   * @param int $drupal_version
   *   The version of Drupal we're getting the migrations for.
   *
   * @return \Drupal\migrate\Plugin\MigrationInterface[]
   *   The migrations for import.
   */
  protected function getMigrations($database_state_key, $drupal_version) {
    $version_tag = 'Drupal ' . $drupal_version;
    /** @var \Drupal\migrate\Plugin\MigrationInterface[] $all_migrations */
    $all_migrations = $this->getMigrationPluginManager()->createInstancesByTag($version_tag);

    // Unset the node migrations that should not run based on the type of node
    // migration. That is, if this is a complete node migration then unset the
    // classic node migrations and if this is a classic node migration then
    // unset the complete node migrations.
    $type = NodeMigrateType::getNodeMigrateType(\Drupal::database(), $drupal_version);
    switch ($type) {
      case NodeMigrateType::NODE_MIGRATE_TYPE_COMPLETE:
        $patterns = '/(d' . $drupal_version . '_node:)|(d' . $drupal_version . '_node_translation:)|(d' . $drupal_version . '_node_revision:)|(d7_node_entity_translation:)/';
        break;

      case NodeMigrateType::NODE_MIGRATE_TYPE_CLASSIC:
        $patterns = '/(d' . $drupal_version . '_node_complete:)/';
        break;
    }
    foreach ($all_migrations as $key => $migrations) {
      if (preg_match($patterns, $key)) {
        unset($all_migrations[$key]);
      }
    }

    $migrations = [];
    foreach ($all_migrations as $migration) {
      // Skip migrations tagged with any of the follow-up migration tags. They
      // will be derived and executed after the migrations on which they depend
      // have been successfully executed.
      // @see Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface
      if (!empty(array_intersect($migration->getMigrationTags(), $this->getFollowUpMigrationTags()))) {
        continue;
      }

      try {
        // @todo https://drupal.org/node/2681867 We should be able to validate
        //   the entire migration at this point.
        $source_plugin = $migration->getSourcePlugin();
        if ($source_plugin instanceof RequirementsInterface) {
          $source_plugin->checkRequirements();
        }
        $destination_plugin = $migration->getDestinationPlugin();
        if ($destination_plugin instanceof RequirementsInterface) {
          $destination_plugin->checkRequirements();
        }
        $migrations[] = $migration;
      }
      catch (RequirementsException) {
        // Migrations which are not applicable given the source and destination
        // site configurations (e.g., what modules are enabled) will be silently
        // ignored.
      }
    }

    return $migrations;
  }

  /**
   * Returns the follow-up migration tags.
   *
   * @return string[]
   *   An array of follow-up migration tags.
   */
  protected function getFollowUpMigrationTags() {
    if ($this->followUpMigrationTags === NULL) {
      $this->followUpMigrationTags = $this->getConfigFactory()
        ->get('migrate_drupal.settings')
        ->get('follow_up_migration_tags') ?: [];
    }
    return $this->followUpMigrationTags;
  }

  /**
   * Determines what version of Drupal the source database contains.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection object.
   *
   * @return string|false
   *   A string representing the major branch of Drupal core (e.g. '6' for
   *   Drupal 6.x), or FALSE if no valid version is matched.
   */
  public static function getLegacyDrupalVersion(Connection $connection) {
    // Don't assume because a table of that name exists, that it has the columns
    // we're querying. Catch exceptions and report that the source database is
    // not Drupal.
    // Drupal 5/6/7 can be detected by the schema_version in the system table.
    if ($connection->schema()->tableExists('system')) {
      try {
        $version_string = $connection
          ->query('SELECT [schema_version] FROM {system} WHERE [name] = :module', [':module' => 'system'])
          ->fetchField();
      }
      catch (DatabaseExceptionWrapper) {
        // All database errors return FALSE.
      }
    }

    return match (TRUE) {
      !isset($version_string) => FALSE,
      (int) $version_string >= 6000 => substr($version_string, 0, 1),
      (int) $version_string >= 1000 => '5',
      default => FALSE,
    };
  }

  /**
   * Gets the config factory service.
   *
   * @return \Drupal\Core\Config\ConfigFactoryInterface
   *   The config factory service.
   */
  protected function getConfigFactory() {
    if (!$this->configFactory) {
      $this->configFactory = \Drupal::service('config.factory');
    }

    return $this->configFactory;
  }

  /**
   * Gets the migration plugin manager service.
   *
   * @return \Drupal\migrate\Plugin\MigrationPluginManagerInterface
   *   The migration plugin manager service.
   */
  protected function getMigrationPluginManager() {
    if (!$this->migrationPluginManager) {
      $this->migrationPluginManager = \Drupal::service('plugin.manager.migration');
    }

    return $this->migrationPluginManager;
  }

  /**
   * Gets the state service.
   *
   * @return \Drupal\Core\State\StateInterface
   *   The state service.
   */
  protected function getState() {
    if (!$this->state) {
      $this->state = \Drupal::service('state');
    }

    return $this->state;
  }

}