Completed
Pull Request — master (#188)
by
unknown
62:48
created

DynamicAggregateFilter::preProcessSearch()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 56
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 2 Features 4
Metric Value
c 6
b 2
f 4
dl 0
loc 56
rs 8.7592
cc 6
eloc 37
nc 16
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\FilterManagerBundle\Filter\Widget\Dynamic;
13
14
use ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue;
15
use ONGR\ElasticsearchDSL\Aggregation\FilterAggregation;
16
use ONGR\ElasticsearchDSL\Aggregation\NestedAggregation;
17
use ONGR\ElasticsearchDSL\Aggregation\TermsAggregation;
18
use ONGR\ElasticsearchDSL\BuilderInterface;
19
use ONGR\ElasticsearchDSL\Query\BoolQuery;
20
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
21
use ONGR\ElasticsearchDSL\Query\NestedQuery;
22
use ONGR\ElasticsearchDSL\Query\TermQuery;
23
use ONGR\ElasticsearchDSL\Search;
24
use ONGR\ElasticsearchBundle\Result\DocumentIterator;
25
use ONGR\FilterManagerBundle\Filter\FilterState;
26
use ONGR\FilterManagerBundle\Filter\Helper\SizeAwareTrait;
27
use ONGR\FilterManagerBundle\Filter\Helper\ViewDataFactoryInterface;
28
use ONGR\FilterManagerBundle\Filter\ViewData\AggregateViewData;
29
use ONGR\FilterManagerBundle\Filter\ViewData;
30
use ONGR\FilterManagerBundle\Filter\Widget\AbstractSingleRequestValueFilter;
31
use ONGR\FilterManagerBundle\Filter\Helper\FieldAwareInterface;
32
use ONGR\FilterManagerBundle\Filter\Helper\FieldAwareTrait;
33
use ONGR\FilterManagerBundle\Search\SearchRequest;
34
use Symfony\Component\HttpFoundation\Request;
35
36
/**
37
 * This class provides single terms choice.
38
 */
39
class DynamicAggregateFilter extends AbstractSingleRequestValueFilter implements
40
    FieldAwareInterface,
41
    ViewDataFactoryInterface
42
{
43
    use FieldAwareTrait, SizeAwareTrait;
44
45
    /**
46
     * @var array
47
     */
48
    private $sortType;
49
50
    /**
51
     * @var string
52
     */
53
    private $nameField;
54
55
    /**
56
     * @param array $sortType
57
     */
58
    public function setSortType($sortType)
59
    {
60
        $this->sortType = $sortType;
61
    }
62
63
    /**
64
     * @return array
65
     */
66
    public function getSortType()
67
    {
68
        return $this->sortType;
69
    }
70
71
    /**
72
     * @return string
73
     */
74
    public function getNameField()
75
    {
76
        return $this->nameField;
77
    }
78
79
    /**
80
     * @param string $nameField
81
     */
82
    public function setNameField($nameField)
83
    {
84
        $this->nameField = $nameField;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function getState(Request $request)
91
    {
92
        $state = new FilterState();
93
        $value = $request->get($this->getRequestField());
94
95
        if (isset($value) && is_array($value)) {
96
            $state->setActive(true);
97
            $state->setValue($value);
98
            $state->setUrlParameters([$this->getRequestField() => $value]);
99
        }
100
101
        return $state;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
108
    {
109
        list($path, $field) = explode('>', $this->getField());
110
111
        if ($state && $state->isActive()) {
112
            $boolQuery = new BoolQuery();
113
            foreach ($state->getValue() as $value) {
114
                $boolQuery->add(
115
                    new NestedQuery(
116
                        $path,
117
                        new TermQuery($field, $value)
118
                    )
119
                );
120
            }
121
            $search->addPostFilter($boolQuery);
122
        }
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
129
    {
130
        list($path, $field) = explode('>', $this->getField());
131
        $name = $state->getName();
0 ignored issues
show
Bug introduced by
It seems like $state is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
132
        $aggregation = new NestedAggregation(
133
            $name,
134
            $path
135
        );
136
        $termsAggregation = new TermsAggregation('query', $field);
137
        $termsAggregation->addParameter('size', 0);
138
139
        if ($this->getSortType()) {
140
            $termsAggregation->addParameter('order', [$this->getSortType()['type'] => $this->getSortType()['order']]);
141
        }
142
143
        if ($this->getSize() > 0) {
144
            $termsAggregation->addParameter('size', $this->getSize());
145
        }
146
147
        $termsAggregation->addAggregation(
148
            new TermsAggregation('name', $this->getNameField())
149
        );
150
        $aggregation->addAggregation($termsAggregation);
151
        $filterAggregation = new FilterAggregation($name . '-filter');
152
153
        if (!empty($relatedSearch->getPostFilters())) {
154
            $filterAggregation->setFilter($relatedSearch->getPostFilters());
155
        } else {
156
            $filterAggregation->setFilter(new MatchAllQuery());
157
        }
158
159
        if ($state->isActive()) {
160
            foreach ($state->getValue() as $key => $term) {
161
                $terms = $state->getValue();
162
                unset($terms[$key]);
163
164
                $this->addSubFilterAggregation(
165
                    $filterAggregation,
166
                    $aggregation,
167
                    $terms,
168
                    $term
169
                );
170
            }
171
172
            $this->addSubFilterAggregation(
173
                $filterAggregation,
174
                $aggregation,
175
                $state->getValue(),
176
                'all-selected'
177
            );
178
        } else {
179
            $filterAggregation->addAggregation($aggregation);
180
        }
181
182
        $search->addAggregation($filterAggregation);
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function createViewData()
189
    {
190
        return new AggregateViewData();
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getViewData(DocumentIterator $result, ViewData $data)
197
    {
198
        $unsortedChoices = [];
199
        $activeNames = $data->getState()->isActive() ? array_keys($data->getState()->getValue()) : [];
200
        $filterAggregations = $this->fetchAggregation($result, $data->getName(), $data->getState()->getValue());
201
202
        /** @var AggregationValue $bucket */
203
        foreach ($filterAggregations as $activeName => $aggregation) {
204
            foreach ($aggregation as $bucket) {
205
                $name = $bucket->getAggregation('name')->getBuckets()[0]['key'];
206
207
                if ($name != $activeName && $activeName != 'all-selected') {
208
                    continue;
209
                }
210
211
                $active = $this->isChoiceActive($bucket['key'], $data);
212
                $choice = new ViewData\Choice();
213
                $choice->setLabel($bucket->getValue('key'));
214
                $choice->setCount($bucket['doc_count']);
215
                $choice->setActive($active);
216
217
                $choice->setUrlParameters(
218
                    $this->getOptionUrlParameters($bucket['key'], $name, $data, $active)
219
                );
220
221
                if ($activeName == 'all-selected') {
222
                    $unsortedChoices[$activeName][$name][] = $choice;
223
                } else {
224
                    $unsortedChoices[$activeName][] = $choice;
225
                }
226
            }
227
        }
228
229
        foreach ($unsortedChoices['all-selected'] as $name => $buckets) {
230
            if (in_array($name, $activeNames)) {
231
                continue;
232
            }
233
234
            $unsortedChoices[$name] = $buckets;
235
        }
236
237
        unset($unsortedChoices['all-selected']);
238
        ksort($unsortedChoices);
239
240
        /** @var AggregateViewData $data */
241
        foreach ($unsortedChoices as $name => $choices) {
242
            $choiceViewData = new ViewData\ChoicesAwareViewData();
243
            $choiceViewData->setName($name);
244
            $choiceViewData->setChoices($choices);
245
            $choiceViewData->setUrlParameters([]);
246
            $choiceViewData->setResetUrlParameters([]);
247
            $data->addItem($choiceViewData);
248
        }
249
250
        return $data;
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256
    public function isRelated()
257
    {
258
        return true;
259
    }
260
261
    /**
262
     * Fetches buckets from search results.
263
     *
264
     * @param DocumentIterator $result     Search results.
265
     * @param string           $filterName Filter name.
266
     * @param array            $values     Values from the state object
267
     *
268
     * @return array Buckets.
269
     */
270
    private function fetchAggregation(DocumentIterator $result, $filterName, $values)
271
    {
272
        $data = [];
273
        $aggregation = $result->getAggregation(sprintf('%s-filter', $filterName));
274
275
        if ($aggregation->getAggregation($filterName)) {
276
            $aggregation = $aggregation->find($filterName.'.query');
277
            $data['all-selected'] = $aggregation;
278
279
            return $data;
280
        }
281
282
        if (!empty($values)) {
283
            foreach ($values as $name => $value) {
284
                $data[$name] = $aggregation->find(sprintf('%s.%s.query', $value, $filterName));
285
            }
286
287
            $data['all-selected'] = $aggregation->find(sprintf('all-selected.%s.query', $filterName));
288
289
            return $data;
290
        }
291
292
        return [];
293
    }
294
295
    /**
296
     * A method used to add an additional filter to the aggregations
297
     * in preProcessSearch
298
     *
299
     * @param FilterAggregation $filterAggregation
300
     * @param NestedAggregation $deepLevelAggregation
301
     * @param array             $terms Terms of additional filter
302
     * @param string            $aggName
303
     *
304
     * @return BuilderInterface
305
     */
306
    private function addSubFilterAggregation(
307
        $filterAggregation,
308
        $deepLevelAggregation,
309
        $terms,
310
        $aggName
311
    ) {
312
        list($path, $field) = explode('>', $this->getField());
313
        $boolQuery = new BoolQuery();
314
315
        foreach ($terms as $term) {
316
            $boolQuery->add(
317
                new NestedQuery($path, new TermQuery($field, $term))
318
            );
319
        }
320
321
        if ($boolQuery->getQueries() == []) {
322
            $boolQuery->add(new MatchAllQuery());
323
        }
324
325
        $innerFilterAggregation = new FilterAggregation(
326
            $aggName,
327
            $boolQuery
328
        );
329
        $innerFilterAggregation->addAggregation($deepLevelAggregation);
330
        $filterAggregation->addAggregation($innerFilterAggregation);
331
    }
332
333
    /**
334
     * @param string   $key
335
     * @param string   $name
336
     * @param ViewData $data
337
     * @param bool     $active True when the choice is active
338
     *
339
     * @return array
340
     */
341
    private function getOptionUrlParameters($key, $name, ViewData $data, $active)
342
    {
343
        $value = $data->getState()->getValue();
344
        $parameters = $data->getResetUrlParameters();
345
346
        if (!empty($value)) {
347
            if ($active) {
348
                unset($value[array_search($key, $value)]);
349
                $parameters[$this->getRequestField()] = $value;
350
351
                return $parameters;
352
            }
353
354
            $parameters[$this->getRequestField()] = $value;
355
        }
356
357
        $parameters[$this->getRequestField()][$name] = $key;
358
359
        return $parameters;
360
    }
361
362
    /**
363
     * Returns whether choice with the specified key is active.
364
     *
365
     * @param string   $key
366
     * @param ViewData $data
367
     *
368
     * @return bool
369
     */
370
    private function isChoiceActive($key, ViewData $data)
371
    {
372
        return $data->getState()->isActive() && in_array($key, $data->getState()->getValue());
373
    }
374
}
375