Completed
Push — master ( 208b18...1a63d3 )
by Bao
06:16
created

ConditionExpression::parseBetweenCondition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
namespace BaoPham\DynamoDb\Parsers;
4
5
use BaoPham\DynamoDb\ComparisonOperator;
6
use BaoPham\DynamoDb\NotSupportedException;
7
use BaoPham\DynamoDb\Facades\DynamoDb;
8
9
class ConditionExpression
10
{
11
    const OPERATORS = [
12
        ComparisonOperator::EQ => '%s = :%s',
13
        ComparisonOperator::LE => '%s <= :%s',
14
        ComparisonOperator::LT => '%s < :%s',
15
        ComparisonOperator::GE => '%s >= :%s',
16
        ComparisonOperator::GT => '%s > :%s',
17
        ComparisonOperator::BEGINS_WITH => 'begins_with(%s, :%s)',
18
        ComparisonOperator::BETWEEN => '(%s BETWEEN :%s AND :%s)',
19
        ComparisonOperator::CONTAINS => 'contains(%s, :%s)',
20
        ComparisonOperator::NOT_CONTAINS => 'NOT contains(%s, :%s)',
21
        ComparisonOperator::NULL => 'attribute_not_exists(%s)',
22
        ComparisonOperator::NOT_NULL => 'attribute_exists(%s)',
23
        ComparisonOperator::NE => '%s <> :%s',
24
        ComparisonOperator::IN => '%s IN (%s)',
25
    ];
26
27
    /**
28
     * @var ExpressionAttributeValues
29
     */
30
    protected $values;
31
32
    /**
33
     * @var ExpressionAttributeNames
34
     */
35
    protected $names;
36
37
    /**
38
     * @var Placeholder
39
     */
40
    protected $placeholder;
41
42 117
    public function __construct(
43
        Placeholder $placeholder,
44
        ExpressionAttributeValues $values,
45
        ExpressionAttributeNames $names
46
    ) {
47 117
        $this->placeholder = $placeholder;
48 117
        $this->values = $values;
49 117
        $this->names = $names;
50 117
    }
51
52
    /**
53
     * @param array $where
54
     *   [
55
     *     'column' => 'name',
56
     *     'type' => 'EQ',
57
     *     'value' => 'foo',
58
     *     'boolean' => 'and',
59
     *   ]
60
     *
61
     * @return string
62
     * @throws NotSupportedException
63
     */
64 70
    public function parse($where)
65
    {
66 70
        if (empty($where)) {
67
            return '';
68
        }
69
70 70
        $parsed = [];
71
72 70
        foreach ($where as $condition) {
73 70
            $boolean = array_get($condition, 'boolean');
74 70
            $value = array_get($condition, 'value');
75 70
            $type = array_get($condition, 'type');
76
77 70
            $prefix = '';
78
79 70
            if (count($parsed) > 0) {
80 44
                $prefix = strtoupper($boolean) . ' ';
81
            }
82
83 70
            if ($type === 'Nested') {
84 3
                $parsed[] = $prefix . $this->parseNestedCondition($value);
85 3
                continue;
86
            }
87
88 70
            $parsed[] = $prefix . $this->parseCondition(
89 70
                array_get($condition, 'column'),
90 70
                $type,
91 70
                $value
92
            );
93
        }
94
95 70
        return implode(' ', $parsed);
96
    }
97
98 112
    public function reset()
99
    {
100 112
        $this->placeholder->reset();
101 112
        $this->names->reset();
102 112
        $this->values->reset();
103 112
    }
104
105 63
    protected function getSupportedOperators()
106
    {
107 63
        return static::OPERATORS;
108
    }
109
110 3
    protected function parseNestedCondition(array $conditions)
111
    {
112 3
        return '(' . $this->parse($conditions) . ')';
113
    }
114
115 70
    protected function parseCondition($name, $operator, $value)
116
    {
117 70
        $operators = $this->getSupportedOperators();
118
119 70
        if (empty($operators[$operator])) {
120
            throw new NotSupportedException("$operator is not supported");
121
        }
122
123 70
        $template = $operators[$operator];
124
125 70
        $this->names->set($name);
126
127 70
        if ($operator === ComparisonOperator::BETWEEN) {
128 3
            return $this->parseBetweenCondition($name, $value, $template);
129
        }
130
131 68
        if ($operator === ComparisonOperator::IN) {
132 3
            return $this->parseInCondition($name, $value, $template);
133
        }
134
135 66
        if ($operator === ComparisonOperator::NULL || $operator === ComparisonOperator::NOT_NULL) {
136 5
            return $this->parseNullCondition($name, $template);
137
        }
138
139 62
        $placeholder = $this->placeholder->next();
140
141 62
        $this->values->set($placeholder, DynamoDb::marshalValue($value));
142
143 62
        return sprintf($template, $this->names->placeholder($name), $placeholder);
144
    }
145
146 3
    protected function parseBetweenCondition($name, $value, $template)
147
    {
148 3
        $first = $this->placeholder->next();
149
150 3
        $second = $this->placeholder->next();
151
152 3
        $this->values->set($first, DynamoDb::marshalValue($value[0]));
153
154 3
        $this->values->set($second, DynamoDb::marshalValue($value[1]));
155
156 3
        return sprintf($template, $this->names->placeholder($name), $first, $second);
157
    }
158
159 3
    protected function parseInCondition($name, $value, $template)
160
    {
161 3
        $valuePlaceholders = [];
162
163 3
        foreach ($value as $item) {
164 3
            $placeholder = $this->placeholder->next();
165
166 3
            $valuePlaceholders[] = ":" . $placeholder;
167
168 3
            $this->values->set($placeholder, DynamoDb::marshalValue($item));
169
        }
170
171 3
        return sprintf($template, $this->names->placeholder($name), implode(', ', $valuePlaceholders));
172
    }
173
174 5
    protected function parseNullCondition($name, $template)
175
    {
176 5
        return sprintf($template, $this->names->placeholder($name));
177
    }
178
}
179