Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion src/Query/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ class QueryBuilder
*/
private ?QueryCacheProfile $resultCacheProfile = null;

/**
* The comment tags to be added to the SQL query.
*
* @var string[]
*/
private array $commentTags = [];

/**
* Initializes a new <tt>QueryBuilder</tt>.
*
Expand Down Expand Up @@ -358,13 +365,19 @@ public function executeStatement(): int|string
*/
public function getSQL(): string
{
return $this->sql ??= match ($this->type) {
if ($this->sql !== null) {
return $this->sql;
}

$sql = match ($this->type) {
QueryType::INSERT => $this->getSQLForInsert(),
QueryType::DELETE => $this->getSQLForDelete(),
QueryType::UPDATE => $this->getSQLForUpdate(),
QueryType::SELECT => $this->getSQLForSelect(),
QueryType::UNION => $this->getSQLForUnion(),
};

return $this->sql = $this->addCommentTagsToSQL($sql);
}

/**
Expand Down Expand Up @@ -1661,4 +1674,52 @@ public function disableResultCache(): self

return $this;
}

/**
* Adds a comment tag to the SQL query.
*
* This method adds a SQL comment that will be prepended to the generated SQL query.
* Multiple tags can be added and will appear in the order they were added.
*
* <code>
* $qb = $conn->createQueryBuilder()
* ->select('u.id', 'u.name')
* ->from('users', 'u')
* ->tagWith('This is a custom tag')
* ->tagWith('Another tag');
* </code>
*
* @param string $tag The comment tag to add to the query.
*
* @return $this This QueryBuilder instance.
*/
public function tagWith(string $tag): self
{
$this->commentTags[] = $tag;

$this->sql = null;

return $this;
}

/**
* Adds comment tags to the SQL query.
*
* @param string $sql The SQL query to add tags to.
*
* @return string The SQL query with comment tags prepended.
*/
private function addCommentTagsToSQL(string $sql): string
{
if (count($this->commentTags) === 0) {
return $sql;
}

$commentLines = [];
foreach ($this->commentTags as $tag) {
$commentLines[] = '-- ' . $tag;
}

return implode("\n", $commentLines) . "\n\n" . $sql;
}
}
122 changes: 122 additions & 0 deletions tests/Query/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1546,4 +1546,126 @@ public function testUnionAndOrderByReturnsUnionQueryWithOrderBy(): void
$qb->getSQL(),
);
}

public function testTagWithSingleTag(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id', 'u.name')
->from('users', 'u')
->tagWith('This is a test query');

self::assertEquals(
"-- This is a test query\n\nSELECT u.id, u.name FROM users u",
$qb->getSQL(),
);
}

public function testTagWithMultipleTags(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id', 'u.name')
->from('users', 'u')
->tagWith('First tag')
->tagWith('Second tag')
->tagWith('Third tag');

self::assertEquals(
"-- First tag\n-- Second tag\n-- Third tag\n\nSELECT u.id, u.name FROM users u",
$qb->getSQL(),
);
}

public function testTagWithInsertQuery(): void
{
$qb = new QueryBuilder($this->conn);
$qb->insert('users')
->values(['name' => '?', 'email' => '?'])
->tagWith('Insert operation');

self::assertEquals(
"-- Insert operation\n\nINSERT INTO users (name, email) VALUES(?, ?)",
$qb->getSQL(),
);
}

public function testTagWithUpdateQuery(): void
{
$qb = new QueryBuilder($this->conn);
$qb->update('users')
->set('name', '?')
->where('id = ?')
->tagWith('Update operation');

self::assertEquals(
"-- Update operation\n\nUPDATE users SET name = ? WHERE id = ?",
$qb->getSQL(),
);
}

public function testTagWithDeleteQuery(): void
{
$qb = new QueryBuilder($this->conn);
$qb->delete('users')
->where('id = ?')
->tagWith('Delete operation');

self::assertEquals(
"-- Delete operation\n\nDELETE FROM users WHERE id = ?",
$qb->getSQL(),
);
}

public function testTagWithNoTags(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id', 'u.name')
->from('users', 'u');

self::assertEquals(
'SELECT u.id, u.name FROM users u',
$qb->getSQL(),
);
}

public function testTagWithMethodChaining(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id')
->from('users', 'u')
->tagWith('Tag 1')
->where('u.id = ?')
->tagWith('Tag 2')
->orderBy('u.name');

self::assertEquals(
"-- Tag 1\n-- Tag 2\n\nSELECT u.id FROM users u WHERE u.id = ? ORDER BY u.name",
$qb->getSQL(),
);
}

public function testTagWithEmptyTag(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id')
->from('users', 'u')
->tagWith('');

self::assertEquals(
"-- \n\nSELECT u.id FROM users u",
$qb->getSQL(),
);
}

public function testTagWithSpecialCharacters(): void
{
$qb = new QueryBuilder($this->conn);
$qb->select('u.id')
->from('users', 'u')
->tagWith("Tag with 'quotes' and \"double quotes\"");

self::assertEquals(
"-- Tag with 'quotes' and \"double quotes\"\n\nSELECT u.id FROM users u",
$qb->getSQL(),
);
}
}
Loading