diff --git a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php index 3c6d4c49a38facf7a45d7d7998bc9a74851b33ad..0bb72e7d59ae296c0d6b7d57679ad6ebeab2e50f 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php +++ b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php @@ -15,13 +15,37 @@ use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Sources whose data may be fetched via DBTNG. + * Sources whose data may be fetched via a database connection. * - * By default, an existing database connection with key 'migrate' and target - * 'default' is used. These may be overridden with explicit 'key' and/or - * 'target' configuration keys. In addition, if the configuration key 'database' - * is present, it is used as a database connection information array to define - * the connection. + * Database configuration, which may appear either within the source plugin + * configuration or in state, is structured as follows: + * + * 'key' - The database key name (defaults to 'migrate'). + * 'target' - The database target name (defaults to 'default'). + * 'database' - Database connection information as accepted by + * Database::addConnectionInfo(). If not present, the key/target is assumed + * to already be defined (e.g., in settings.php). + * + * This configuration info is obtained in the following order: + * + * 1. If the source plugin configuration contains a key 'database_state_key', + * its value is taken as the name of a state key which contains an array + * with the above database configuration. + * 2. Otherwise, if the source plugin configuration contains 'key', the above + * database configuration is obtained directly from the plugin configuration. + * 3. Otherwise, if the state 'migrate.fallback_state_key' exists, its value is + * taken as the name of a state key which contains an array with the above + * database configuration. + * 4. Otherwise, if a connection named 'migrate' exists, that is used as the + * database connection. + * 5. Otherwise, RequirementsException is thrown. + * + * It is strongly recommended that database connections be explicitly defined + * via 'database_state_key' or in the source plugin configuration. Defining + * migrate.fallback_state_key or a 'migrate' connection affects not only any + * migrations intended to use that particular connection, but all + * SqlBase-derived source plugins which do not have explicit database + * configuration. */ abstract class SqlBase extends SourcePluginBase implements ContainerFactoryPluginInterface, RequirementsInterface { @@ -101,16 +125,21 @@ public function __toString() { */ public function getDatabase() { if (!isset($this->database)) { - // See if the database info is in state - if not, fallback to - // configuration. + // Look first for an explicit state key containing the configuration. if (isset($this->configuration['database_state_key'])) { $this->database = $this->setUpDatabase($this->state->get($this->configuration['database_state_key'])); } + // Next, use explicit configuration in the source plugin. + elseif (isset($this->configuration['key'])) { + $this->database = $this->setUpDatabase($this->configuration); + } + // Next, try falling back to the global state key. elseif (($fallback_state_key = $this->state->get('migrate.fallback_state_key'))) { $this->database = $this->setUpDatabase($this->state->get($fallback_state_key)); } + // If all else fails, let setUpDatabase() fallback to the 'migrate' key. else { - $this->database = $this->setUpDatabase($this->configuration); + $this->database = $this->setUpDatabase([]); } } return $this->database; diff --git a/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php b/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php index ef15bd575a2728294df75091f5abea09a978cf03..faf064d21f7242000ccb9712b6f08f7c6fc1ef22 100644 --- a/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php +++ b/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\migrate\Kernel; +use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\migrate\source\TestSqlBase; use Drupal\Core\Database\Database; @@ -23,11 +24,24 @@ class SqlBaseTest extends MigrateTestBase { public function testConnectionTypes() { $sql_base = new TestSqlBase(); - // Check the default values. - $sql_base->setConfiguration([]); - $this->assertIdentical($sql_base->getDatabase()->getTarget(), 'default'); - $this->assertIdentical($sql_base->getDatabase()->getKey(), 'migrate'); + // Verify that falling back to the default 'migrate' connection (defined in + // the base class) works. + $this->assertSame($sql_base->getDatabase()->getTarget(), 'default'); + $this->assertSame($sql_base->getDatabase()->getKey(), 'migrate'); + + // Verify the fallback state key overrides the 'migrate' connection. + $target = 'test_fallback_target'; + $key = 'test_fallback_key'; + $config = ['target' => $target, 'key' => $key]; + $database_state_key = 'test_fallback_state'; + \Drupal::state()->set($database_state_key, $config); + \Drupal::state()->set('migrate.fallback_state_key', $database_state_key); + // Create a test connection using the default database configuration. + Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); + $this->assertSame($sql_base->getDatabase()->getTarget(), $target); + $this->assertSame($sql_base->getDatabase()->getKey(), $key); + // Verify that setting explicit connection information overrides fallbacks. $target = 'test_db_target'; $key = 'test_migrate_connection'; $config = ['target' => $target, 'key' => $key]; @@ -35,8 +49,8 @@ public function testConnectionTypes() { Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); // Validate we have injected our custom key and target. - $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); - $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); + $this->assertSame($sql_base->getDatabase()->getTarget(), $target); + $this->assertSame($sql_base->getDatabase()->getKey(), $key); // Now test we can have SqlBase create the connection from an info array. $sql_base = new TestSqlBase(); @@ -51,7 +65,7 @@ public function testConnectionTypes() { $sql_base->getDatabase(); // Validate the connection has been created with the right values. - $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); + $this->assertSame(Database::getConnectionInfo($key)[$target], $database); // Now, test this all works when using state to store db info. $target = 'test_state_db_target'; @@ -63,8 +77,8 @@ public function testConnectionTypes() { Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); // Validate we have injected our custom key and target. - $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); - $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); + $this->assertSame($sql_base->getDatabase()->getTarget(), $target); + $this->assertSame($sql_base->getDatabase()->getKey(), $key); // Now test we can have SqlBase create the connection from an info array. $sql_base = new TestSqlBase(); @@ -81,7 +95,16 @@ public function testConnectionTypes() { $sql_base->getDatabase(); // Validate the connection has been created with the right values. - $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); + $this->assertSame(Database::getConnectionInfo($key)[$target], $database); + + // Verify that falling back to 'migrate' when the connection is not defined + // throws a RequirementsException. + \Drupal::state()->delete('migrate.fallback_state_key'); + $sql_base->setConfiguration([]); + Database::renameConnection('migrate', 'fallback_connection'); + $this->setExpectedException(RequirementsException::class, + 'No database connection configured for source plugin'); + $sql_base->getDatabase(); } }