Skip to content

Commit 5c080b3

Browse files
authored
Allow passing custom param value converters (#286)
Co-authored-by: David <[email protected]>
1 parent 9a90f8e commit 5c080b3

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,31 @@ $output = $client->selectWithParams(
243243
All types are supported (except `AggregateFunction`, `SimpleAggregateFunction` and `Nothing` by design).
244244
You can also pass `DateTimeInterface` into `Date*` types or native array into `Array`, `Tuple`, `Native` and `Geo` types
245245

246+
### Custom Query Parameter Value Conversion
247+
248+
Query parameters passed to `selectWithParams()` are converted into an HTTP-API-compatible format. To overwrite an existing value converter or
249+
provide a converter for a type that the library does not (yet) support, pass these to the
250+
`SimPod\ClickHouseClient\Param\ParamValueConverterRegistry` constructor:
251+
252+
```php
253+
<?php
254+
255+
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
256+
use SimPod\ClickHouseClient\Client\PsrClickHouseClient;
257+
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
258+
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
259+
260+
$paramValueConverterRegistry = new ParamValueConverterRegistry([
261+
'datetime' => static fn (mixed $v) => $v instanceof DateTimeInterface ? $v->format('c') : throw UnsupportedParamValue::type($value)
262+
]);
263+
264+
$client = new PsrClickHouseClient(..., new RequestFactory($paramValueConverterRegistry, ...));
265+
```
266+
267+
Be aware that the library can not ensure that passed values have a certain type. They are passed as-is and closures must accept `mixed` values.
268+
269+
Throw an exception of type `UnsupportedParamValue` if your converter does not support the passed value type.
270+
246271
### Expression
247272

248273
To represent complex expressions there's `SimPod\ClickHouseClient\Sql\Expression` class. When passed to `SqlFactory` its value gets evaluated.

src/Param/ParamValueConverterRegistry.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use function array_keys;
1414
use function array_map;
15+
use function array_merge;
1516
use function explode;
1617
use function implode;
1718
use function in_array;
@@ -24,7 +25,10 @@
2425
use function strtolower;
2526
use function trim;
2627

27-
/** @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string) */
28+
/**
29+
* @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string)
30+
* @phpstan-type ConverterRegistry = array<string, Converter>
31+
*/
2832
final class ParamValueConverterRegistry
2933
{
3034
/** @var list<string> */
@@ -44,10 +48,11 @@ final class ParamValueConverterRegistry
4448
'json',
4549
];
4650

47-
/** @phpstan-var array<string, Converter> */
51+
/** @phpstan-var ConverterRegistry */
4852
private array $registry;
4953

50-
public function __construct()
54+
/** @phpstan-param ConverterRegistry $registry */
55+
public function __construct(array $registry = [])
5156
{
5257
$formatPoint = static fn (array $point) => sprintf('(%s)', implode(',', $point));
5358
// phpcs:ignore SlevomatCodingStandard.Functions.RequireArrowFunction.RequiredArrowFunction
@@ -67,8 +72,8 @@ public function __construct()
6772
));
6873
};
6974

70-
/** @phpstan-var array<string, Converter> $registry */
71-
$registry = [
75+
/** @phpstan-var ConverterRegistry $defaultRegistry */
76+
$defaultRegistry = [
7277
'String' => self::stringConverter(),
7378
'FixedString' => self::stringConverter(),
7479

@@ -208,7 +213,7 @@ public function __construct()
208213
return '(' . $innerExpression . ')';
209214
},
210215
];
211-
$this->registry = $registry;
216+
$this->registry = array_merge($defaultRegistry, $registry);
212217
}
213218

214219
/**

tests/Param/ParamValueConverterRegistryTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
namespace SimPod\ClickHouseClient\Tests\Param;
66

77
use DateTimeImmutable;
8+
use DateTimeInterface;
9+
use DateTimeZone;
810
use Generator;
911
use PHPUnit\Framework\Attributes\BeforeClass;
1012
use PHPUnit\Framework\Attributes\CoversClass;
1113
use PHPUnit\Framework\Attributes\DataProvider;
1214
use Psr\Http\Client\ClientExceptionInterface;
1315
use SimPod\ClickHouseClient\Exception\ServerError;
1416
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
17+
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
1518
use SimPod\ClickHouseClient\Format\JsonEachRow;
1619
use SimPod\ClickHouseClient\Format\TabSeparated;
1720
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
@@ -294,4 +297,22 @@ public function testThrowsOnUnknownType(): void
294297
$this->expectException(UnsupportedParamType::class);
295298
$registry->get('fOo');
296299
}
300+
301+
public function testParameterRegistryOverwrite(): void
302+
{
303+
$registry = new ParamValueConverterRegistry([
304+
'datetime' => static fn (mixed $value) => $value instanceof DateTimeInterface
305+
? $value->format('c')
306+
: throw UnsupportedParamValue::value($value),
307+
]);
308+
309+
self::assertSame(
310+
'2025-01-28T16:00:00+01:00',
311+
$registry->get('datetime')(
312+
new DateTimeImmutable('2025-01-28T16:00:00', new DateTimeZone('+0100')),
313+
null,
314+
false,
315+
),
316+
);
317+
}
297318
}

0 commit comments

Comments
 (0)