Passed
Pull Request — master (#35)
by
unknown
12:58
created

WIQL   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 51
c 1
b 0
f 0
dl 0
loc 229
rs 10
wmc 21

13 Methods

Rating   Name   Duplication   Size   Complexity  
A buildWhere() 0 7 2
A buildFrom() 0 3 1
A select() 0 5 1
A orWhere() 0 3 1
A from() 0 5 1
A where() 0 9 1
A buildOrder() 0 12 2
A orderBy() 0 5 1
A quote() 0 15 2
A buildSelect() 0 3 1
A when() 0 9 3
A getConditions() 0 19 4
A getQuery() 0 7 1
1
<?php
2
3
namespace TestMonitor\DevOps\Builders\WIQL;
4
5
use Closure;
6
7
class WIQL
8
{
9
    /**
10
     * @var string[]
11
     */
12
    protected $selects = [Field::ID];
13
14
    /**
15
     * @var string
16
     */
17
    protected $from = 'WorkItems';
18
19
    /**
20
     * @var array
21
     */
22
    protected $wheres = [];
23
24
    /**
25
     * @var array
26
     */
27
    protected $orders = [];
28
29
    /**
30
     * Set field selection.
31
     *
32
     * @param array $fields
33
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
34
     */
35
    public function select(array $fields): self
36
    {
37
        $this->selects = $fields;
38
39
        return $this;
40
    }
41
42
    /**
43
     * Set query source (WorkItems, workItemLinks).
44
     *
45
     * @param string $source
46
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
47
     */
48
    public function from(string $source): self
49
    {
50
        $this->from = $source;
51
52
        return $this;
53
    }
54
55
    /**
56
     * Add a new where condition.
57
     *
58
     * @param string|\Closure $field
59
     * @param mixed $operator
60
     * @param mixed $value
61
     * @param string $boolean
62
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
63
     */
64
    public function where(
65
        string|Closure $field,
66
        mixed $operator = Operator::EQUALS,
67
        mixed $value = null,
68
        string $boolean = Keyword::AND
69
    ): self {
70
        $this->wheres[] = compact('field', 'operator', 'value', 'boolean');
71
72
        return $this;
73
    }
74
75
    /**
76
     * Add a new where condition (using OR).
77
     *
78
     * @param string $field
79
     * @param mixed $operator
80
     * @param mixed $value
81
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
82
     */
83
    public function orWhere(string|Closure $field, mixed $operator = Operator::EQUALS, mixed $value = null): self
84
    {
85
        return $this->where($field, $operator, $value, Keyword::OR);
86
    }
87
88
    /**
89
     * Add new sort criteria.
90
     *
91
     * @param string $field
92
     * @param string $direction
93
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
94
     */
95
    public function orderBy(string $field, string $direction = 'ASC'): self
96
    {
97
        $this->orders[] = compact('field', 'direction');
98
99
        return $this;
100
    }
101
102
    /**
103
     * Executes the callback when value is true.
104
     *
105
     * @param mixed $value
106
     * @param callable $callback
107
     * @return \TestMonitor\DevOps\Builders\WIQL\WIQL
108
     */
109
    public function when(mixed $value, callable $callback): self
110
    {
111
        $value = $value instanceof Closure ? $value($this) : $value;
112
113
        if ($value) {
114
            return $callback($this, $value) ?? $this;
115
        }
116
117
        return $this;
118
    }
119
120
    /**
121
     * Quotes a value based on its operator.
122
     *
123
     * @param string $operator
124
     * @param mixed $value
125
     * @return string
126
     */
127
    public function quote(string $operator, mixed $value): string
128
    {
129
        if (in_array($operator, [Operator::IN, Operator::NOT_IN], true)) {
130
            $values = implode(
131
                ', ',
132
                array_map(
133
                    fn ($value) => "'$value'",
134
                    (array) $value
135
                )
136
            );
137
138
            return "($values)";
139
        }
140
141
        return "'{$value}'";
142
    }
143
144
    /**
145
     * Generates conditions based on the available "wheres".
146
     *
147
     * @return string
148
     */
149
    public function getConditions(): string
150
    {
151
        $conditions = '';
152
153
        foreach ($this->wheres as $key => $condition) {
154
            $conditions .= $key !== array_key_first($this->wheres) ? " {$condition['boolean']} " : '';
155
156
            if ($condition['field'] instanceof Closure) {
157
                $conditions .= '(' . $condition['field'](new WIQL)->getConditions() . ')';
158
159
                continue;
160
            }
161
162
            $values = $this->quote($condition['operator'], $condition['value']);
163
164
            $conditions .= "{$condition['field']} {$condition['operator']} {$values}";
165
        }
166
167
        return $conditions;
168
    }
169
170
    /**
171
     * Generates the WIQL query.
172
     *
173
     * @return string
174
     */
175
    public function getQuery(): string
176
    {
177
        return trim(
178
            $this->buildSelect() . ' ' .
179
            $this->buildFrom() . ' ' .
180
            $this->buildWhere() . ' ' .
181
            $this->buildOrder()
182
        );
183
    }
184
185
    /**
186
     * Generate the SELECT part of the query.
187
     *
188
     * @return string
189
     */
190
    protected function buildSelect(): string
191
    {
192
        return 'SELECT ' . implode(', ', $this->selects);
193
    }
194
195
    /**
196
     * Generate the FROM part of the query.
197
     *
198
     * @return string
199
     */
200
    protected function buildFrom(): string
201
    {
202
        return "FROM {$this->from}";
203
    }
204
205
    /**
206
     * Generate the WHERE part of the query.
207
     *
208
     * @return string
209
     */
210
    protected function buildWhere(): string
211
    {
212
        if (empty($this->wheres)) {
213
            return '';
214
        }
215
216
        return 'WHERE ' . $this->getConditions();
217
    }
218
219
    /**
220
     * Generate the ORDER BY part of the query.
221
     *
222
     * @return string
223
     */
224
    protected function buildOrder(): string
225
    {
226
        if (empty($this->orders)) {
227
            return '';
228
        }
229
230
        $criteria = array_map(
231
            fn ($sort) => trim("{$sort['field']} {$sort['direction']}"),
232
            $this->orders
233
        );
234
235
        return 'ORDER BY ' . implode(', ', $criteria);
236
    }
237
}
238