Passed
Pull Request — master (#19)
by Wilmer
12:28
created

ArrayExpressionBuilder::buildPlaceholders()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
c 0
b 0
f 0
nc 7
nop 2
dl 0
loc 33
rs 8.0555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql\Expression;
6
7
use Yiisoft\Db\Expression\ArrayExpression;
8
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
9
use Yiisoft\Db\Expression\ExpressionBuilderTrait;
10
use Yiisoft\Db\Expression\ExpressionInterface;
11
use Yiisoft\Db\Expression\JsonExpression;
12
use Yiisoft\Db\Pgsql\Schema\Schema;
13
use Yiisoft\Db\Query\Query;
14
15
/**
16
 * Class ArrayExpressionBuilder builds {@see ArrayExpression} for Postgres SQL DBMS.
17
 */
18
class ArrayExpressionBuilder implements ExpressionBuilderInterface
19
{
20
    use ExpressionBuilderTrait;
21
22
    /**
23
     * {@inheritdoc}
24
     *
25
     * @param ArrayExpression|ExpressionInterface $expression the expression to be built
26
     */
27
    public function build(ExpressionInterface $expression, array &$params = []): string
28
    {
29
        $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

29
        /** @scrutinizer ignore-call */ 
30
        $value = $expression->getValue();
Loading history...
30
        if ($value === null) {
31
            return 'NULL';
32
        }
33
34
        if ($value instanceof Query) {
35
            [$sql, $params] = $this->queryBuilder->build($value, $params);
36
            return $this->buildSubqueryArray($sql, $expression);
37
        }
38
39
        $placeholders = $this->buildPlaceholders($expression, $params);
40
41
        return 'ARRAY[' . \implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
42
    }
43
44
    /**
45
     * Builds placeholders array out of $expression values
46
     *
47
     * @param ExpressionInterface|ArrayExpression $expression
48
     * @param array $params the binding parameters.
49
     *
50
     * @return array
51
     */
52
    protected function buildPlaceholders(ExpressionInterface $expression, &$params): array
53
    {
54
        $value = $expression->getValue();
55
56
        $placeholders = [];
57
        if ($value === null || (!\is_array($value) && !$value instanceof \Traversable)) {
58
            return $placeholders;
59
        }
60
61
        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

61
        if ($expression->/** @scrutinizer ignore-call */ getDimension() > 1) {
Loading history...
62
            foreach ($value as $item) {
63
                $placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
64
            }
65
            return $placeholders;
66
        }
67
68
        foreach ($value as $item) {
69
            if ($item instanceof Query) {
70
                list($sql, $params) = $this->queryBuilder->build($item, $params);
71
                $placeholders[] = $this->buildSubqueryArray($sql, $expression);
72
                continue;
73
            }
74
75
            $item = $this->typecastValue($expression, $item);
76
            if ($item instanceof ExpressionInterface) {
77
                $placeholders[] = $this->queryBuilder->buildExpression($item, $params);
78
                continue;
79
            }
80
81
            $placeholders[] = $this->queryBuilder->bindParam($item, $params);
82
        }
83
84
        return $placeholders;
85
    }
86
87
    /**
88
     * @param ArrayExpression $expression
89
     * @param mixed $value
90
     *
91
     * @return ArrayExpression
92
     */
93
    private function unnestArrayExpression(ArrayExpression $expression, $value): ArrayExpression
94
    {
95
        $expressionClass = \get_class($expression);
96
97
        return new $expressionClass($value, $expression->getType(), $expression->getDimension() - 1);
98
    }
99
100
    /**
101
     * @param ArrayExpression $expression
102
     *
103
     * @return string the typecast expression based on {@see type}
104
     */
105
    protected function getTypehint(ArrayExpression $expression): string
106
    {
107
        if ($expression->getType() === null) {
108
            return '';
109
        }
110
111
        $result = '::' . $expression->getType();
112
        $result .= \str_repeat('[]', $expression->getDimension());
113
114
        return $result;
115
    }
116
117
    /**
118
     * Build an array expression from a subquery SQL.
119
     *
120
     * @param string $sql the subquery SQL.
121
     * @param ArrayExpression $expression.
122
     *
123
     * @return string the subquery array expression.
124
     */
125
    protected function buildSubqueryArray($sql, ArrayExpression $expression): string
126
    {
127
        return 'ARRAY(' . $sql . ')' . $this->getTypehint($expression);
128
    }
129
130
    /**
131
     * Casts $value to use in $expression
132
     *
133
     * @param ArrayExpression $expression
134
     * @param mixed $value
135
     *
136
     * @return int|JsonExpression
137
     */
138
    protected function typecastValue(ArrayExpression $expression, $value)
139
    {
140
        if ($value instanceof ExpressionInterface) {
141
            return $value;
142
        }
143
144
        if (\in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
145
            return new JsonExpression($value);
146
        }
147
148
        return $value;
149
    }
150
}
151