Completed
Pull Request — master (#195)
by
unknown
63:20
created

DynamicAggregate::getViewData()   C

Complexity

Conditions 13
Paths 80

Size

Total Lines 63
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 63
rs 6.1128
cc 13
eloc 37
nc 80
nop 2

How to fix   Long Method    Complexity   

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 DynamicAggregate extends AbstractSingleRequestValueFilter implements
40
    FieldAwareInterface,
41
    ViewDataFactoryInterface
42
{
43
    use FieldAwareTrait;
44
45
    /**
46
     * @var array
47
     */
48
    private $sortType;
49
50
    /**
51
     * @var string
52
     */
53
    private $nameField;
54
55
    /**
56
     * @var bool
57
     */
58
    private $showZeroChoices;
59
60
    /**
61
     * @param array $sortType
62
     */
63
    public function setSortType($sortType)
64
    {
65
        $this->sortType = $sortType;
66
    }
67
68
    /**
69
     * @return array
70
     */
71
    public function getSortType()
72
    {
73
        return $this->sortType;
74
    }
75
76
    /**
77
     * @return string
78
     */
79
    public function getNameField()
80
    {
81
        return $this->nameField;
82
    }
83
84
    /**
85
     * @param string $nameField
86
     */
87
    public function setNameField($nameField)
88
    {
89
        $this->nameField = $nameField;
90
    }
91
92
    /**
93
     * @return bool
94
     */
95
    public function getShowZeroChoices()
96
    {
97
        return $this->showZeroChoices;
98
    }
99
100
    /**
101
     * @param bool $showZeroChoices
102
     */
103
    public function setShowZeroChoices($showZeroChoices)
104
    {
105
        $this->showZeroChoices = $showZeroChoices;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function getState(Request $request)
112
    {
113
        $state = new FilterState();
114
        $value = $request->get($this->getRequestField());
115
116
        if (isset($value) && is_array($value)) {
117
            $state->setActive(true);
118
            $state->setValue($value);
119
            $state->setUrlParameters([$this->getRequestField() => $value]);
120
        }
121
122
        return $state;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
129
    {
130
        list($path, $field) = explode('>', $this->getField());
131
132
        if ($state && $state->isActive()) {
133
            $boolQuery = new BoolQuery();
134
            foreach ($state->getValue() as $value) {
135
                $boolQuery->add(
136
                    new NestedQuery(
137
                        $path,
138
                        new TermQuery($field, $value)
139
                    )
140
                );
141
            }
142
            $search->addPostFilter($boolQuery);
143
        }
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
150
    {
151
        list($path, $field) = explode('>', $this->getField());
152
        $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...
153
        $aggregation = new NestedAggregation(
154
            $name,
155
            $path
156
        );
157
        $termsAggregation = new TermsAggregation('query', $field);
158
        $termsAggregation->addParameter('size', 0);
159
160
        if ($this->getSortType()) {
161
            $termsAggregation->addParameter('order', [$this->getSortType()['type'] => $this->getSortType()['order']]);
162
        }
163
164
        $termsAggregation->addAggregation(
165
            new TermsAggregation('name', $this->getNameField())
166
        );
167
        $aggregation->addAggregation($termsAggregation);
168
        $filterAggregation = new FilterAggregation($name . '-filter');
169
170
        if (!empty($relatedSearch->getPostFilters())) {
171
            $filterAggregation->setFilter($relatedSearch->getPostFilters());
172
        } else {
173
            $filterAggregation->setFilter(new MatchAllQuery());
174
        }
175
176
        if ($state->isActive()) {
177
            foreach ($state->getValue() as $key => $term) {
178
                $terms = $state->getValue();
179
                unset($terms[$key]);
180
181
                $this->addSubFilterAggregation(
182
                    $filterAggregation,
183
                    $aggregation,
184
                    $terms,
185
                    $key
186
                );
187
            }
188
        }
189
190
        $valueArray = $state->getValue() ? $state->getValue() : [];
191
        $this->addSubFilterAggregation(
192
            $filterAggregation,
193
            $aggregation,
194
            $valueArray,
195
            'all-selected'
196
        );
197
198
        $search->addAggregation($filterAggregation);
199
200
        if ($this->getShowZeroChoices()) {
201
            $search->addAggregation($aggregation);
202
        }
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208
    public function createViewData()
209
    {
210
        return new AggregateViewData();
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function getViewData(DocumentIterator $result, ViewData $data)
217
    {
218
        $unsortedChoices = [];
219
        $activeNames = $data->getState()->isActive() ? array_keys($data->getState()->getValue()) : [];
220
        $filterAggregations = $this->fetchAggregation($result, $data->getName(), $data->getState()->getValue());
221
222
        if ($this->getShowZeroChoices() && $data->getState()->isActive()) {
223
            $unsortedChoices = $this->formInitialUnsortedChoices($result, $data);
224
        }
225
226
        /** @var AggregationValue $bucket */
227
        foreach ($filterAggregations as $activeName => $aggregation) {
228
            foreach ($aggregation as $bucket) {
229
                $name = $bucket->getAggregation('name')->getBuckets()[0]['key'];
230
231
                if ($name != $activeName && $activeName != 'all-selected') {
232
                    continue;
233
                }
234
235
                $active = $this->isChoiceActive($bucket['key'], $data, $activeName);
236
                $choice = new ViewData\Choice();
237
                $choice->setLabel($bucket->getValue('key'));
238
                $choice->setCount($bucket['doc_count']);
239
                $choice->setActive($active);
240
241
                $choice->setUrlParameters(
242
                    $this->getOptionUrlParameters($bucket['key'], $name, $data, $active)
243
                );
244
245
                if ($activeName == 'all-selected') {
246
                    $unsortedChoices[$activeName][$name][$bucket['key']] = $choice;
247
                } else {
248
                    $unsortedChoices[$activeName][$bucket['key']] = $choice;
249
                }
250
            }
251
        }
252
253
        if (isset($unsortedChoices['all-selected'])) {
254
            foreach ($unsortedChoices['all-selected'] as $name => $buckets) {
255
                if (in_array($name, $activeNames)) {
256
                    continue;
257
                }
258
259
                $unsortedChoices[$name] = $buckets;
260
            }
261
262
            unset($unsortedChoices['all-selected']);
263
        }
264
265
        ksort($unsortedChoices);
266
267
        /** @var AggregateViewData $data */
268
        foreach ($unsortedChoices as $name => $choices) {
269
            $choiceViewData = new ViewData\ChoicesAwareViewData();
270
            $choiceViewData->setName($name);
271
            $choiceViewData->setChoices($choices);
272
            $choiceViewData->setUrlParameters([]);
273
            $choiceViewData->setResetUrlParameters([]);
274
            $data->addItem($choiceViewData);
275
        }
276
277
        return $data;
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function isRelated()
284
    {
285
        return true;
286
    }
287
288
    /**
289
     * Fetches buckets from search results.
290
     *
291
     * @param DocumentIterator $result     Search results.
292
     * @param string           $filterName Filter name.
293
     * @param array            $values     Values from the state object
294
     *
295
     * @return array Buckets.
296
     */
297
    protected function fetchAggregation(DocumentIterator $result, $filterName, $values)
298
    {
299
        $data = [];
300
        $values = empty($values) ? [] : $values;
301
        $aggregation = $result->getAggregation(sprintf('%s-filter', $filterName));
302
303
        foreach ($values as $name => $value) {
304
            $data[$name] = $aggregation->find(sprintf('%s.%s.query', $name, $filterName));
305
        }
306
307
        $data['all-selected'] = $aggregation->find(sprintf('all-selected.%s.query', $filterName));
308
309
        return $data;
310
    }
311
312
    /**
313
     * A method used to add an additional filter to the aggregations
314
     * in preProcessSearch
315
     *
316
     * @param FilterAggregation $filterAggregation
317
     * @param NestedAggregation $deepLevelAggregation
318
     * @param array             $terms Terms of additional filter
319
     * @param string            $aggName
320
     *
321
     * @return BuilderInterface
322
     */
323 View Code Duplication
    protected function addSubFilterAggregation(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
324
        $filterAggregation,
325
        $deepLevelAggregation,
326
        $terms,
327
        $aggName
328
    ) {
329
        list($path, $field) = explode('>', $this->getField());
330
        $boolQuery = new BoolQuery();
331
332
        foreach ($terms as $term) {
333
            $boolQuery->add(
334
                new NestedQuery($path, new TermQuery($field, $term))
335
            );
336
        }
337
338
        if ($boolQuery->getQueries() == []) {
339
            $boolQuery->add(new MatchAllQuery());
340
        }
341
342
        $innerFilterAggregation = new FilterAggregation(
343
            $aggName,
344
            $boolQuery
345
        );
346
        $innerFilterAggregation->addAggregation($deepLevelAggregation);
347
        $filterAggregation->addAggregation($innerFilterAggregation);
348
    }
349
350
    /**
351
     * @param string   $key
352
     * @param string   $name
353
     * @param ViewData $data
354
     * @param bool     $active True when the choice is active
355
     *
356
     * @return array
357
     */
358
    protected function getOptionUrlParameters($key, $name, ViewData $data, $active)
359
    {
360
        $value = $data->getState()->getValue();
361
        $parameters = $data->getResetUrlParameters();
362
363 View Code Duplication
        if (!empty($value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
364
            if ($active) {
365
                unset($value[array_search($key, $value)]);
366
                $parameters[$this->getRequestField()] = $value;
367
368
                return $parameters;
369
            }
370
371
            $parameters[$this->getRequestField()] = $value;
372
        }
373
374
        $parameters[$this->getRequestField()][$name] = $key;
375
376
        return $parameters;
377
    }
378
379
    /**
380
     * Returns whether choice with the specified key is active.
381
     *
382
     * @param string   $key
383
     * @param ViewData $data
384
     * @param string   $activeName
385
     *
386
     * @return bool
387
     */
388
    protected function isChoiceActive($key, ViewData $data, $activeName)
389
    {
390
        return $data->getState()->isActive() && in_array($key, $data->getState()->getValue());
391
    }
392
393
    /**
394
     * Forms $unsortedChoices array with all possible choices.
395
     * 0 is assigned to the document count of the choices.
396
     *
397
     * @param DocumentIterator $result
398
     * @param ViewData         $data
399
     *
400
     * @return array
401
     */
402
    private function formInitialUnsortedChoices($result, $data)
403
    {
404
        $unsortedChoices = [];
405
        $urlParameters = array_merge(
406
            $data->getResetUrlParameters(),
407
            $data->getState()->getUrlParameters()
408
        );
409
410
        foreach ($result->getAggregation($data->getName())->getAggregation('query') as $bucket) {
411
            $groupName = $bucket->getAggregation('name')->getBuckets()[0]['key'];
412
            $choice = new ViewData\Choice();
413
            $choice->setActive(false);
414
            $choice->setUrlParameters($urlParameters);
415
            $choice->setLabel($bucket['key']);
416
            $choice->setCount(0);
417
            $unsortedChoices[$groupName][$bucket['key']] = $choice;
418
            $unsortedChoices['all-selected'][$groupName][$bucket['key']] = $choice;
419
        }
420
421
        return $unsortedChoices;
422
    }
423
}
424