Completed
Pull Request — master (#188)
by
unknown
65:11
created

DynamicAggregateFilter::getViewData()   C

Complexity

Conditions 10
Paths 60

Size

Total Lines 56
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 3 Features 3
Metric Value
c 9
b 3
f 3
dl 0
loc 56
rs 6.7741
cc 10
eloc 34
nc 60
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 DynamicAggregateFilter extends AbstractSingleRequestValueFilter
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between "AbstractSingleRequestValueFilter" and comma; 1 found
Loading history...
40
    implements FieldAwareInterface, ViewDataFactoryInterface
0 ignored issues
show
Coding Style introduced by
The implements keyword must be on the same line as the class name
Loading history...
41
{
42
    use FieldAwareTrait, SizeAwareTrait;
43
44
    /**
45
     * @var array
46
     */
47
    private $sortType;
48
49
    /**
50
     * @var string
51
     */
52
    private $nameField;
53
54
    /**
55
     * @param array $sortType
56
     */
57
    public function setSortType($sortType)
58
    {
59
        $this->sortType = $sortType;
60
    }
61
62
    /**
63
     * @return array
64
     */
65
    public function getSortType()
66
    {
67
        return $this->sortType;
68
    }
69
70
    /**
71
     * @return string
72
     */
73
    public function getNameField()
74
    {
75
        return $this->nameField;
76
    }
77
78
    /**
79
     * @param string $nameField
80
     */
81
    public function setNameField($nameField)
82
    {
83
        $this->nameField = $nameField;
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function getState(Request $request)
90
    {
91
        $state = new FilterState();
92
        $value = $request->get($this->getRequestField());
93
94
        if (isset($value) && is_array($value)) {
95
            $state->setActive(true);
96
            $state->setValue($value);
97
            $state->setUrlParameters([$this->getRequestField() => $value]);
98
        }
99
100
        return $state;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
107
    {
108
        list($path, $field) = explode('>', $this->getField());
109
110
        if ($state && $state->isActive()) {
111
            $boolQuery = new BoolQuery();
112
            foreach ($state->getValue() as $value) {
113
                $boolQuery->add(
114
                    new NestedQuery(
115
                        $path,
116
                        new TermQuery($field, $value)
117
                    )
118
                );
119
            }
120
            $search->addPostFilter($boolQuery);
121
        }
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
128
    {
129
        list($path, $field) = explode('>', $this->getField());
130
        $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...
131
        $aggregation = new NestedAggregation(
132
            $name,
133
            $path
134
        );
135
        $termsAggregation = new TermsAggregation('query', $field);
136
        $termsAggregation->addParameter('size', 0);
137
138
        if ($this->getSortType()) {
139
            $termsAggregation->addParameter('order', [$this->getSortType()['type'] => $this->getSortType()['order']]);
140
        }
141
142
        if ($this->getSize() > 0) {
143
            $termsAggregation->addParameter('size', $this->getSize());
144
        }
145
146
        $termsAggregation->addAggregation(
147
            new TermsAggregation('name', $this->getNameField())
148
        );
149
        $aggregation->addAggregation($termsAggregation);
150
        $filterAggregation = new FilterAggregation($name . '-filter');
151
152
        if (!empty($relatedSearch->getPostFilters())) {
153
            $filterAggregation->setFilter($relatedSearch->getPostFilters());
154
        } else {
155
            $filterAggregation->setFilter(new MatchAllQuery());
156
        }
157
158
        if ($state->isActive()) {
159
            foreach ($state->getValue() as $key => $term) {
160
                $terms = $state->getValue();
161
                unset($terms[$key]);
162
163
                $this->addSubFilterAggregation(
164
                    $filterAggregation,
165
                    $aggregation,
166
                    $terms,
167
                    $term
168
                );
169
            }
170
171
            $this->addSubFilterAggregation(
172
                $filterAggregation,
173
                $aggregation,
174
                $state->getValue(),
175
                'all-selected'
176
            );
177
        } else {
178
            $filterAggregation->addAggregation($aggregation);
179
        }
180
181
        $search->addAggregation($filterAggregation);
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function createViewData()
188
    {
189
        return new AggregateViewData();
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function getViewData(DocumentIterator $result, ViewData $data)
196
    {
197
        $unsortedChoices = [];
198
        $activeNames = $data->getState()->isActive() ? array_keys($data->getState()->getValue()) : [];
199
        $filterAggregations = $this->fetchAggregation($result, $data->getName(), $data->getState()->getValue());
200
201
        /** @var AggregationValue $bucket */
202
        foreach ($filterAggregations as $activeName => $aggregation) {
203
            foreach ($aggregation as $bucket) {
204
                $name = $bucket->getAggregation('name')->getBuckets()[0]['key'];
205
206
                if ($name != $activeName && $activeName != 'all-selected') {
207
                    continue;
208
                }
209
210
                $active = $this->isChoiceActive($bucket['key'], $data);
211
                $choice = new ViewData\Choice();
212
                $choice->setLabel($bucket->getValue('key'));
213
                $choice->setCount($bucket['doc_count']);
214
                $choice->setActive($active);
215
216
                $choice->setUrlParameters(
217
                    $this->getOptionUrlParameters($bucket['key'], $name, $data, $active)
218
                );
219
220
                if ($activeName == 'all-selected') {
221
                    $unsortedChoices[$activeName][$name][] = $choice;
222
                } else {
223
                    $unsortedChoices[$activeName][] = $choice;
224
                }
225
            }
226
        }
227
228
        foreach ($unsortedChoices['all-selected'] as $name => $buckets) {
229
            if (in_array($name, $activeNames)) {
230
                continue;
231
            }
232
233
            $unsortedChoices[$name] = $buckets;
234
        }
235
236
        unset($unsortedChoices['all-selected']);
237
        ksort($unsortedChoices);
238
239
        /** @var AggregateViewData $data */
240
        foreach ($unsortedChoices as $name => $choices) {
241
            $choiceViewData = new ViewData\ChoicesAwareViewData();
242
            $choiceViewData->setName($name);
243
            $choiceViewData->setChoices($choices);
244
            $choiceViewData->setUrlParameters([]);
245
            $choiceViewData->setResetUrlParameters([]);
246
            $data->addItem($choiceViewData);
247
        }
248
249
        return $data;
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255
    public function isRelated()
256
    {
257
        return true;
258
    }
259
260
    /**
261
     * Fetches buckets from search results.
262
     *
263
     * @param DocumentIterator $result     Search results.
264
     * @param string           $filterName Filter name.
265
     * @param array            $values     Values from the state object
266
     *
267
     * @return array Buckets.
268
     */
269
    private function fetchAggregation(DocumentIterator $result, $filterName, $values)
270
    {
271
        $data = [];
272
        $aggregation = $result->getAggregation(sprintf('%s-filter', $filterName));
273
274
        if ($aggregation->getAggregation($filterName)) {
275
            $aggregation = $aggregation->find($filterName.'.query');
276
            $data['all-selected'] = $aggregation;
277
278
            return $data;
279
        }
280
281
        if (!empty($values)) {
282
            foreach ($values as $name => $value) {
283
                $data[$name] = $aggregation->find(sprintf('%s.%s.query', $value, $filterName));
284
            }
285
286
            $data['all-selected'] = $aggregation->find(sprintf('all-selected.%s.query', $filterName));
287
288
            return $data;
289
        }
290
291
        return [];
292
    }
293
294
    /**
295
     * A method used to add an additional filter to the aggregations
296
     * in preProcessSearch
297
     *
298
     * @param FilterAggregation $filterAggregation
299
     * @param NestedAggregation $deepLevelAggregation
300
     * @param array             $terms Terms of additional filter
301
     * @param string            $aggName
302
     *
303
     * @return BuilderInterface
304
     */
305
    private function addSubFilterAggregation(
306
        $filterAggregation,
307
        $deepLevelAggregation,
308
        $terms,
309
        $aggName
310
    ) {
311
        list($path, $field) = explode('>', $this->getField());
312
        $boolQuery = new BoolQuery();
313
314
        foreach ($terms as $term) {
315
            $boolQuery->add(
316
                new NestedQuery($path, new TermQuery($field, $term))
317
            );
318
        }
319
320
        if ($boolQuery->getQueries() == []) {
321
            $boolQuery->add(new MatchAllQuery());
322
        }
323
324
        $innerFilterAggregation = new FilterAggregation(
325
            $aggName,
326
            $boolQuery
327
        );
328
        $innerFilterAggregation->addAggregation($deepLevelAggregation);
329
        $filterAggregation->addAggregation($innerFilterAggregation);
330
    }
331
332
    /**
333
     * @param string   $key
334
     * @param string   $name
335
     * @param ViewData $data
336
     * @param bool     $active True when the choice is active
337
     *
338
     * @return array
339
     */
340
    private function getOptionUrlParameters($key, $name, ViewData $data, $active)
341
    {
342
        $value = $data->getState()->getValue();
343
        $parameters = $data->getResetUrlParameters();
344
345
        if (!empty($value)) {
346
            if ($active) {
347
                unset($value[array_search($key, $value)]);
348
                $parameters[$this->getRequestField()] = $value;
349
350
                return $parameters;
351
            }
352
353
            $parameters[$this->getRequestField()] = $value;
354
        }
355
356
        $parameters[$this->getRequestField()][$name] = $key;
357
358
        return $parameters;
359
    }
360
361
    /**
362
     * Returns whether choice with the specified key is active.
363
     *
364
     * @param string   $key
365
     * @param ViewData $data
366
     *
367
     * @return bool
368
     */
369
    private function isChoiceActive($key, ViewData $data)
370
    {
371
        return $data->getState()->isActive() && in_array($key, $data->getState()->getValue());
372
    }
373
}
374