diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index d17234d29e..d40967567f 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -400,6 +400,34 @@ Multiple CTEs can be defined by calling the with method multiple times. Values of parameters used in a CTE should be defined in the main QueryBuilder. +Comments +~~~~~~~~~~~ + +To add comments to the query you can use the ``withComment()`` method which will add the comment at the top of the query: + +.. code-block:: php + + select('id', 'name') + ->from('users') + ->withComment('This is a comment'); + // /* This is a comment */ SELECT id, name FROM users + +Multiple comments can be added by calling the method multiple times: + +.. code-block:: php + + select('id', 'name') + ->from('users') + ->withComment('Comment 1') + ->withComment('Comment 2'); + // /* Comment 1 */ /* Comment 2 */ SELECT id, name FROM users + Building Expressions -------------------- diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php index a58680e76f..d2ccfc5cee 100644 --- a/src/Query/QueryBuilder.php +++ b/src/Query/QueryBuilder.php @@ -170,6 +170,13 @@ class QueryBuilder */ private array $commonTableExpressions = []; + /** + * Comments that will be added to the query. + * + * @var string[] + */ + private array $comments = []; + /** * The query cache profile used for caching results. */ @@ -358,7 +365,7 @@ public function executeStatement(): int|string */ public function getSQL(): string { - return $this->sql ??= match ($this->type) { + return $this->sql ??= $this->getComments() . match ($this->type) { QueryType::INSERT => $this->getSQLForInsert(), QueryType::DELETE => $this->getSQLForDelete(), QueryType::UPDATE => $this->getSQLForUpdate(), @@ -1625,4 +1632,23 @@ public function disableResultCache(): self return $this; } + + public function withComment(string $comment): self + { + $this->comments[] = $comment; + + $this->sql = null; + + return $this; + } + + private function getComments(): string + { + $comments = ''; + foreach ($this->comments as $comment) { + $comments .= sprintf('/* %s */ ', $comment); + } + + return $comments; + } } diff --git a/tests/Functional/Query/QueryBuilderTest.php b/tests/Functional/Query/QueryBuilderTest.php index 263cce4bf3..55243bc598 100644 --- a/tests/Functional/Query/QueryBuilderTest.php +++ b/tests/Functional/Query/QueryBuilderTest.php @@ -528,6 +528,22 @@ public function testSelectWithCTEUnion(): void self::assertSame($expectedRows, $qb->executeQuery()->fetchAllAssociative()); } + public function testSelectWithComment(): void + { + $expectedRows = $this->prepareExpectedRows([['id' => 1], ['id' => 2]]); + $qb = $this->connection->createQueryBuilder(); + + $select = $qb + ->select('id') + ->from('for_update') + ->where('id IN (?, ?)') + ->setParameters([1, 2], [ParameterType::INTEGER, ParameterType::INTEGER]) + ->withComment('Test comment'); + + self::assertSame('/* Test comment */ SELECT id FROM for_update WHERE id IN (?, ?)', $select->getSQL()); + self::assertSame($expectedRows, $select->executeQuery()->fetchAllAssociative()); + } + public function testPlatformDoesNotSupportCTE(): void { if ($this->platformSupportsCTEs()) { diff --git a/tests/Query/QueryBuilderTest.php b/tests/Query/QueryBuilderTest.php index 09f6fe02dc..72e7dfde1a 100644 --- a/tests/Query/QueryBuilderTest.php +++ b/tests/Query/QueryBuilderTest.php @@ -65,6 +65,16 @@ public function testSimpleSelectWithoutFrom(): void self::assertEquals('SELECT some_function()', (string) $qb); } + public function testSimpleSelectWithComment(): void + { + $qb = new QueryBuilder($this->conn); + + $qb->select('some_function()') + ->withComment('Test comment'); + + self::assertEquals('/* Test comment */ SELECT some_function()', (string) $qb); + } + public function testSimpleSelect(): void { $qb = new QueryBuilder($this->conn); @@ -414,6 +424,17 @@ public function testUpdate(): void self::assertEquals('UPDATE users SET foo = ?, bar = ?', (string) $qb); } + public function testUpdateWithComment(): void + { + $qb = new QueryBuilder($this->conn); + $qb->update('users') + ->set('foo', '?') + ->set('bar', '?') + ->withComment('Test comment'); + + self::assertEquals('/* Test comment */ UPDATE users SET foo = ?, bar = ?', (string) $qb); + } + public function testUpdateWhere(): void { $qb = new QueryBuilder($this->conn); @@ -432,6 +453,15 @@ public function testDelete(): void self::assertEquals('DELETE FROM users', (string) $qb); } + public function testDeleteWithComment(): void + { + $qb = new QueryBuilder($this->conn); + $qb->delete('users'); + $qb->withComment('Test comment'); + + self::assertEquals('/* Test comment */ DELETE FROM users', (string) $qb); + } + public function testDeleteWhere(): void { $qb = new QueryBuilder($this->conn); @@ -455,6 +485,21 @@ public function testInsertValues(): void self::assertEquals('INSERT INTO users (foo, bar) VALUES(?, ?)', (string) $qb); } + public function testInsertValuesWithComment(): void + { + $qb = new QueryBuilder($this->conn); + $qb->insert('users') + ->values( + [ + 'foo' => '?', + 'bar' => '?', + ], + ) + ->withComment('Test comment'); + + self::assertEquals('/* Test comment */ INSERT INTO users (foo, bar) VALUES(?, ?)', (string) $qb); + } + public function testInsertReplaceValues(): void { $qb = new QueryBuilder($this->conn);