diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php index 5b68cd9a8d5e9b6a89c8bbd39e0c8d3e968d9bad..ba1b0216c8ddf56c38610bb1fe0a6279dd90c4cf 100644 --- a/core/lib/Drupal/Core/Database/Connection.php +++ b/core/lib/Drupal/Core/Database/Connection.php @@ -2,6 +2,8 @@ namespace Drupal\Core\Database; +use Drupal\Core\Database\Query\Condition; + /** * Base Database API class. * @@ -865,6 +867,11 @@ public function getDriverClass($class) { } $driver_class = $this->connectionOptions['namespace'] . '\\' . $class; $this->driverClasses[$class] = class_exists($driver_class) ? $driver_class : $class; + if ($this->driverClasses[$class] === 'Condition') { + // @todo Deprecate the fallback for contrib and custom drivers in 9.1.x + // in https://www.drupal.org/project/drupal/issues/3120036. + $this->driverClasses[$class] = Condition::class; + } } return $this->driverClasses[$class]; } @@ -1026,6 +1033,22 @@ public function schema() { return $this->schema; } + /** + * Prepares and returns a CONDITION query object. + * + * @param string $conjunction + * The operator to use to combine conditions: 'AND' or 'OR'. + * + * @return \Drupal\Core\Database\Query\Condition + * A new Condition query object. + * + * @see \Drupal\Core\Database\Query\Condition + */ + public function condition($conjunction) { + $class = $this->getDriverClass('Condition'); + return new $class($conjunction); + } + /** * Escapes a database name string. * diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php index 1c7670a5a4c3058fcb1a57ce0bd764c408aa47df..d909f7c50b1f867f92c28310493052c94e8f21ea 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php @@ -2,7 +2,6 @@ namespace Drupal\Core\Database\Driver\mysql; -use Drupal\Core\Database\Query\Condition; use Drupal\Core\Database\SchemaException; use Drupal\Core\Database\SchemaObjectExistsException; use Drupal\Core\Database\SchemaObjectDoesNotExistException; @@ -75,7 +74,7 @@ protected function getPrefixInfo($table = 'default', $add_prefix = TRUE) { protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) { $table_info = $this->getPrefixInfo($table_name, $add_prefix); - $condition = new Condition('AND'); + $condition = $this->connection->condition('AND'); $condition->condition('table_schema', $table_info['database']); $condition->condition('table_name', $table_info['table'], $operator); return $condition; diff --git a/core/lib/Drupal/Core/Database/Query/Condition.php b/core/lib/Drupal/Core/Database/Query/Condition.php index e7038e23c91ee76f14392aa4d7f23fd200d48611..2515bbfdc89ae74e47b1088fbf1b43f67f137b0e 100644 --- a/core/lib/Drupal/Core/Database/Query/Condition.php +++ b/core/lib/Drupal/Core/Database/Query/Condition.php @@ -399,7 +399,7 @@ protected function mapConditionOperator($operator) { * {@inheritdoc} */ public function conditionGroupFactory($conjunction = 'AND') { - return new Condition($conjunction); + return new static($conjunction); } /** diff --git a/core/lib/Drupal/Core/Database/Query/Delete.php b/core/lib/Drupal/Core/Database/Query/Delete.php index def88385e0beff4b3696eec04710ed4515bfa4c2..658b8342ccded28a6854b13ee5b7d6d9706c42a5 100644 --- a/core/lib/Drupal/Core/Database/Query/Delete.php +++ b/core/lib/Drupal/Core/Database/Query/Delete.php @@ -36,7 +36,7 @@ public function __construct(Connection $connection, $table, array $options = []) parent::__construct($connection, $options); $this->table = $table; - $this->condition = new Condition('AND'); + $this->condition = $this->connection->condition('AND'); } /** diff --git a/core/lib/Drupal/Core/Database/Query/Merge.php b/core/lib/Drupal/Core/Database/Query/Merge.php index 6d895324727605ec3cebcc07c06612b6b389f7f4..5a9bc3db894494a49e1ea45d4b7f0bfceb16cc23 100644 --- a/core/lib/Drupal/Core/Database/Query/Merge.php +++ b/core/lib/Drupal/Core/Database/Query/Merge.php @@ -138,7 +138,7 @@ public function __construct(Connection $connection, $table, array $options = []) parent::__construct($connection, $options); $this->table = $table; $this->conditionTable = $table; - $this->condition = new Condition('AND'); + $this->condition = $this->connection->condition('AND'); } /** diff --git a/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php b/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php index 9053a17772aeaa062b86c0b75e85cdef2a437fee..83be429fa581cfa7e4e8360385877c4a72ba6289 100644 --- a/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php +++ b/core/lib/Drupal/Core/Database/Query/QueryConditionTrait.php @@ -108,7 +108,7 @@ public function compiled() { * {@inheritdoc} */ public function conditionGroupFactory($conjunction = 'AND') { - return new Condition($conjunction); + return $this->connection->condition($conjunction); } /** diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php index fb74d9f1c13eedaed115286508092d0a49476ec4..78462d0b638f9a1edb1a387e4b4ae5efc93d1abe 100644 --- a/core/lib/Drupal/Core/Database/Query/Select.php +++ b/core/lib/Drupal/Core/Database/Query/Select.php @@ -134,8 +134,8 @@ public function __construct(Connection $connection, $table, $alias = NULL, $opti $options['return'] = Database::RETURN_STATEMENT; parent::__construct($connection, $options); $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND'; - $this->condition = new Condition($conjunction); - $this->having = new Condition($conjunction); + $this->condition = $this->connection->condition($conjunction); + $this->having = $this->connection->condition($conjunction); $this->addJoin(NULL, $table, $alias); } diff --git a/core/lib/Drupal/Core/Database/Query/SelectExtender.php b/core/lib/Drupal/Core/Database/Query/SelectExtender.php index dc3531df2f965895e041fa9949702bb1c4d6ff76..e428c21d86bb85d2623136de6e5fae291bb1551a 100644 --- a/core/lib/Drupal/Core/Database/Query/SelectExtender.php +++ b/core/lib/Drupal/Core/Database/Query/SelectExtender.php @@ -521,7 +521,7 @@ public function __call($method, $args) { * {@inheritdoc} */ public function conditionGroupFactory($conjunction = 'AND') { - return new Condition($conjunction); + return $this->connection->condition($conjunction); } /** diff --git a/core/lib/Drupal/Core/Database/Query/Update.php b/core/lib/Drupal/Core/Database/Query/Update.php index 5de5d682ec3f541bef007d7df710cd60817e21c2..2b08e20d00184b0069806c6f8fa1e1bd591fd2df 100644 --- a/core/lib/Drupal/Core/Database/Query/Update.php +++ b/core/lib/Drupal/Core/Database/Query/Update.php @@ -65,7 +65,7 @@ public function __construct(Connection $connection, $table, array $options = []) parent::__construct($connection, $options); $this->table = $table; - $this->condition = new Condition('AND'); + $this->condition = $this->connection->condition('AND'); } /** diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php index 38fde96345c13c4fab2213a7afe69b229d50deeb..1987177a3e55bb89288b865ed8f7c7dfd09bd364 100644 --- a/core/lib/Drupal/Core/Database/Schema.php +++ b/core/lib/Drupal/Core/Database/Schema.php @@ -2,7 +2,6 @@ namespace Drupal\Core\Database; -use Drupal\Core\Database\Query\Condition; use Drupal\Core\Database\Query\PlaceholderInterface; /** @@ -150,7 +149,7 @@ protected function buildTableNameCondition($table_name, $operator = '=', $add_pr // Retrieve the table name and schema $table_info = $this->getPrefixInfo($table_name, $add_prefix); - $condition = new Condition('AND'); + $condition = $this->connection->condition('AND'); $condition->condition('table_catalog', $info['database']); $condition->condition('table_schema', $table_info['schema']); $condition->condition('table_name', $table_info['table'], $operator); diff --git a/core/modules/system/tests/modules/database_statement_monitoring_test/src/LoggedStatementsTrait.php b/core/modules/system/tests/modules/database_statement_monitoring_test/src/LoggedStatementsTrait.php index ceb98ea05d7dc09463637671ffe90b0e06578112..d4f760e093cfe33290c7525c0b3d255c4befb4ac 100644 --- a/core/modules/system/tests/modules/database_statement_monitoring_test/src/LoggedStatementsTrait.php +++ b/core/modules/system/tests/modules/database_statement_monitoring_test/src/LoggedStatementsTrait.php @@ -2,6 +2,8 @@ namespace Drupal\database_statement_monitoring_test; +use Drupal\Core\Database\Query\Condition; + /** * Trait for Connection classes that can store logged statements. */ @@ -48,7 +50,13 @@ public function getDriverClass($class) { // based on object, which would break. $namespace = (new \ReflectionClass(get_parent_class($this)))->getNamespaceName(); $driver_class = $namespace . '\\' . $class; - return class_exists($driver_class) ? $driver_class : $class; + if (class_exists($driver_class)) { + return $driver_class; + } + elseif ($class == 'Condition') { + return Condition::class; + } + return $class; } /** diff --git a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php index 7e0a5f89ac35674337d5fd18713d219b92d3ee5d..69b5de82fbde8bf42ed3d7475aa82b1319e4131a 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; +use Drupal\Core\Database\Query\Condition; /** * Tests of the core database system. @@ -155,4 +156,17 @@ public function testMultipleStatements() { } } + /** + * Test that the method ::condition() returns a Condition object. + */ + public function testCondition() { + $connection = Database::getConnection('default', 'default'); + $namespace = (new \ReflectionObject($connection))->getNamespaceName() . "\\Condition"; + if (!class_exists($namespace)) { + $namespace = Condition::class; + } + $condition = $connection->condition('AND'); + $this->assertSame($namespace, get_class($condition)); + } + } diff --git a/core/tests/Drupal/Tests/Core/Database/ConditionTest.php b/core/tests/Drupal/Tests/Core/Database/ConditionTest.php index c38261b86626ab0a3539a5b12746cd6c59d897c5..93eb0ef8a8bd8d74ffcf21411c4f4dca4483ce5a 100644 --- a/core/tests/Drupal/Tests/Core/Database/ConditionTest.php +++ b/core/tests/Drupal/Tests/Core/Database/ConditionTest.php @@ -5,6 +5,7 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Database\Query\Condition; use Drupal\Core\Database\Query\PlaceholderInterface; +use Drupal\Tests\Core\Database\Stub\StubPDO; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; use PHPUnit\Framework\Error\Error; @@ -176,4 +177,34 @@ public function providerTestCompileWithSqlInjectionForOperator() { return $data; } + /** + * Test that the core Condition can be overridden. + */ + public function testContribCondition() { + $mockCondition = $this->getMockBuilder(Condition::class) + ->setMockClassName('MockCondition') + ->setConstructorArgs([NULL]) + ->disableOriginalConstructor() + ->getMock(); + $contrib_namespace = 'Drupal\Driver\Database\mock'; + $mocked_namespace = $contrib_namespace . '\\Condition'; + class_alias('MockCondition', $mocked_namespace); + + $options['namespace'] = $contrib_namespace; + $options['prefix']['default'] = ''; + + $mockPdo = $this->createMock(StubPDO::class); + + $connection = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([$mockPdo, $options]) + ->setMethods(['identifierQuote']) + ->getMockForAbstractClass(); + // @todo In drupal:10.0.0 this function will be abstract and the mock + // builder will automatically create it. This can be + // can be removed at that time. + $connection->method('identifierQuote')->willReturn(NULL); + $condition = $connection->condition('AND'); + $this->assertSame('MockCondition', get_class($condition)); + } + }