Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Statement::fetchAllIndexedAssociative() and ::iterateIndexedAssociative() #4341

Merged
merged 1 commit into from
Oct 14, 2020
Merged
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
33 changes: 33 additions & 0 deletions docs/en/reference/data-retrieval-and-manipulation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,26 @@ Execute the query and fetch the first two columns into an associative array as k
.. note::
All additional columns will be ignored and are only allowed to be selected by DBAL for its internal purposes.

fetchAllAssociativeIndexed()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Execute the query and fetch the data as an associative array where the key represents the first column and the value is
an associative array of the rest of the columns and their values:

.. code-block:: php

<?php
$users = $conn->fetchAllAssociativeIndexed('SELECT id, username, password FROM user');

/*
array(
1 => array(
'username' => 'jwage',
'password' => 'changeme',
)
)
*/

fetchNumeric()
~~~~~~~~~~~~~~

Expand Down Expand Up @@ -459,6 +479,19 @@ Execute the query and iterate over the first two columns as keys and values resp
.. note::
All additional columns will be ignored and are only allowed to be selected by DBAL for its internal purposes.

iterateAssociativeIndexed()
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Execute the query and iterate over the result with the key representing the first column and the value being
an associative array of the rest of the columns and their values:

.. code-block:: php

<?php
foreach ($conn->iterateAssociativeIndexed('SELECT id, username, password FROM user') as $id => $data) {
// ...
}

delete()
~~~~~~~~~

Expand Down
61 changes: 55 additions & 6 deletions lib/Doctrine/DBAL/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Traversable;

use function array_key_exists;
use function array_shift;
use function assert;
use function func_get_args;
use function implode;
Expand Down Expand Up @@ -990,9 +991,9 @@ 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.
* @param string $query SQL query
* @param array<int, mixed>|array<string, mixed> $params Query parameters
* @param array<int, int|string>|array<string, int|string> $types Parameter types
*
* @return array<mixed,mixed>
*
Expand All @@ -1013,6 +1014,32 @@ public function fetchAllKeyValue(string $query, array $params = [], array $types
return $data;
}

/**
* 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 being an associative array representing the rest of the columns
* and their values.
*
* @param string $query SQL query
* @param array<int, mixed>|array<string, mixed> $params Query parameters
* @param array<int, int|string>|array<string, int|string> $types Parameter types
*
* @return array<mixed,array<string,mixed>>
*
* @throws Exception
*/
public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array
{
$stmt = $this->executeQuery($query, $params, $types);

$data = [];

foreach ($stmt->fetchAll(FetchMode::ASSOCIATIVE) as $row) {
$data[array_shift($row)] = $row;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to obtain a float from that call to array_shift? If yes, should we assert the result of the call is a valid array key? Bit weird that SA tools do not complain here… Using a float as a key is possible but has surprising results: https://3v4l.org/E1jGM

Copy link
Member Author

@morozov morozov Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When floats are used as array keys, they are cast to an integer. I don't think DBAL should do much about that. It can be not only float but NULL. It's up to the developer who uses this API to decide what they are using as a key.

}

return $data;
}

/**
* Prepares and executes an SQL query and returns the result as an array of the first column values.
*
Expand Down Expand Up @@ -1100,9 +1127,9 @@ 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.
* @param string $query SQL query
* @param array<int, mixed>|array<string, mixed> $params Query parameters
* @param array<int, int|string>|array<string, int|string> $types Parameter types
*
* @return Traversable<mixed,mixed>
*
Expand All @@ -1119,6 +1146,28 @@ public function iterateKeyValue(string $query, array $params = [], array $types
}
}

/**
* Prepares and executes an SQL query and returns the result as an iterator with the keys mapped
* to the first column and the values being an associative array representing the rest of the columns
* and their values.
*
* @param string $query SQL query
* @param array<int, mixed>|array<string, mixed> $params Query parameters
* @param array<int, int|string>|array<string, int|string> $types Parameter types
*
* @return Traversable<mixed,array<string,mixed>>
*
* @throws Exception
*/
public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable
{
$stmt = $this->executeQuery($query, $params, $types);

while (($row = $stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
yield array_shift($row) => $row;
}
}

/**
* Prepares and executes an SQL query and returns the result as an iterator over the first column values.
*
Expand Down
35 changes: 35 additions & 0 deletions lib/Doctrine/DBAL/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Throwable;
use Traversable;

use function array_shift;
use function is_array;
use function is_string;

Expand Down Expand Up @@ -398,6 +399,25 @@ public function fetchAllKeyValue(): array
return $data;
}

/**
* Returns an associative array with the keys mapped to the first column and the values being
* an associative array representing the rest of the columns and their values.
*
* @return array<mixed,array<string,mixed>>
*
* @throws Exception
*/
public function fetchAllAssociativeIndexed(): array
{
$data = [];

foreach ($this->fetchAll(FetchMode::ASSOCIATIVE) as $row) {
$data[array_shift($row)] = $row;
}

return $data;
}

/**
* {@inheritdoc}
*
Expand Down Expand Up @@ -483,6 +503,21 @@ public function iterateKeyValue(): Traversable
}
}

/**
* Returns an iterator over the result set with the keys mapped to the first column and the values being
* an associative array representing the rest of the columns and their values.
*
* @return Traversable<mixed,array<string,mixed>>
*
* @throws Exception
*/
public function iterateAssociativeIndexed(): Traversable
{
while (($row = $this->stmt->fetch(FetchMode::ASSOCIATIVE)) !== false) {
yield array_shift($row) => $row;
}
}

/**
* {@inheritDoc}
*
Expand Down
44 changes: 43 additions & 1 deletion tests/Doctrine/Tests/DBAL/Functional/Connection/FetchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,27 @@ public function testFetchAllKeyValueOneColumn(): void
$this->connection->fetchAllKeyValue($sql);
}

public function testFetchAllAssociativeIndexed(): void
{
self::assertEquals([
'foo' => ['b' => 1],
'bar' => ['b' => 2],
'baz' => ['b' => 3],
], $this->connection->fetchAllAssociativeIndexed($this->query));
}

public function testStatementFetchAllAssociativeIndexed(): void
{
$stmt = $this->connection->prepare($this->query);
$stmt->execute();

self::assertEquals([
'foo' => ['b' => 1],
'bar' => ['b' => 2],
'baz' => ['b' => 3],
], $stmt->fetchAllAssociativeIndexed());
}

public function testFetchFirstColumn(): void
{
self::assertEquals([
Expand Down Expand Up @@ -171,7 +192,7 @@ public function testIterateKeyValue(): void
], iterator_to_array($this->connection->iterateKeyValue($this->query)));
}

public function testStatementKeyValue(): void
public function testStatementIterateKeyValue(): void
{
$stmt = $this->connection->prepare($this->query);
$stmt->execute();
Expand All @@ -192,6 +213,27 @@ public function testIterateKeyValueOneColumn(): void
iterator_to_array($this->connection->iterateKeyValue($sql));
}

public function testIterateAssociativeIndexed(): void
{
self::assertEquals([
'foo' => ['b' => 1],
'bar' => ['b' => 2],
'baz' => ['b' => 3],
], iterator_to_array($this->connection->iterateAssociativeIndexed($this->query)));
}

public function testStatementIterateAssociativeIndexed(): void
{
$stmt = $this->connection->prepare($this->query);
$stmt->execute();

self::assertEquals([
'foo' => ['b' => 1],
'bar' => ['b' => 2],
'baz' => ['b' => 3],
], iterator_to_array($stmt->iterateAssociativeIndexed()));
}

public function testIterateColumn(): void
{
self::assertEquals([
Expand Down