From b2149b6d398a25fdfa0fd994696dce3c6fefda0a Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Wed, 23 Sep 2020 18:00:36 -0700 Subject: [PATCH] Add Result::fetchAllKeyValue() and ::iterateKeyValue() --- .../data-retrieval-and-manipulation.rst | 28 ++++++++++++ src/Abstraction/Result.php | 23 ++++++++++ src/Connection.php | 34 ++++++++++++++ src/Exception/NoKeyValue.php | 25 +++++++++++ src/Result.php | 45 +++++++++++++++++++ tests/Functional/Connection/FetchTest.php | 37 +++++++++++++++ 6 files changed, 192 insertions(+) create mode 100644 src/Exception/NoKeyValue.php diff --git a/docs/en/reference/data-retrieval-and-manipulation.rst b/docs/en/reference/data-retrieval-and-manipulation.rst index 8a88deb6a64..6dfbf75e0e4 100644 --- a/docs/en/reference/data-retrieval-and-manipulation.rst +++ b/docs/en/reference/data-retrieval-and-manipulation.rst @@ -379,6 +379,22 @@ Execute the query and fetch all results into an array: ) */ +fetchAllKeyValue() +~~~~~~~~~~~~~~~~~~~~~ + +Execute the query and fetch the first two columns into an associative array as keys and values respectively: + +.. code-block:: php + + fetchAllKeyValue('SELECT username, password FROM user'); + + /* + array( + 'jwage' => 'changeme', + ) + */ + fetchNumeric() ~~~~~~~~~~~~~~ @@ -425,6 +441,18 @@ Retrieve associative array of the first result row. There are also convenience methods for data manipulation queries: +iterateKeyValue() +~~~~~~~~~~~~~~~~~~~~~ + +Execute the query and iterate over the first two columns as keys and values respectively: + +.. code-block:: php + + iterateKeyValue('SELECT username, password FROM user') as $username => $password) { + // ... + } + delete() ~~~~~~~~~ diff --git a/src/Abstraction/Result.php b/src/Abstraction/Result.php index 93d0e4643e0..b63ce8e56db 100644 --- a/src/Abstraction/Result.php +++ b/src/Abstraction/Result.php @@ -14,6 +14,17 @@ */ interface Result extends DriverResult { + /** + * Returns an associative array with the keys mapped to the first column and the values mapped to the second column. + * + * The result must contain at least two columns. + * + * @return array + * + * @throws Exception + */ + public function fetchAllKeyValue(): array; + /** * Returns an iterator over the result set rows represented as numeric arrays. * @@ -32,6 +43,18 @@ public function iterateNumeric(): Traversable; */ public function iterateAssociative(): Traversable; + /** + * Returns an iterator over the result set with the keys mapped to the first column + * and the values mapped to the second column. + * + * The result must contain at least two columns. + * + * @return Traversable + * + * @throws Exception + */ + public function iterateKeyValue(): Traversable; + /** * Returns an iterator over the values of the first column of the result set. * diff --git a/src/Connection.php b/src/Connection.php index 715ab0f694c..096ee99f858 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -817,6 +817,23 @@ public function fetchAllAssociative(string $query, array $params = [], array $ty } } + /** + * Prepares and executes an SQL query and returns the result as an associative array with the keys + * mapped to the first column and the values mapped to the second column. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return array + * + * @throws Exception + */ + public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array + { + return $this->executeQuery($query, $params, $types)->fetchAllKeyValue(); + } + /** * Prepares and executes an SQL query and returns the result as an array of the first column values. * @@ -886,6 +903,23 @@ public function iterateAssociative(string $query, array $params = [], array $typ } } + /** + * Prepares and executes an SQL query and returns the result as an iterator with the keys + * mapped to the first column and the values mapped to the second column. + * + * @param string $query The SQL query. + * @param array|array $params The query parameters. + * @param array|array $types The query parameter types. + * + * @return Traversable + * + * @throws Exception + */ + public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable + { + return $this->executeQuery($query, $params, $types)->iterateKeyValue(); + } + /** * Prepares and executes an SQL query and returns the result as an iterator over the first column values. * diff --git a/src/Exception/NoKeyValue.php b/src/Exception/NoKeyValue.php new file mode 100644 index 00000000000..eaf06f0e56d --- /dev/null +++ b/src/Exception/NoKeyValue.php @@ -0,0 +1,25 @@ +ensureHasKeyValue(); + + $data = []; + + foreach ($this->fetchAllNumeric() as [$key, $value]) { + $data[$key] = $value; + } + + return $data; + } + /** * {@inheritDoc} * @@ -142,6 +161,20 @@ public function iterateAssociative(): Traversable } } + /** + * {@inheritDoc} + * + * @throws Exception + */ + public function iterateKeyValue(): Traversable + { + $this->ensureHasKeyValue(); + + foreach ($this->iterateNumeric() as [$key, $value]) { + yield $key => $value; + } + } + /** * @return Traversable * @@ -186,4 +219,16 @@ public function free(): void { $this->result->free(); } + + /** + * @throws Exception + */ + private function ensureHasKeyValue(): void + { + $columnCount = $this->columnCount(); + + if ($columnCount < 2) { + throw NoKeyValue::fromColumnCount($columnCount); + } + } } diff --git a/tests/Functional/Connection/FetchTest.php b/tests/Functional/Connection/FetchTest.php index ce6b5a37208..683ac538a04 100644 --- a/tests/Functional/Connection/FetchTest.php +++ b/tests/Functional/Connection/FetchTest.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Tests\Functional\Connection; +use Doctrine\DBAL\Exception\NoKeyValue; use Doctrine\DBAL\Tests\FunctionalTestCase; use Doctrine\DBAL\Tests\TestUtil; @@ -77,6 +78,24 @@ public function testFetchAllAssociative(): void ], $this->connection->fetchAllAssociative($this->query)); } + public function testFetchAllKeyValue(): void + { + self::assertEquals([ + 'foo' => 1, + 'bar' => 2, + 'baz' => 3, + ], $this->connection->fetchAllKeyValue($this->query)); + } + + public function testFetchAllKeyValueOneColumn(): void + { + $sql = $this->connection->getDatabasePlatform() + ->getDummySelectSQL(); + + $this->expectException(NoKeyValue::class); + $this->connection->fetchAllKeyValue($sql); + } + public function testFetchFirstColumn(): void { self::assertEquals([ @@ -113,6 +132,24 @@ public function testIterateAssociative(): void ], iterator_to_array($this->connection->iterateAssociative($this->query))); } + public function testIterateKeyValue(): void + { + self::assertEquals([ + 'foo' => 1, + 'bar' => 2, + 'baz' => 3, + ], iterator_to_array($this->connection->iterateKeyValue($this->query))); + } + + public function testIterateKeyValueOneColumn(): void + { + $sql = $this->connection->getDatabasePlatform() + ->getDummySelectSQL(); + + $this->expectException(NoKeyValue::class); + iterator_to_array($this->connection->iterateKeyValue($sql)); + } + public function testIterateColumn(): void { self::assertEquals([