From d9b67ba3945a2b4f8e12fdb8f600343cc2b55d77 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Fri, 2 Apr 2021 16:51:42 +0900 Subject: [PATCH] Predictable `QueryBuilder::executeQuery()` and `QueryBuilder::executeStatement()` This deprecates `QueryBuilder::execute()`, because its return type is unpredictable and raises issues with static analysis tools such as PHPStan. Instead you should use either `QueryBuilder::executeQuery()` or `QueryBuilder::executeStatement()`, depending on whether the queryBuilder is a query (SELECT) or a statement (INSERT, UPDATE, DELETE). You might also consider the use of the new shortcut methods, such as: - `fetchAllAssociative()` - `fetchAllAssociativeIndexed()` - `fetchAllKeyValue()` - `fetchAllNumeric()` - `fetchAssociative()` - `fetchFirstColumn()` - `fetchNumeric()` - `fetchOne()` This commit is a direct follow-up to https://github.com/doctrine/dbal/issues/4461 where those shortcut methods where introduced. --- UPGRADE.md | 17 ++++++++ src/Query/QueryBuilder.php | 26 ++++++++++++ tests/Query/QueryBuilderTest.php | 69 ++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index 0c1fba1e138..339f0eac900 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -36,6 +36,23 @@ Use `Connection::createSchemaManager()` instead. The usage of `Connection::$_expr` and `Connection::getExpressionBuilder()` is deprecated. Use `Connection::createExpressionBuilder()` instead. +## Deprecated `QueryBuilder::execute()` + +The usage of `QueryBuilder::execute()` is deprecated. Use either `QueryBuilder::executeQuery()` or +`QueryBuilder::executeStatement()`, depending on whether the queryBuilder is a query (SELECT) or a statement (INSERT, +UPDATE, DELETE). + +You might also consider the use of the new shortcut methods, such as: + +- `fetchAllAssociative()` +- `fetchAllAssociativeIndexed()` +- `fetchAllKeyValue()` +- `fetchAllNumeric()` +- `fetchAssociative()` +- `fetchFirstColumn()` +- `fetchNumeric()` +- `fetchOne()` + # Upgrade to 3.0 ## BC BREAK: leading colon in named parameter names not supported diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php index 24f1706fa38..09683dd64a9 100644 --- a/src/Query/QueryBuilder.php +++ b/src/Query/QueryBuilder.php @@ -300,9 +300,35 @@ public function fetchFirstColumn(): array return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes); } + /** + * Executes an SQL query (SELECT) and returns a Result. + * + * @throws Exception + */ + public function executeQuery(): Result + { + return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + } + + /** + * Executes an SQL statement and returns the number of affected rows. + * + * Should be used for INSERT, UPDATE and DELETE + * + * @return int The number of affected rows. + * + * @throws Exception + */ + public function executeStatement(): int + { + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); + } + /** * Executes this query using the bound parameters and their types. * + * @deprecated Use {@link executeQuery()} or {@link executeStatement()} instead. + * * @return Result|int * * @throws Exception diff --git a/tests/Query/QueryBuilderTest.php b/tests/Query/QueryBuilderTest.php index 3313375cb16..4230baf0f31 100644 --- a/tests/Query/QueryBuilderTest.php +++ b/tests/Query/QueryBuilderTest.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Query\QueryException; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use PHPUnit\Framework\MockObject\MockObject; @@ -1302,4 +1303,72 @@ public static function fetchProvider(): iterable 'SELECT id, username FROM user WHERE password = :password AND username != :username AND id != :id', ]; } + + /** + * @param list|array $parameters + * @param array|array $parameterTypes + * + * @dataProvider fetchProvider + */ + public function testExecuteQuery( + string $select, + string $from, + string $where, + array $parameters, + array $parameterTypes, + string $expectedSql + ): void { + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); + + $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes) + ->willReturn($mockedResult); + + $results = $qb->select($select) + ->from($from) + ->where($where) + ->setParameters($parameters, $parameterTypes) + ->executeQuery(); + + self::assertSame( + $mockedResult, + $results + ); + } + + public function testExecuteStatement(): void + { + $qb = new QueryBuilder($this->conn); + $mockedResult = 123; + $expectedSql = 'UPDATE users SET foo = ?, bar = ? WHERE bar = 1'; + + $parameters = [ + 'foo' => 'jwage', + 'bar' => false, + ]; + + $parameterTypes = [ + 'foo' => Types::STRING, + 'bar' => Types::BOOLEAN, + ]; + + $this->conn->expects(self::once()) + ->method('executeStatement') + ->with($expectedSql, $parameters, $parameterTypes) + ->willReturn($mockedResult); + + $results = $qb->update('users') + ->set('foo', '?') + ->set('bar', '?') + ->where('bar = 1') + ->setParameters($parameters, $parameterTypes) + ->executeStatement(); + + self::assertSame( + $mockedResult, + $results + ); + } }