-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
/
ArrayExpressionBuilder.php
149 lines (125 loc) · 4.3 KB
/
ArrayExpressionBuilder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\pgsql;
use yii\db\ArrayExpression;
use yii\db\ExpressionBuilderInterface;
use yii\db\ExpressionBuilderTrait;
use yii\db\ExpressionInterface;
use yii\db\JsonExpression;
use yii\db\Query;
/**
* Class ArrayExpressionBuilder builds [[ArrayExpression]] for PostgreSQL DBMS.
*
* @author Dmytro Naumenko <d.naumenko.a@gmail.com>
* @since 2.0.14
*/
class ArrayExpressionBuilder implements ExpressionBuilderInterface
{
use ExpressionBuilderTrait;
/**
* {@inheritdoc}
* @param ArrayExpression|ExpressionInterface $expression the expression to be built
*/
public function build(ExpressionInterface $expression, array &$params = [])
{
$value = $expression->getValue();
if ($value === null) {
return 'NULL';
}
if ($value instanceof Query) {
list ($sql, $params) = $this->queryBuilder->build($value, $params);
return $this->buildSubqueryArray($sql, $expression);
}
$placeholders = $this->buildPlaceholders($expression, $params);
return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
}
/**
* Builds placeholders array out of $expression values
* @param ExpressionInterface|ArrayExpression $expression
* @param array $params the binding parameters.
* @return array
*/
protected function buildPlaceholders(ExpressionInterface $expression, &$params)
{
$value = $expression->getValue();
$placeholders = [];
if ($value === null || !is_array($value) && !$value instanceof \Traversable) {
return $placeholders;
}
if ($expression->getDimension() > 1) {
foreach ($value as $item) {
$placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
}
return $placeholders;
}
foreach ($value as $item) {
if ($item instanceof Query) {
list ($sql, $params) = $this->queryBuilder->build($item, $params);
$placeholders[] = $this->buildSubqueryArray($sql, $expression);
continue;
}
$item = $this->typecastValue($expression, $item);
if ($item instanceof ExpressionInterface) {
$placeholders[] = $this->queryBuilder->buildExpression($item, $params);
continue;
}
$placeholders[] = $this->queryBuilder->bindParam($item, $params);
}
return $placeholders;
}
/**
* @param ArrayExpression $expression
* @param mixed $value
* @return ArrayExpression
*/
private function unnestArrayExpression(ArrayExpression $expression, $value)
{
$expressionClass = get_class($expression);
return new $expressionClass($value, $expression->getType(), $expression->getDimension()-1);
}
/**
* @param ArrayExpression $expression
* @return string the typecast expression based on [[type]].
*/
protected function getTypehint(ArrayExpression $expression)
{
if ($expression->getType() === null) {
return '';
}
$result = '::' . $expression->getType();
$result .= str_repeat('[]', $expression->getDimension());
return $result;
}
/**
* Build an array expression from a subquery SQL.
*
* @param string $sql the subquery SQL.
* @param ArrayExpression $expression
* @return string the subquery array expression.
*/
protected function buildSubqueryArray($sql, ArrayExpression $expression)
{
return 'ARRAY(' . $sql . ')' . $this->getTypehint($expression);
}
/**
* Casts $value to use in $expression
*
* @param ArrayExpression $expression
* @param mixed $value
* @return JsonExpression
*/
protected function typecastValue(ArrayExpression $expression, $value)
{
if ($value instanceof ExpressionInterface) {
return $value;
}
if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
return new JsonExpression($value);
}
return $value;
}
}