Completed
Push — master ( 2bc3d9...4d44a5 )
by Sergey
15:01
created

Filter::setOptions()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
rs 9.4285
cc 3
eloc 5
nc 4
nop 3
1
<?php namespace Isswp101\Persimmon\QueryBuilder;
2
3
use Illuminate\Support\Collection;
4
5
abstract class Filter {
6
7
    const MODE_INCLUDE = 'include';
8
    const MODE_EXCLUDE = 'exclude';
9
    const MODE_OFF = 'off';
10
11
    /**
12
     * Filter mode.
13
     *
14
     * @var string Can be as 'include', 'exclude', 'off'.
15
     */
16
    protected $mode;
17
18
    /**
19
     * Filter values.
20
     *
21
     * @var mixed
22
     */
23
    protected $values;
24
25
    /**
26
     * This array contains linked instances of current filter.
27
     *
28
     * @var array
29
     */
30
    protected $linkedFilters = [];
31
32
    /**
33
     * Filters are merged using AND (must) by default.
34
     * But you can override it and merge them using OR (should).
35
     *
36
     * @var string
37
     */
38
    protected $mergeType = 'AND';
39
40
    /**
41
     * Linked filters are merged using AND (must) by default.
42
     * But you can override it and merge them using OR (should).
43
     *
44
     * @var string
45
     */
46
    protected $logicalOperator = 'OR';
47
48
    /**
49
     * Constructor.
50
     *
51
     * @param mixed $values Filter values.
52
     * @param string $mode Filter mode.
53
     * @param string $logicalOperator Linked filters will be interconnected via "OR" || "AND".
54
     * @param array $linkedFilters
55
     */
56
    public function __construct($values = null, $mode = self::MODE_INCLUDE, $logicalOperator = 'OR', array $linkedFilters = []) {
57
        $this->values = $values;
58
        $this->setOptions($mode, $logicalOperator, $linkedFilters);
59
    }
60
61
    /**
62
     * Set filter options.
63
     *
64
     * @param string $mode
65
     * @param string $logicalOperator
66
     * @param array $linkedFilters
67
     * @return $this
68
     */
69
    public function setOptions($mode = null, $logicalOperator = null, array $linkedFilters = []) {
70
        $this->mode = is_null($mode) ? self::MODE_INCLUDE : $mode;
71
        $this->logicalOperator = is_null($logicalOperator) ? 'OR' : $logicalOperator;
72
        $this->linkedFilters = $linkedFilters;
73
        return $this;
74
    }
75
76
    /**
77
     * Returns the actual elasticsearch query for one filter.
78
     * {
79
     *   "term": {
80
     *     "price": "0"
81
     *   }
82
     * }
83
     *
84
     * @param mixed $values
85
     * @return array
86
     */
87
    abstract public function query($values);
88
89
    /**
90
     * Returns wrapped elasticsearch filter query.
91
     * {
92
     *   "bool": {
93
     *     "should": [],
94
     *     "must_not": []
95
     *   }
96
     * }
97
     *
98
     * @return array
99
     */
100
    public function makeQuery() {
101
        $query = $this->query($this->getValues());
102
103
        $map = [
104
            'AND' => 'must',
105
            'OR' => 'should'
106
        ];
107
108
        if ($this->isInclude()) {
109
            if ($this->getLogicalOperator() == 'AND') {
110
                $query = [
111
                    'bool' => [
112
                        'must' => [
113
                            $query
114
                        ]
115
                    ]
116
                ];
117
            }
118
            elseif ($this->getLogicalOperator() == 'OR') {
119
                $query = [
120
                    'bool' => [
121
                        'should' => [
122
                            $query
123
                        ]
124
                    ]
125
                ];
126
            }
127
        }
128
        elseif ($this->isExclude()) {
129
            $mergeOperator = $map[$this->getLogicalOperator()];
130
            $query = [
131
                'bool' => [
132
                    $mergeOperator => [
133
                        [
134
                            'bool' => [
135
                                'must_not' => [
136
                                    $query
137
                                ]
138
                            ]
139
                        ]
140
                    ]
141
                ]
142
            ];
143
        }
144
        elseif ($this->isOff()) {
145
            $query = [];
146
        }
147
148
        foreach ($this->getLinkedFilters() as $filter) {
149
            /** @var Filter $filter */
150
            $extraQuery = $filter->makeQuery();
151
152
//            if ($filter->isInclude()) {
1 ignored issue
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
153
                if ($this->getLogicalOperator() == 'AND') {
154
                    $query = $this->mergeBoolQuery($query, $extraQuery, 'must');
155
                }
156
                elseif ($this->getLogicalOperator() == 'OR') {
157
                    $query = $this->mergeBoolQuery($query, $extraQuery, 'should');
158
                }
159
//            }
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
160
//            elseif ($filter->isExclude()) {
161
//                $query = $this->mergeBoolQuery($query, $extraQuery, 'must_not');
162
//            }
163
        }
164
165
        return $query;
166
    }
167
168
    /**
169
     * Merges elastcisearch query with current filter query.
170
     *
171
     * @param array $query Elastcisearch query.
172
     * @return array Merged elasticsearch query.
173
     */
174
    public function mergeQuery(array $query) {
175
        $types = [
176
            'AND' => 'must',
177
            'OR' => 'should'
178
        ];
179
        $type = $this->getMergeType();
180
181
        $query['body']['filter']['bool'][$types[$type]][] = $this->makeQuery();
182
183
        return $query;
184
    }
185
186
    /**
187
     * Returns filter mode.
188
     *
189
     * @return string Can be as 'include', 'exclude', 'off'.
190
     */
191
    public function getMode() {
192
        return $this->mode;
193
    }
194
195
    /**
196
     * Returns true if mode is 'include'.
197
     *
198
     * @return bool
199
     */
200
    public function isInclude() {
201
        return $this->getMode() == self::MODE_INCLUDE;
202
    }
203
204
    /**
205
     * Returns true if mode is 'exclude'.
206
     *
207
     * @return bool
208
     */
209
    public function isExclude() {
210
        return $this->getMode() == self::MODE_EXCLUDE;
211
    }
212
213
    /**
214
     * Returns true if mode is 'off'.
215
     *
216
     * @return bool
217
     */
218
    public function isOff() {
219
        return $this->getMode() == self::MODE_OFF;
220
    }
221
222
    /**
223
     * Retuns filter values as collection.
224
     *
225
     * @return Collection
226
     */
227
    public function getValuesAsCollection() {
228
        return new Collection($this->values);
229
    }
230
231
    /**
232
     * Retuns filter values as array.
233
     *
234
     * @return mixed
235
     */
236
    public function getValues() {
237
        return $this->values;
238
    }
239
240
    /**
241
     * Returns linked instances of current filter.
242
     *
243
     * @return array
244
     */
245
    public function getLinkedFilters() {
246
        return $this->linkedFilters;
247
    }
248
249
    /**
250
     * Returns true if filter has linked filters.
251
     *
252
     * @return bool
253
     */
254
    public function hasLinkedFilters() {
255
        return !empty($this->linkedFilters);
256
    }
257
258
    /**
259
     * Returns filter merge type.
260
     *
261
     * @return string - "AND" || "OR"
262
     */
263
    public function getMergeType() {
264
        return $this->mergeType;
265
    }
266
267
    /**
268
     * Sets filter merge type.
269
     *
270
     * @param string $mergeType - "AND" || "OR"
271
     */
272
    public function setMergeType($mergeType) {
273
        $this->mergeType = $mergeType;
274
    }
275
276
    /**
277
     * Returns linked filters logical operator.
278
     *
279
     * @return string - "AND" || "OR"
280
     */
281
    public function getLogicalOperator() {
282
        return $this->logicalOperator;
283
    }
284
285
    /**
286
     * Updates linked filters logical operator.
287
     *
288
     * @param string $logicalOperator - "AND" || "OR"
289
     */
290
    public function setLogicalOperator($logicalOperator) {
291
        $this->logicalOperator = $logicalOperator;
292
    }
293
294
    /**
295
     * Returns true if merge type is valid.
296
     * @return bool
297
     */
298
    public function isMergeTypeValid() {
299
        return in_array($this->mergeType, ['AND', 'OR']);
300
    }
301
302
    /**
303
     * Merges BOOL elasticsearch queries.
304
     *
305
     * @param array $query1
306
     * @param array $query2
307
     * @param string $type - must, must_not, should
308
     * @return array
309
     */
310
    protected function mergeBoolQuery(array $query1, array $query2, $type) {
311
        if (empty($query2['bool'][$type])) {
312
            return $query1;
313
        }
314
        else {
315
            if (empty($query1['bool'][$type])) {
316
                $query1['bool'][$type] = [];
317
            }
318
        }
319
320
        $query1['bool'][$type] = array_merge($query1['bool'][$type], $query2['bool'][$type]);
321
322
        return $query1;
323
    }
324
325
}