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