Passed
Pull Request — master (#32)
by Wilmer
14:54
created

ArrayExpressionBuilder::unnestArrayExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use Traversable;
8
use Yiisoft\Db\Exception\Exception;
9
use Yiisoft\Db\Exception\InvalidArgumentException;
10
use Yiisoft\Db\Exception\InvalidConfigException;
11
use Yiisoft\Db\Exception\NotSupportedException;
12
use Yiisoft\Db\Expression\ArrayExpression;
13
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
14
use Yiisoft\Db\Expression\ExpressionBuilderTrait;
15
use Yiisoft\Db\Expression\ExpressionInterface;
16
use Yiisoft\Db\Expression\JsonExpression;
17
use Yiisoft\Db\Pgsql\Schema;
18
use Yiisoft\Db\Query\Query;
19
20
use function get_class;
21
use function implode;
22
use function in_array;
23
use function is_array;
24
use function str_repeat;
25
26
final class ArrayExpressionBuilder implements ExpressionBuilderInterface
27
{
28
    use ExpressionBuilderTrait;
29
30
    /**
31
     * Method builds the raw SQL from the $expression that will not be additionally escaped or quoted.
32
     *
33
     * @param ExpressionInterface $expression the expression to be built.
34
     * @param array $params the binding parameters.
35
     *
36
     * @throws Exception
37
     * @throws InvalidArgumentException
38
     * @throws InvalidConfigException
39
     * @throws NotSupportedException
40
     *
41
     * @return string the raw SQL that will not be additionally escaped or quoted.
42
     */
43
    public function build(ExpressionInterface $expression, array &$params = []): string
44
    {
45
        $value = $expression->getValue();
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Yiisoft\Db\Expression\ExpressionInterface. It seems like you code against a sub-type of Yiisoft\Db\Expression\ExpressionInterface such as Yiisoft\Db\Pdo\PdoValue or Yiisoft\Db\Expression\ArrayExpression or Yiisoft\Db\Expression\JsonExpression or Yiisoft\Db\Query\Conditions\SimpleCondition or Yiisoft\Db\Query\Conditi...BetweenColumnsCondition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
        /** @scrutinizer ignore-call */ 
46
        $value = $expression->getValue();
Loading history...
46
        if ($value === null) {
47
            return 'NULL';
48
        }
49
50
        if ($value instanceof Query) {
51
            [$sql, $params] = $this->queryBuilder->build($value, $params);
52
            return $this->buildSubqueryArray($sql, $expression);
53
        }
54
55
        $placeholders = $this->buildPlaceholders($expression, $params);
56
57
        return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
58
    }
59
60
    /**
61
     * Builds placeholders array out of $expression values.
62
     *
63
     * @param ExpressionInterface $expression
64
     * @param array $params the binding parameters.
65
     *
66
     * @throws Exception
67
     * @throws InvalidArgumentException
68
     * @throws InvalidConfigException
69
     * @throws NotSupportedException
70
     *
71
     * @return array
72
     */
73
    protected function buildPlaceholders(ExpressionInterface $expression, &$params): array
74
    {
75
        $value = $expression->getValue();
76
77
        $placeholders = [];
78
        if ($value === null || (!is_array($value) && !$value instanceof Traversable)) {
79
            return $placeholders;
80
        }
81
82
        if ($expression->getDimension() > 1) {
0 ignored issues
show
Bug introduced by
The method getDimension() does not exist on Yiisoft\Db\Expression\ExpressionInterface. It seems like you code against a sub-type of Yiisoft\Db\Expression\ExpressionInterface such as Yiisoft\Db\Expression\ArrayExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

82
        if ($expression->/** @scrutinizer ignore-call */ getDimension() > 1) {
Loading history...
83
            foreach ($value as $item) {
84
                $placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
85
            }
86
            return $placeholders;
87
        }
88
89
        foreach ($value as $item) {
90
            if ($item instanceof Query) {
91
                [$sql, $params] = $this->queryBuilder->build($item, $params);
92
                $placeholders[] = $this->buildSubqueryArray($sql, $expression);
93
                continue;
94
            }
95
96
            $item = $this->typecastValue($expression, $item);
97
98
            if ($item instanceof ExpressionInterface) {
99
                $placeholders[] = $this->queryBuilder->buildExpression($item, $params);
100
                continue;
101
            }
102
103
            $placeholders[] = $this->queryBuilder->bindParam($item, $params);
104
        }
105
106
        return $placeholders;
107
    }
108
109
    private function unnestArrayExpression(ArrayExpression $expression, $value): ArrayExpression
110
    {
111
        $expressionClass = get_class($expression);
112
113
        return new $expressionClass($value, $expression->getType(), $expression->getDimension() - 1);
114
    }
115
116
    protected function getTypeHint(ArrayExpression $expression): string
117
    {
118
        if ($expression->getType() === null) {
119
            return '';
120
        }
121
122
        $result = '::' . $expression->getType();
123
        $result .= str_repeat('[]', $expression->getDimension());
124
125
        return $result;
126
    }
127
128
    /**
129
     * Build an array expression from a subquery SQL.
130
     *
131
     * @param string $sql the subquery SQL.
132
     * @param ArrayExpression $expression
133
     *
134
     * @return string the subquery array expression.
135
     */
136
    protected function buildSubqueryArray(string $sql, ArrayExpression $expression): string
137
    {
138
        return 'ARRAY(' . $sql . ')' . $this->getTypeHint($expression);
139
    }
140
141
    /**
142
     * Casts $value to use in $expression.
143
     *
144
     * @param ArrayExpression $expression
145
     * @param mixed $value
146
     *
147
     * @return int|JsonExpression
148
     */
149
    protected function typecastValue(ArrayExpression $expression, $value)
150
    {
151
        if ($value instanceof ExpressionInterface) {
152
            return $value;
153
        }
154
155
        if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
156
            return new JsonExpression($value);
157
        }
158
159
        return $value;
160
    }
161
}
162