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 ArrayType as a generic wrapper for a "list of custom types" #6883

Draft
wants to merge 1 commit into
base: 4.3.x
Choose a base branch
from
Draft
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
19 changes: 19 additions & 0 deletions src/ArrayType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL;

use Doctrine\DBAL\Types\Type;

class ArrayType
{
public function __construct(private readonly string|Type|ParameterType $baseType)

Check warning on line 11 in src/ArrayType.php

View check run for this annotation

Codecov / codecov/patch

src/ArrayType.php#L11

Added line #L11 was not covered by tests
{
}

Check warning on line 13 in src/ArrayType.php

View check run for this annotation

Codecov / codecov/patch

src/ArrayType.php#L13

Added line #L13 was not covered by tests

public function getBaseType(): Type|string|ParameterType
{
return $this->baseType;
}
}
4 changes: 2 additions & 2 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
* configuration, emulated transaction nesting, lazy connecting and more.
*
* @phpstan-import-type Params from DriverManager
* @phpstan-type WrapperParameterType = string|Type|ParameterType|ArrayParameterType
* @phpstan-type WrapperParameterType = string|Type|ParameterType|ArrayParameterType|ArrayType
* @phpstan-type WrapperParameterTypeArray = array<
* int<0, max>,
* WrapperParameterType>|array<string, WrapperParameterType
Expand Down Expand Up @@ -1423,7 +1423,7 @@ private function expandArrayParameters(string $sql, array $params, array $types)
$needsConversion = true;
} else {
foreach ($types as $key => $type) {
if ($type instanceof ArrayParameterType) {
if ($type instanceof ArrayParameterType || $type instanceof ArrayType) {
$needsConversion = true;
break;
}
Expand Down
8 changes: 6 additions & 2 deletions src/ExpandArrayParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private function acceptParameter(int|string $key, mixed $value): void

$type = $this->types[$key];

if (! $type instanceof ArrayParameterType) {
if (! $type instanceof ArrayParameterType && ! $type instanceof ArrayType) {
$this->appendTypedParameter([$value], $type);

return;
Expand All @@ -102,7 +102,11 @@ private function acceptParameter(int|string $key, mixed $value): void
return;
}

$this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type));
if ($type instanceof ArrayType) {
$this->appendTypedParameter($value, $type->getBaseType());
} else {
$this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type));
}
}

/** @return array<int<0, max>,string|ParameterType|Type> */
Expand Down
9 changes: 5 additions & 4 deletions src/Query/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Doctrine\DBAL\Query;

use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\ArrayType;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
Expand Down Expand Up @@ -377,7 +378,7 @@ public function getSQL(): string
public function setParameter(
int|string $key,
mixed $value,
string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING,
string|ParameterType|Type|ArrayParameterType|ArrayType $type = ParameterType::STRING,
): self {
$this->params[$key] = $value;
$this->types[$key] = $type;
Expand Down Expand Up @@ -449,7 +450,7 @@ public function getParameterTypes(): array
*
* @param int|string $key The key of the bound parameter type
*/
public function getParameterType(int|string $key): string|ParameterType|Type|ArrayParameterType
public function getParameterType(int|string $key): string|ParameterType|Type|ArrayParameterType|ArrayType
Copy link
Contributor Author

Choose a reason for hiding this comment

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

BC break?

Copy link
Member

@morozov morozov Mar 31, 2025

Choose a reason for hiding this comment

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

My idea was that ArrayType is a Type. It with throw a "not implemented" exception for the SQL declaration (because we can't use it as a column type) but should be able to implement the rest of the methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What would it do to convert a PHP value (an array, probably?) to a database value?

Copy link
Member

Choose a reason for hiding this comment

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

It will delegate the conversion of each element of the array to its element type. We can start simple and accept only Type in the constructor to prove the idea:

public function __construct(private readonly Type $elementType)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When would it be necessary to use the ArrayType in this way?

Copy link
Member

Choose a reason for hiding this comment

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

When building the query here. If this code builds the parameter types as [new ArrayType('rot13'), 'rot13'], the DBAL should be able to apply rot13 to the array elements similar to how it does with a single value.

{
return $this->types[$key] ?? ParameterType::STRING;
}
Expand Down Expand Up @@ -1484,7 +1485,7 @@ public function __toString(): string
*/
public function createNamedParameter(
mixed $value,
string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING,
string|ParameterType|Type|ArrayParameterType|ArrayType $type = ParameterType::STRING,
?string $placeHolder = null,
): string {
if ($placeHolder === null) {
Expand Down Expand Up @@ -1516,7 +1517,7 @@ public function createNamedParameter(
*/
public function createPositionalParameter(
mixed $value,
string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING,
string|ParameterType|Type|ArrayParameterType|ArrayType $type = ParameterType::STRING,
): string {
$this->setParameter($this->boundCounter, $value, $type);
$this->boundCounter++;
Expand Down
10 changes: 10 additions & 0 deletions tests/Connection/ExpandArrayParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\DBAL\ArrayParameters\Exception\MissingNamedParameter;
use Doctrine\DBAL\ArrayParameters\Exception\MissingPositionalParameter;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\ArrayType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ExpandArrayParameters;
use Doctrine\DBAL\ParameterType;
Expand Down Expand Up @@ -381,6 +382,15 @@ public static function dataExpandListParameters(): iterable
ParameterType::BINARY,
],
];

yield 'ArrayType wrapped around a custom type' => [
'SELECT * FROM Foo WHERE foo IN (?)',
[['this', 'that']],
[new ArrayType('my_type')],
'SELECT * FROM Foo WHERE foo IN (?, ?)',
['this', 'that'],
['my_type', 'my_type'],
];
}

/**
Expand Down