Skip to content

Commit f99ca9f

Browse files
Unlinkdg
authored andcommitted
Connection, ResultSet: added custom row normalizer [Closes #138]
1 parent 43059e3 commit f99ca9f

4 files changed

Lines changed: 116 additions & 45 deletions

File tree

src/Database/Connection.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class Connection
4343
/** @var PDO|null */
4444
private $pdo;
4545

46+
/** @var callable(array, ResultSet): array */
47+
private $rowNormalizer = [Helpers::class, 'normalizeRow'];
48+
4649
/** @var string|null */
4750
private $sql;
4851

@@ -125,6 +128,13 @@ public function getSupplementalDriver(): Driver
125128
}
126129

127130

131+
public function setRowNormalizer(?callable $normalizer): self
132+
{
133+
$this->rowNormalizer = $normalizer;
134+
return $this;
135+
}
136+
137+
128138
public function getInsertId(string $sequence = null): string
129139
{
130140
try {
@@ -212,7 +222,7 @@ public function query(string $sql, ...$params): ResultSet
212222
{
213223
[$this->sql, $params] = $this->preprocess($sql, ...$params);
214224
try {
215-
$result = new ResultSet($this, $this->sql, $params);
225+
$result = new ResultSet($this, $this->sql, $params, $this->rowNormalizer);
216226
} catch (PDOException $e) {
217227
Arrays::invoke($this->onQuery, $this, $e);
218228
throw $e;

src/Database/Helpers.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,48 @@ public static function detectType(string $type): string
191191
}
192192

193193

194+
/** @internal */
195+
public static function normalizeRow(array $row, ResultSet $resultSet): array
196+
{
197+
foreach ($resultSet->getColumnTypes() as $key => $type) {
198+
$value = $row[$key];
199+
if ($value === null || $value === false || $type === IStructure::FIELD_TEXT) {
200+
// do nothing
201+
} elseif ($type === IStructure::FIELD_INTEGER) {
202+
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
203+
204+
} elseif ($type === IStructure::FIELD_FLOAT) {
205+
if (is_string($value) && ($pos = strpos($value, '.')) !== false) {
206+
$value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.');
207+
}
208+
$float = (float) $value;
209+
$row[$key] = (string) $float === $value ? $float : $value;
210+
211+
} elseif ($type === IStructure::FIELD_BOOL) {
212+
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
213+
214+
} elseif (
215+
$type === IStructure::FIELD_DATETIME
216+
|| $type === IStructure::FIELD_DATE
217+
|| $type === IStructure::FIELD_TIME
218+
) {
219+
$row[$key] = new Nette\Utils\DateTime($value);
220+
221+
} elseif ($type === IStructure::FIELD_TIME_INTERVAL) {
222+
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m);
223+
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
224+
$row[$key]->f = isset($m[5]) ? (float) $m[5] : 0.0;
225+
$row[$key]->invert = (int) (bool) $m[1];
226+
227+
} elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) {
228+
$row[$key] = Nette\Utils\DateTime::from($value);
229+
}
230+
}
231+
232+
return $row;
233+
}
234+
235+
194236
/**
195237
* Import SQL dump from file - extremely fast.
196238
* @param array<callable(int, ?float): void> $onProgress

src/Database/ResultSet.php

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class ResultSet implements \Iterator, IRowContainer
2626
/** @var \PDOStatement|null */
2727
private $pdoStatement;
2828

29+
/** @var callable(array, ResultSet): array */
30+
private $normalizer;
31+
2932
/** @var Row|false */
3033
private $result;
3134

@@ -48,12 +51,13 @@ class ResultSet implements \Iterator, IRowContainer
4851
private $types;
4952

5053

51-
public function __construct(Connection $connection, string $queryString, array $params)
54+
public function __construct(Connection $connection, string $queryString, array $params, callable $normalizer = null)
5255
{
5356
$time = microtime(true);
5457
$this->connection = $connection;
5558
$this->queryString = $queryString;
5659
$this->params = $params;
60+
$this->normalizer = $normalizer;
5761

5862
try {
5963
if (substr($queryString, 0, 2) === '::') {
@@ -118,57 +122,27 @@ public function getRowCount(): ?int
118122
}
119123

120124

125+
public function getColumnTypes(): array
126+
{
127+
if ($this->types === null) {
128+
$this->types = $this->connection->getDriver()->getColumnTypes($this->pdoStatement);
129+
}
130+
return $this->types;
131+
}
132+
133+
121134
public function getTime(): float
122135
{
123136
return $this->time;
124137
}
125138

126139

127-
/**
128-
* Normalizes result row.
129-
*/
140+
/** @internal */
130141
public function normalizeRow(array $row): array
131142
{
132-
if ($this->types === null) {
133-
$this->types = $this->connection->getDriver()->getColumnTypes($this->pdoStatement);
134-
}
135-
136-
foreach ($this->types as $key => $type) {
137-
$value = $row[$key];
138-
if ($value === null || $value === false || $type === IStructure::FIELD_TEXT) {
139-
// do nothing
140-
} elseif ($type === IStructure::FIELD_INTEGER) {
141-
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
142-
143-
} elseif ($type === IStructure::FIELD_FLOAT) {
144-
if (is_string($value) && ($pos = strpos($value, '.')) !== false) {
145-
$value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.');
146-
}
147-
$float = (float) $value;
148-
$row[$key] = (string) $float === $value ? $float : $value;
149-
150-
} elseif ($type === IStructure::FIELD_BOOL) {
151-
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
152-
153-
} elseif (
154-
$type === IStructure::FIELD_DATETIME
155-
|| $type === IStructure::FIELD_DATE
156-
|| $type === IStructure::FIELD_TIME
157-
) {
158-
$row[$key] = new Nette\Utils\DateTime($value);
159-
160-
} elseif ($type === IStructure::FIELD_TIME_INTERVAL) {
161-
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m);
162-
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
163-
$row[$key]->f = isset($m[5]) ? (float) $m[5] : 0.0;
164-
$row[$key]->invert = (int) (bool) $m[1];
165-
166-
} elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) {
167-
$row[$key] = Nette\Utils\DateTime::from($value);
168-
}
169-
}
170-
171-
return $row;
143+
return $this->normalizer
144+
? ($this->normalizer)($row, $this)
145+
: $row;
172146
}
173147

174148

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
/**
5+
* @dataProvider? databases.ini
6+
*/
7+
8+
use Tester\Assert;
9+
10+
require __DIR__ . '/connect.inc.php'; // create $connection
11+
12+
Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName}-nette_test1.sql");
13+
14+
15+
test('disabled normalization', function () use ($connection) {
16+
global $driverName;
17+
18+
$connection->setRowNormalizer(null);
19+
$res = $connection->query('SELECT * FROM author');
20+
Assert::same([
21+
'id' => $driverName === 'pgsql' ? 11 : '11',
22+
'name' => 'Jakub Vrana',
23+
'web' => 'http://www.vrana.cz/',
24+
'born' => null,
25+
], (array) $res->fetch());
26+
});
27+
28+
29+
test('custom normalization', function () use ($connection) {
30+
$connection->setRowNormalizer(function (array $row, Nette\Database\ResultSet $resultSet) {
31+
foreach ($row as $key => $value) {
32+
unset($row[$key]);
33+
$row['_' . $key . '_'] = (string) $value;
34+
}
35+
return $row;
36+
});
37+
38+
$res = $connection->query('SELECT * FROM author');
39+
Assert::same([
40+
'_id_' => '11',
41+
'_name_' => 'Jakub Vrana',
42+
'_web_' => 'http://www.vrana.cz/',
43+
'_born_' => '',
44+
], (array) $res->fetch());
45+
});

0 commit comments

Comments
 (0)