diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 427380679de..059a70bef5f 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -87,6 +87,21 @@ and ``delete($tableName)``: You can convert a query builder to its SQL string representation by calling ``$queryBuilder->getSQL()`` or casting the object to string. +DISTINCT-Clause +~~~~~~~~~~~~~~~ + +The ``SELECT`` statement can be specified with a ``DISTINCT`` clause: + +.. code-block:: php + + select('name') + ->distinct() + ->from('users') + ; + WHERE-Clause ~~~~~~~~~~~~ diff --git a/lib/Doctrine/DBAL/Query/QueryBuilder.php b/lib/Doctrine/DBAL/Query/QueryBuilder.php index 50c08655ff3..587e26656ab 100644 --- a/lib/Doctrine/DBAL/Query/QueryBuilder.php +++ b/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -52,22 +52,28 @@ class QueryBuilder */ private $connection; + /* + * The default values of SQL parts collection + */ + private const SQL_PARTS_DEFAULTS = [ + 'select' => [], + 'distinct' => false, + 'from' => [], + 'join' => [], + 'set' => [], + 'where' => null, + 'groupBy' => [], + 'having' => null, + 'orderBy' => [], + 'values' => [], + ]; + /** * The array of SQL parts collected. * * @var mixed[] */ - private $sqlParts = [ - 'select' => [], - 'from' => [], - 'join' => [], - 'set' => [], - 'where' => null, - 'groupBy' => [], - 'having' => null, - 'orderBy' => [], - 'values' => [], - ]; + private $sqlParts = self::SQL_PARTS_DEFAULTS; /** * The complete SQL string for this query. @@ -469,6 +475,25 @@ public function select($select = null) return $this->add('select', $selects); } + /** + * Adds DISTINCT to the query. + * + * + * $qb = $conn->createQueryBuilder() + * ->select('u.id') + * ->distinct() + * ->from('users', 'u') + * + * + * @return $this This QueryBuilder instance. + */ + public function distinct() : self + { + $this->sqlParts['distinct'] = true; + + return $this; + } + /** * Adds an item that is to be returned in the query result. * @@ -1083,8 +1108,7 @@ public function resetQueryParts($queryPartNames = null) */ public function resetQueryPart($queryPartName) { - $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName]) - ? [] : null; + $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; $this->state = self::STATE_DIRTY; @@ -1098,7 +1122,8 @@ public function resetQueryPart($queryPartName) */ private function getSQLForSelect() { - $query = 'SELECT ' . implode(', ', $this->sqlParts['select']); + $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') . + implode(', ', $this->sqlParts['select']); $query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '') . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '') diff --git a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php index 610abc319a9..a210ef2fe73 100644 --- a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php @@ -50,6 +50,17 @@ public function testSimpleSelect() : void self::assertEquals('SELECT u.id FROM users u', (string) $qb); } + public function testSimpleSelectWithDistinct() : void + { + $qb = new QueryBuilder($this->conn); + + $qb->select('u.id') + ->distinct() + ->from('users', 'u'); + + self::assertEquals('SELECT DISTINCT u.id FROM users u', (string) $qb); + } + public function testSelectWithSimpleWhere() : void { $qb = new QueryBuilder($this->conn);