Skip to content

Commit c3f667b

Browse files
committed
DB: Result - move parsing column values into separate object [WIP: ?, docs]
1 parent c805941 commit c3f667b

File tree

11 files changed

+129
-89
lines changed

11 files changed

+129
-89
lines changed

docs/db.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ You can get your own data (manually passed, not from DB) parsed to the same type
315315
```php
316316
$result = $connection->query('SELECT id, nick, active FROM users');
317317

318-
$data = $result->parseColumnValue('id', '123');
318+
$data = $result->getColumnValueParser()->parseColumnValue('id', '123');
319319
dump($data); // (integer) 123
320320
```
321321

@@ -331,11 +331,11 @@ $row2 = $result->fetch();
331331
dump($row2->id); // (integer) 2
332332
dump($row2->nick); // (string) 'Brandon'
333333

334-
$parsedColumns = $result->getParsedColumns();
334+
$parsedColumns = $result->getColumnValueParser()->getParsedColumns();
335335
dump($parsedColumns); // (array) ['id' => true, 'nick' => true, 'active' => false]
336336

337337
$result = $connection->query('SELECT id, nick, active FROM users ORDER BY id');
338-
$parsedColumns = $result->getParsedColumns();
338+
$parsedColumns = $result->getColumnValueParser()->getParsedColumns();
339339
dump($parsedColumns); // (null)
340340
```
341341

phpcs-ignores.neon

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ ignoreErrors:
9696
path: src/Db/AsyncQuery.php
9797

9898
-
99-
sniff: Squiz.Scope.MethodScope.Missing
100-
message: Visibility must be declared on method "parseColumnValue"
99+
sniff: SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal
100+
message: All classes should be declared using either the "abstract" or "final" keyword.
101101
count: 1
102102
path: src/Db/ColumnValueParser.php
103103

@@ -141,19 +141,13 @@ ignoreErrors:
141141
sniff: SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal
142142
message: All classes should be declared using either the "abstract" or "final" keyword.
143143
count: 1
144-
path: src/Db/DummyColumnValueParser.php
145-
146-
-
147-
sniff: SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
148-
message: Unused parameter $column.
149-
count: 1
150-
path: src/Db/DummyColumnValueParser.php
144+
path: src/Db/Events.php
151145

152146
-
153147
sniff: SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal
154148
message: All classes should be declared using either the "abstract" or "final" keyword.
155149
count: 1
156-
path: src/Db/Events.php
150+
path: src/Db/Exceptions/ColumnValueParserException.php
157151

158152
-
159153
sniff: SlevomatCodingStandard.Classes.RequireAbstractOrFinal.ClassNeitherAbstractNorFinal

src/Db/ColumnValueParser.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,51 @@
22

33
namespace Forrest79\PhPgSql\Db;
44

5-
interface ColumnValueParser
5+
class ColumnValueParser
66
{
7+
private DataTypeParser $dataTypeParser;
78

8-
function parseColumnValue(string $column, mixed $rawValue): mixed;
9+
/** @var array<string, string> */
10+
private array $columnsDataTypes;
11+
12+
/** @var array<string, bool> */
13+
private array $parsedColumns = [];
14+
15+
16+
/**
17+
* @param array<string, string> $columnsDataTypes
18+
*/
19+
public function __construct(DataTypeParser $dataTypeParser, array $columnsDataTypes)
20+
{
21+
$this->dataTypeParser = $dataTypeParser;
22+
$this->columnsDataTypes = $columnsDataTypes;
23+
}
24+
25+
26+
/**
27+
* @throws Exceptions\ColumnValueParserException
28+
*/
29+
public function parseColumnValue(string $column, string|null $rawValue): mixed
30+
{
31+
$value = $this->dataTypeParser->parse(
32+
$this->columnsDataTypes[$column] ?? throw Exceptions\ColumnValueParserException::noColumn($column),
33+
$rawValue,
34+
);
35+
36+
$this->parsedColumns[$column] = true;
37+
38+
return $value;
39+
}
40+
41+
42+
/**
43+
* @return array<string, bool>|null null = no column was used
44+
*/
45+
public function getParsedColumns(): array|null
46+
{
47+
return $this->parsedColumns === []
48+
? null
49+
: $this->parsedColumns + \array_fill_keys(\array_keys($this->columnsDataTypes), false);
50+
}
951

1052
}

src/Db/DummyColumnValueParser.php

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Forrest79\PhPgSql\Db\Exceptions;
4+
5+
class ColumnValueParserException extends Exception
6+
{
7+
public const int NO_COLUMN = 1;
8+
9+
10+
public static function noColumn(string $column): self
11+
{
12+
return new self(\sprintf('There is no column \'%s\'.', $column), self::NO_COLUMN);
13+
}
14+
15+
}

src/Db/Exceptions/RowException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class RowException extends Exception
66
{
77
public const int NO_COLUMN = 1;
88
public const int NOT_STRING_KEY = 2;
9+
public const int COLUMN_VALUE_PARSER_MISSING = 3;
910

1011

1112
public static function noColumn(string $column): self
@@ -19,4 +20,10 @@ public static function notStringKey(): self
1920
return new self('Requested key must be string.', self::NOT_STRING_KEY);
2021
}
2122

23+
24+
public static function columnValueParserMissing(): self
25+
{
26+
return new self('You must pass the ColumnValueParser, if there are some rawValues.', self::COLUMN_VALUE_PARSER_MISSING);
27+
}
28+
2229
}

src/Db/Result.php

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use PgSql;
66

7-
class Result implements ColumnValueParser, \Countable
7+
class Result implements \Countable
88
{
99
protected PgSql\Result $queryResource;
1010

@@ -28,8 +28,7 @@ class Result implements ColumnValueParser, \Countable
2828
/** @var array<string, string>|null */
2929
private array|null $columnsDataTypes = null;
3030

31-
/** @var array<string, bool> */
32-
private array $parsedColumns = [];
31+
private ColumnValueParser|null $columnValueParser = null;
3332

3433

3534
/**
@@ -126,11 +125,7 @@ public function fetch(): Row|null
126125
return null;
127126
}
128127

129-
// Detecting column data types need valid query result and it can be closed before columns data types
130-
// ...are detected, that's why we're detecting it with the first fetch (before first row is created)
131-
$this->detectColumnDataTypes();
132-
133-
$row = $this->rowFactory->create($this, $data);
128+
$row = $this->rowFactory->create($this->getColumnValueParser(), $data);
134129

135130
if ($this->rowFetchMutator !== null) {
136131
call_user_func($this->rowFetchMutator, $row);
@@ -406,30 +401,10 @@ public function getColumns(): array
406401
}
407402

408403

409-
public function parseColumnValue(string $column, mixed $rawValue): mixed
410-
{
411-
\assert(($rawValue === null) || \is_string($rawValue)); // database result all values as string or null
412-
$value = $this->dataTypeParser->parse($this->getColumnType($column), $rawValue);
413-
414-
$this->parsedColumns[$column] = true;
415-
416-
return $value;
417-
}
418-
419-
420404
/**
421405
* @return array<string, string>
422406
*/
423407
private function getColumnsDataTypes(): array
424-
{
425-
$this->detectColumnDataTypes();
426-
\assert($this->columnsDataTypes !== null);
427-
428-
return $this->columnsDataTypes;
429-
}
430-
431-
432-
private function detectColumnDataTypes(): void
433408
{
434409
if ($this->columnsDataTypes === null) {
435410
$this->columnsDataTypes = [];
@@ -455,17 +430,18 @@ private function detectColumnDataTypes(): void
455430
$this->columnsDataTypes[$name] = $type;
456431
}
457432
}
433+
434+
return $this->columnsDataTypes;
458435
}
459436

460437

461-
/**
462-
* @return array<string, bool>|null null = no column was used
463-
*/
464-
public function getParsedColumns(): array|null
438+
public function getColumnValueParser(): ColumnValueParser
465439
{
466-
return $this->parsedColumns === []
467-
? null
468-
: $this->parsedColumns + \array_fill_keys($this->getColumns(), false);
440+
if ($this->columnValueParser === null) {
441+
$this->columnValueParser = new ColumnValueParser($this->dataTypeParser, $this->getColumnsDataTypes());
442+
}
443+
444+
return $this->columnValueParser;
469445
}
470446

471447
}

src/Db/Row.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
class Row implements \ArrayAccess, \IteratorAggregate, \Countable, \JsonSerializable
1010
{
11-
private ColumnValueParser $columnValueParser;
11+
private ColumnValueParser|null $columnValueParser;
1212

1313
/** @var array<string, string|null> */
1414
private array $rawValues;
@@ -20,8 +20,12 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable, \JsonSerializ
2020
/**
2121
* @param array<string, string|null> $rawValues
2222
*/
23-
public function __construct(ColumnValueParser $columnValueParser, array $rawValues)
23+
public function __construct(ColumnValueParser|null $columnValueParser, array $rawValues)
2424
{
25+
if ($columnValueParser === null && $rawValues !== []) {
26+
throw Exceptions\RowException::columnValueParserMissing();
27+
}
28+
2529
$this->columnValueParser = $columnValueParser;
2630
$this->rawValues = $rawValues;
2731

@@ -185,14 +189,15 @@ public function __serialize(): array
185189
*/
186190
public function __unserialize(array $values): void
187191
{
188-
$this->columnValueParser = new DummyColumnValueParser();
192+
$this->columnValueParser = null;
189193
$this->rawValues = [];
190194
$this->values = $values;
191195
}
192196

193197

194198
private function parseValue(string $column): void
195199
{
200+
assert($this->columnValueParser !== null);
196201
$this->values[$column] = $this->columnValueParser->parseColumnValue($column, $this->rawValues[$column]);
197202
unset($this->rawValues[$column]);
198203
}
@@ -226,7 +231,9 @@ public function jsonSerialize(): array
226231
*/
227232
public static function from(array $values): static
228233
{
229-
return new static(new DummyColumnValueParser(), $values);
234+
$row = new static(null, []);
235+
$row->values = $values;
236+
return $row;
230237
}
231238

232239
}

tests/Integration/BasicTest.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -266,24 +266,6 @@ public function testResultGetQuery(): void
266266
}
267267

268268

269-
public function testRowFrom(): void
270-
{
271-
$row = Db\Row::from(['column1' => 1, 'column2' => 'text', 'column3' => true, 'column4' => null]);
272-
273-
Tester\Assert::same(1, $row->column1);
274-
Tester\Assert::same('text', $row->column2);
275-
Tester\Assert::same(true, $row->column3);
276-
Tester\Assert::same(null, $row->column4);
277-
278-
Tester\Assert::true(isset($row->column3));
279-
Tester\Assert::false(isset($row->column4));
280-
Tester\Assert::true($row->hasColumn('column4'));
281-
282-
$blankRow = Db\Row::from([]);
283-
Tester\Assert::same([], $blankRow->toArray());
284-
}
285-
286-
287269
public function testRowSerialize(): void
288270
{
289271
$this->connection->query('

tests/Integration/CollectingResultsTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@ public function testResultCollector(): void
4545
Tester\Assert::same('INSERT INTO test(name) VALUES($1)', $queryInsert->sql);
4646
Tester\Assert::same(['phpgsql'], $queryInsert->params);
4747

48-
Tester\Assert::null($resultInsert->getParsedColumns());
48+
Tester\Assert::null($resultInsert->getColumnValueParser()->getParsedColumns());
4949

5050
$resultSelect = $results[2];
5151

5252
// Try also parse some non-existing column
5353
Tester\Assert::exception(static function () use ($resultSelect): void {
54-
$resultSelect->parseColumnValue('non_existing_column', 'someValue');
55-
}, Db\Exceptions\ResultException::class, code: Db\Exceptions\ResultException::NO_COLUMN);
54+
$resultSelect->getColumnValueParser()->parseColumnValue('non_existing_column', 'someValue');
55+
}, Db\Exceptions\ColumnValueParserException::class, code: Db\Exceptions\ColumnValueParserException::NO_COLUMN);
5656

5757
$querySelect = $resultSelect->getQuery();
5858

5959
Tester\Assert::same('SELECT id, name FROM test', $querySelect->sql);
6060
Tester\Assert::same([], $querySelect->params);
6161

62-
Tester\Assert::equal(['id' => false, 'name' => true], $resultSelect->getParsedColumns());
62+
Tester\Assert::equal(['id' => false, 'name' => true], $resultSelect->getColumnValueParser()->getParsedColumns());
6363
}
6464

6565
}

0 commit comments

Comments
 (0)