Skip to content

Commit

Permalink
feat: allow passing custom param value converters
Browse files Browse the repository at this point in the history
  • Loading branch information
filecage authored and simPod committed Jan 29, 2025
1 parent 9a90f8e commit 586df5a
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 6 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,31 @@ $output = $client->selectWithParams(
All types are supported (except `AggregateFunction`, `SimpleAggregateFunction` and `Nothing` by design).
You can also pass `DateTimeInterface` into `Date*` types or native array into `Array`, `Tuple`, `Native` and `Geo` types

### Custom Query Parameter Value Conversion

Query parameters passed to `selectWithParams()` are converted into an HTTP-API-compatible format. To overwrite an existing value converter or
provide a converter for a type that the library does not (yet) support, pass these to the
`SimPod\ClickHouseClient\Param\ParamValueConverterRegistry` constructor:

```php
<?php
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
use SimPod\ClickHouseClient\Client\PsrClickHouseClient;
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
$paramValueConverterRegistry = new ParamValueConverterRegistry([
'datetime' => static fn (mixed $v) => $v instanceof DateTimeInterface ? $v->format('c') : throw UnsupportedParamValue::type($value)
]);
$client = new PsrClickHouseClient(..., new RequestFactory($paramValueConverterRegistry, ...));
```

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.

Throw an exception of type `UnsupportedParamValue` if your converter does not support the passed value type.

### Expression

To represent complex expressions there's `SimPod\ClickHouseClient\Sql\Expression` class. When passed to `SqlFactory` its value gets evaluated.
Expand Down
17 changes: 11 additions & 6 deletions src/Param/ParamValueConverterRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use function array_keys;
use function array_map;
use function array_merge;
use function explode;
use function implode;
use function in_array;
Expand All @@ -24,7 +25,10 @@
use function strtolower;
use function trim;

/** @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string) */
/**
* @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string)
* @phpstan-type ConverterRegistry = array<string, Converter>
*/
final class ParamValueConverterRegistry
{
/** @var list<string> */
Expand All @@ -44,10 +48,11 @@ final class ParamValueConverterRegistry
'json',
];

/** @phpstan-var array<string, Converter> */
/** @phpstan-var ConverterRegistry */
private array $registry;

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

/** @phpstan-var array<string, Converter> $registry */
$registry = [
/** @phpstan-var ConverterRegistry $defaultRegistry */
$defaultRegistry = [
'String' => self::stringConverter(),
'FixedString' => self::stringConverter(),

Expand Down Expand Up @@ -208,7 +213,7 @@ public function __construct()
return '(' . $innerExpression . ')';
},
];
$this->registry = $registry;
$this->registry = array_merge($defaultRegistry, $registry);
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/Param/ParamValueConverterRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
namespace SimPod\ClickHouseClient\Tests\Param;

use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Generator;
use PHPUnit\Framework\Attributes\BeforeClass;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use Psr\Http\Client\ClientExceptionInterface;
use SimPod\ClickHouseClient\Exception\ServerError;
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
use SimPod\ClickHouseClient\Format\JsonEachRow;
use SimPod\ClickHouseClient\Format\TabSeparated;
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
Expand Down Expand Up @@ -294,4 +297,22 @@ public function testThrowsOnUnknownType(): void
$this->expectException(UnsupportedParamType::class);
$registry->get('fOo');
}

public function testParameterRegistryOverwrite(): void
{
$registry = new ParamValueConverterRegistry([
'datetime' => static fn (mixed $value) => $value instanceof DateTimeInterface
? $value->format('c')
: throw UnsupportedParamValue::value($value),
]);

self::assertSame(
'2025-01-28T16:00:00+01:00',
$registry->get('datetime')(
new DateTimeImmutable('2025-01-28T16:00:00', new DateTimeZone('+0100')),
null,
false,
),
);
}
}

0 comments on commit 586df5a

Please sign in to comment.