Skip to content

Commit 384c326

Browse files
authored
Move ArrayColumnSchema and StructuredColumnSchema to db (#363)
1 parent 3e1cbfb commit 384c326

22 files changed

Lines changed: 150 additions & 809 deletions

src/ArrayParser.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44

55
namespace Yiisoft\Db\Pgsql;
66

7+
use Yiisoft\Db\Syntax\ParserToArrayInterface;
8+
79
use function in_array;
810

911
/**
1012
* Array representation to PHP array parser for PostgreSQL Server.
1113
*/
12-
final class ArrayParser
14+
final class ArrayParser implements ParserToArrayInterface
1315
{
1416
/**
1517
* Convert an array from PostgresSQL to PHP.
16-
*
17-
* @param string|null $value String to convert.
1818
*/
19-
public function parse(string|null $value): array|null
19+
public function parse(string $value): array|null
2020
{
21-
return $value !== null && $value[0] === '{'
21+
return $value[0] === '{'
2222
? $this->parseArray($value)
2323
: null;
2424
}

src/Builder/StructuredExpressionBuilder.php

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
use Yiisoft\Db\Exception\NotSupportedException;
1111
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
1212
use Yiisoft\Db\Expression\ExpressionInterface;
13-
use Yiisoft\Db\Pgsql\StructuredExpression;
14-
use Yiisoft\Db\Query\QueryInterface;
13+
use Yiisoft\Db\Expression\StructuredExpression;
1514
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
1615

16+
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
17+
1718
use function implode;
1819

1920
/**
@@ -40,47 +41,38 @@ public function __construct(private QueryBuilderInterface $queryBuilder)
4041
*/
4142
public function build(ExpressionInterface $expression, array &$params = []): string
4243
{
43-
$value = $expression->getValue();
44+
$value = $expression->getNormalizedValue();
4445

4546
if (empty($value)) {
4647
return 'NULL';
4748
}
4849

49-
if ($value instanceof QueryInterface) {
50-
[$sql, $params] = $this->queryBuilder->build($value, $params);
51-
return "($sql)" . $this->getTypeHint($expression);
50+
if ($value instanceof ExpressionInterface) {
51+
$sql = $this->queryBuilder->buildExpression($value, $params);
52+
return $sql . $this->getTypeHint($expression);
5253
}
5354

5455
/** @psalm-var string[] $placeholders */
55-
$placeholders = $this->buildPlaceholders($expression, $params);
56-
57-
if (empty($placeholders)) {
58-
return 'NULL';
59-
}
56+
$placeholders = $this->buildPlaceholders($value, $expression->getColumns(), $params);
6057

6158
return 'ROW(' . implode(', ', $placeholders) . ')' . $this->getTypeHint($expression);
6259
}
6360

6461
/**
6562
* Builds a placeholder array out of $expression values.
6663
*
64+
* @param array|object $value The expression value.
65+
* @param ColumnSchemaInterface[] $columns The structured type columns.
6766
* @param array $params The binding parameters.
6867
*
6968
* @throws Exception
7069
* @throws InvalidArgumentException
7170
* @throws InvalidConfigException
7271
* @throws NotSupportedException
7372
*/
74-
private function buildPlaceholders(StructuredExpression $expression, array &$params): array
73+
private function buildPlaceholders(array|object $value, array $columns, array &$params): array
7574
{
76-
$value = $expression->getNormalizedValue();
77-
78-
if (!is_iterable($value)) {
79-
return [];
80-
}
81-
8275
$placeholders = [];
83-
$columns = $expression->getColumns();
8476

8577
/** @psalm-var int|string $columnName */
8678
foreach ($value as $columnName => $item) {

src/Column/ArrayColumnSchema.php

Lines changed: 5 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -4,164 +4,14 @@
44

55
namespace Yiisoft\Db\Pgsql\Column;
66

7-
use Traversable;
8-
use Yiisoft\Db\Constant\ColumnType;
9-
use Yiisoft\Db\Constant\PhpType;
10-
use Yiisoft\Db\Expression\ArrayExpression;
11-
use Yiisoft\Db\Expression\ExpressionInterface;
127
use Yiisoft\Db\Pgsql\ArrayParser;
13-
use Yiisoft\Db\Schema\Column\AbstractColumnSchema;
14-
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
8+
use Yiisoft\Db\Schema\Column\ArrayColumnSchema as BaseArrayColumnSchema;
9+
use Yiisoft\Db\Syntax\ParserToArrayInterface;
1510

16-
use function array_map;
17-
use function array_walk_recursive;
18-
use function is_array;
19-
use function is_iterable;
20-
use function is_string;
21-
use function iterator_to_array;
22-
23-
final class ArrayColumnSchema extends AbstractColumnSchema
11+
final class ArrayColumnSchema extends BaseArrayColumnSchema
2412
{
25-
/**
26-
* @var ColumnSchemaInterface|null The column of an array item.
27-
*/
28-
private ColumnSchemaInterface|null $column = null;
29-
30-
/**
31-
* @var int The dimension of array, must be greater than 0.
32-
*/
33-
private int $dimension = 1;
34-
35-
/**
36-
* @psalm-param ColumnType::* $type
37-
*/
38-
public function __construct(
39-
string $type = ColumnType::ARRAY,
40-
) {
41-
parent::__construct($type);
42-
}
43-
44-
/**
45-
* Set column of an array item.
46-
*/
47-
public function column(ColumnSchemaInterface|null $column): static
48-
{
49-
$this->column = $column;
50-
return $this;
51-
}
52-
53-
/**
54-
* @return ColumnSchemaInterface the column of an array item.
55-
*/
56-
public function getColumn(): ColumnSchemaInterface
57-
{
58-
if ($this->column === null) {
59-
$this->column = (new ColumnFactory())->fromDbType($this->getDbType() ?? '');
60-
$this->column->enumValues($this->getEnumValues());
61-
$this->column->scale($this->getScale());
62-
$this->column->size($this->getSize());
63-
}
64-
65-
return $this->column;
66-
}
67-
68-
/**
69-
* Set dimension of an array, must be greater than 0.
70-
*/
71-
public function dimension(int $dimension): static
72-
{
73-
$this->dimension = $dimension;
74-
return $this;
75-
}
76-
77-
/**
78-
* @return int the dimension of the array.
79-
*/
80-
public function getDimension(): int
81-
{
82-
return $this->dimension;
83-
}
84-
85-
public function getPhpType(): string
86-
{
87-
return PhpType::ARRAY;
88-
}
89-
90-
public function dbTypecast(mixed $value): ExpressionInterface|null
91-
{
92-
if ($value === null || $value instanceof ExpressionInterface) {
93-
return $value;
94-
}
95-
96-
if ($this->dimension === 1 && is_array($value)) {
97-
$value = array_map($this->getColumn()->dbTypecast(...), $value);
98-
} else {
99-
$value = $this->dbTypecastArray($value, $this->dimension);
100-
}
101-
102-
return new ArrayExpression($value, $this->getDbType() ?? $this->getColumn()->getDbType(), $this->dimension);
103-
}
104-
105-
public function phpTypecast(mixed $value): array|null
106-
{
107-
if (is_string($value)) {
108-
$value = (new ArrayParser())->parse($value);
109-
}
110-
111-
if (!is_array($value)) {
112-
return null;
113-
}
114-
115-
if ($this->getType() === ColumnType::STRING) {
116-
return $value;
117-
}
118-
119-
$column = $this->getColumn();
120-
121-
if ($this->dimension === 1 && $column->getType() !== ColumnType::JSON) {
122-
return array_map($column->phpTypecast(...), $value);
123-
}
124-
125-
array_walk_recursive($value, function (string|null &$val) use ($column): void {
126-
$val = $column->phpTypecast($val);
127-
});
128-
129-
return $value;
130-
}
131-
132-
/**
133-
* Recursively converts array values for use in a db query.
134-
*
135-
* @param mixed $value The array or iterable object.
136-
* @param int $dimension The array dimension. Should be more than 0.
137-
*
138-
* @return array|null Converted values.
139-
*/
140-
private function dbTypecastArray(mixed $value, int $dimension): array|null
13+
protected function getParser(): ParserToArrayInterface
14114
{
142-
if ($value === null) {
143-
return null;
144-
}
145-
146-
if (!is_iterable($value)) {
147-
return [];
148-
}
149-
150-
if ($dimension <= 1) {
151-
return array_map(
152-
$this->getColumn()->dbTypecast(...),
153-
$value instanceof Traversable
154-
? iterator_to_array($value, false)
155-
: $value
156-
);
157-
}
158-
159-
$items = [];
160-
161-
foreach ($value as $val) {
162-
$items[] = $this->dbTypecastArray($val, $dimension - 1);
163-
}
164-
165-
return $items;
15+
return new ArrayParser();
16616
}
16717
}

src/Column/ColumnBuilder.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,6 @@ public static function array(ColumnSchemaInterface|null $column = null): ColumnS
5656
->column($column);
5757
}
5858

59-
/**
60-
* @param string|null $dbType The DB type of the column.
61-
* @param ColumnSchemaInterface[] $columns The columns (name -> instance) that the structured column should contain.
62-
*
63-
* @psalm-param array<string, ColumnSchemaInterface> $columns
64-
*/
6559
public static function structured(string|null $dbType = null, array $columns = []): ColumnSchemaInterface
6660
{
6761
return (new StructuredColumnSchema(ColumnType::STRUCTURED))

src/Column/ColumnFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* @psalm-type ColumnInfo = array{
1616
* auto_increment?: bool|string,
1717
* check?: string|null,
18+
* column?: ColumnSchemaInterface,
1819
* columns?: array<string, ColumnSchemaInterface>,
1920
* comment?: string|null,
2021
* computed?: bool|string,
@@ -127,7 +128,7 @@ public function fromType(string $type, array $info = []): ColumnSchemaInterface
127128
unset($info['dimension']);
128129
$column = (new ArrayColumnSchema())
129130
->dimension($dimension)
130-
->column($this->fromType($type, $info));
131+
->column($info['column'] ?? $this->fromType($type, $info));
131132
} else {
132133
$column = match ($type) {
133134
ColumnType::BOOLEAN => new BooleanColumnSchema($type),

src/Column/StructuredColumnSchema.php

Lines changed: 5 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4,92 +4,14 @@
44

55
namespace Yiisoft\Db\Pgsql\Column;
66

7-
use Traversable;
8-
use Yiisoft\Db\Constant\ColumnType;
9-
use Yiisoft\Db\Constant\PhpType;
10-
use Yiisoft\Db\Expression\ExpressionInterface;
11-
use Yiisoft\Db\Pgsql\StructuredExpression;
127
use Yiisoft\Db\Pgsql\StructuredParser;
13-
use Yiisoft\Db\Schema\Column\AbstractColumnSchema;
14-
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
8+
use Yiisoft\Db\Schema\Column\StructuredColumnSchema as BaseStructuredColumnSchema;
9+
use Yiisoft\Db\Syntax\ParserToArrayInterface;
1510

16-
use function array_keys;
17-
use function is_iterable;
18-
use function is_string;
19-
use function iterator_to_array;
20-
21-
final class StructuredColumnSchema extends AbstractColumnSchema implements StructuredColumnSchemaInterface
11+
final class StructuredColumnSchema extends BaseStructuredColumnSchema
2212
{
23-
/**
24-
* @var ColumnSchemaInterface[] Columns metadata of the composite type.
25-
* @psalm-var array<string, ColumnSchemaInterface>
26-
*/
27-
private array $columns = [];
28-
29-
/**
30-
* @psalm-param ColumnType::* $type
31-
*/
32-
public function __construct(
33-
string $type = ColumnType::STRUCTURED,
34-
) {
35-
parent::__construct($type);
36-
}
37-
38-
public function columns(array $columns): static
39-
{
40-
$this->columns = $columns;
41-
return $this;
42-
}
43-
44-
public function getColumns(): array
45-
{
46-
return $this->columns;
47-
}
48-
49-
public function getPhpType(): string
13+
protected function getParser(): ParserToArrayInterface
5014
{
51-
return PhpType::ARRAY;
52-
}
53-
54-
public function dbTypecast(mixed $value): mixed
55-
{
56-
if ($value === null || $value instanceof ExpressionInterface) {
57-
return $value;
58-
}
59-
60-
return new StructuredExpression($value, $this->getDbType(), $this->columns);
61-
}
62-
63-
public function phpTypecast(mixed $value): array|null
64-
{
65-
if (is_string($value)) {
66-
$value = (new StructuredParser())->parse($value);
67-
}
68-
69-
if (!is_iterable($value)) {
70-
return null;
71-
}
72-
73-
if (empty($this->columns)) {
74-
return $value instanceof Traversable
75-
? iterator_to_array($value)
76-
: $value;
77-
}
78-
79-
$fields = [];
80-
$columnNames = array_keys($this->columns);
81-
82-
/** @psalm-var int|string $columnName */
83-
foreach ($value as $columnName => $item) {
84-
$columnName = $columnNames[$columnName] ?? $columnName;
85-
86-
if (isset($this->columns[$columnName])) {
87-
$fields[$columnName] = $this->columns[$columnName]->phpTypecast($item);
88-
} else {
89-
$fields[$columnName] = $item;
90-
}
91-
}
92-
93-
return $fields;
15+
return new StructuredParser();
9416
}
9517
}

0 commit comments

Comments
 (0)