Skip to content

Commit

Permalink
Add Result::fetchAllKeyValue() and ::iterateKeyValue()
Browse files Browse the repository at this point in the history
  • Loading branch information
morozov committed Sep 24, 2020
1 parent 1a97b87 commit b2149b6
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/en/reference/data-retrieval-and-manipulation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
<?php
$users = $conn->fetchAllKeyValue('SELECT username, password FROM user');
/*
array(
'jwage' => 'changeme',
)
*/
fetchNumeric()
~~~~~~~~~~~~~~

Expand Down Expand Up @@ -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
<?php
foreach ($conn->iterateKeyValue('SELECT username, password FROM user') as $username => $password) {
// ...
}
delete()
~~~~~~~~~

Expand Down
23 changes: 23 additions & 0 deletions src/Abstraction/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<mixed,mixed>
*
* @throws Exception
*/
public function fetchAllKeyValue(): array;

/**
* Returns an iterator over the result set rows represented as numeric arrays.
*
Expand All @@ -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<mixed,mixed>
*
* @throws Exception
*/
public function iterateKeyValue(): Traversable;

/**
* Returns an iterator over the values of the first column of the result set.
*
Expand Down
34 changes: 34 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, mixed>|array<string, mixed> $params The query parameters.
* @param array<int, int|string>|array<string, int|string> $types The query parameter types.
*
* @return array<mixed,mixed>
*
* @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.
*
Expand Down Expand Up @@ -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<int, mixed>|array<string, mixed> $params The query parameters.
* @param array<int, int|string>|array<string, int|string> $types The query parameter types.
*
* @return Traversable<mixed,mixed>
*
* @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.
*
Expand Down
25 changes: 25 additions & 0 deletions src/Exception/NoKeyValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Doctrine\DBAL\Exception;

use Doctrine\DBAL\Exception;

use function sprintf;

/**
* @psalm-immutable
*
* @internal
*/
final class NoKeyValue extends Exception
{
public static function fromColumnCount(int $columnCount): self
{
return new self(
sprintf(
'Fetching as key-value pairs requires the result to contain at least 2 columns, %d given.',
$columnCount
)
);
}
}
45 changes: 45 additions & 0 deletions src/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\DBAL\Abstraction\Result as ResultInterface;
use Doctrine\DBAL\Driver\Exception as DriverException;
use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Exception\NoKeyValue;
use Traversable;

final class Result implements ResultInterface
Expand Down Expand Up @@ -96,6 +97,24 @@ public function fetchAllAssociative(): array
}
}

/**
* {@inheritDoc}
*
* @throws Exception
*/
public function fetchAllKeyValue(): array
{
$this->ensureHasKeyValue();

$data = [];

foreach ($this->fetchAllNumeric() as [$key, $value]) {
$data[$key] = $value;
}

return $data;
}

/**
* {@inheritDoc}
*
Expand Down Expand Up @@ -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<int,mixed>
*
Expand Down Expand Up @@ -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);
}
}
}
37 changes: 37 additions & 0 deletions tests/Functional/Connection/FetchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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([
Expand Down Expand Up @@ -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([
Expand Down

0 comments on commit b2149b6

Please sign in to comment.