Completed
Push — master ( 3a2e0f...acc7aa )
by Simonas
262:41 queued 198:07
created

SingleTermChoice::setShowZeroChoices()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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\Choice;
13
14
use ONGR\ElasticsearchDSL\Aggregation\FilterAggregation;
15
use ONGR\ElasticsearchDSL\Aggregation\TermsAggregation;
16
use ONGR\ElasticsearchDSL\Query\TermQuery;
17
use ONGR\ElasticsearchDSL\Search;
18
use ONGR\ElasticsearchBundle\Result\DocumentIterator;
19
use ONGR\FilterManagerBundle\Filter\FilterState;
20
use ONGR\FilterManagerBundle\Filter\Helper\SizeAwareTrait;
21
use ONGR\FilterManagerBundle\Filter\Helper\ViewDataFactoryInterface;
22
use ONGR\FilterManagerBundle\Filter\ViewData\ChoicesAwareViewData;
23
use ONGR\FilterManagerBundle\Filter\ViewData;
24
use ONGR\FilterManagerBundle\Filter\Widget\AbstractSingleRequestValueFilter;
25
use ONGR\FilterManagerBundle\Filter\Helper\FieldAwareInterface;
26
use ONGR\FilterManagerBundle\Filter\Helper\FieldAwareTrait;
27
use ONGR\FilterManagerBundle\Search\SearchRequest;
28
29
/**
30
 * This class provides single terms choice.
31
 */
32
class SingleTermChoice extends AbstractSingleRequestValueFilter implements FieldAwareInterface, ViewDataFactoryInterface
33
{
34
    use FieldAwareTrait, SizeAwareTrait;
35
36
    /**
37
     * @var array
38
     */
39
    private $sortType;
40
41
    /**
42
     * @var bool
43
     */
44
    private $showZeroChoices;
45
46
    /**
47
     * @param array $sortType
48
     */
49
    public function setSortType($sortType)
50
    {
51
        $this->sortType = $sortType;
52
    }
53
54
    /**
55
     * @return array
56
     */
57
    public function getSortType()
58
    {
59
        return $this->sortType;
60
    }
61
62
    /**
63
     * @param bool $showZeroChoices
64
     */
65
    public function setShowZeroChoices($showZeroChoices)
66
    {
67
        $this->showZeroChoices = $showZeroChoices;
68
    }
69
70
    /**
71
     * @return bool
72
     */
73
    public function getShowZeroChoices()
74
    {
75
        return $this->showZeroChoices;
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function modifySearch(Search $search, FilterState $state = null, SearchRequest $request = null)
82
    {
83
        if ($state && $state->isActive()) {
84
            $search->addPostFilter(new TermQuery($this->getField(), $state->getValue()));
85
        }
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function preProcessSearch(Search $search, Search $relatedSearch, FilterState $state = null)
92
    {
93
        $name = $state ? $state->getName() : $this->getField();
94
        $aggregation = new TermsAggregation($name, $this->getField());
95
96
        if ($this->getSortType()) {
97
            $aggregation->addParameter('order', [$this->getSortType()['type'] => $this->getSortType()['order']]);
98
        }
99
100
        $aggregation->addParameter('size', 0);
101
        if ($this->getSize() > 0) {
102
            $aggregation->addParameter('size', $this->getSize());
103
        }
104
105
        if ($relatedSearch->getPostFilters()) {
106
            $filterAggregation = new FilterAggregation($name . '-filter');
107
            $filterAggregation->setFilter($relatedSearch->getPostFilters());
108
            $filterAggregation->addAggregation($aggregation);
109
            $search->addAggregation($filterAggregation);
110
111
            if ($this->showZeroChoices) {
112
                $unfilteredAggregation = clone $aggregation;
113
                $unfilteredAggregation->setName($name . '-unfiltered');
114
                $search->addAggregation($unfilteredAggregation);
115
            }
116
        } else {
117
            $search->addAggregation($aggregation);
118
        }
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function createViewData()
125
    {
126
        return new ChoicesAwareViewData();
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function getViewData(DocumentIterator $result, ViewData $data)
133
    {
134
        /** @var ChoicesAwareViewData $data */
135
136
        $unsortedChoices = [];
137
        $zeroValueChoices = [];
138
139
        if ($this->showZeroChoices && $agg = $result->getAggregation($data->getName() . '-unfiltered')) {
140
            foreach ($agg as $bucket) {
141
                $zeroValueChoices[$bucket['key']] = $bucket['doc_count'];
142
            }
143
        }
144
145
        foreach ($this->fetchAggregation($result, $data->getName()) as $bucket) {
146
            $active = $this->isChoiceActive($bucket['key'], $data);
147
            $choice = new ViewData\Choice();
148
            $choice->setLabel($bucket['key']);
149
            $choice->setCount($bucket['doc_count']);
150
            $choice->setActive($active);
151
            if ($active) {
152
                $choice->setUrlParameters($this->getUnsetUrlParameters($bucket['key'], $data));
153
            } else {
154
                $choice->setUrlParameters($this->getOptionUrlParameters($bucket['key'], $data));
155
            }
156
            $unsortedChoices[$bucket['key']] = $choice;
157
158
            if (!empty($zeroValueChoices)) {
159
                unset($zeroValueChoices[$bucket['key']]);
160
            }
161
        }
162
163
        foreach ($zeroValueChoices as $choiceLabel => $value) {
164
            $choice = new ViewData\Choice();
165
            $choice->setLabel($choiceLabel);
166
            $choice->setCount(0);
167
            $choice->setActive(false);
168
            $choice->setUrlParameters($data->getResetUrlParameters());
169
            $unsortedChoices[$choiceLabel] = $choice;
170
        }
171
172
        // Add the prioritized choices first.
173
        if ($this->getSortType()) {
174
            $unsortedChoices = $this->addPriorityChoices($unsortedChoices, $data);
175
        }
176
177
        foreach ($unsortedChoices as $choice) {
178
            $data->addChoice($choice);
179
        }
180
181
        return $data;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187
    public function isRelated()
188
    {
189
        return true;
190
    }
191
192
    /**
193
     * Adds prioritized choices.
194
     *
195
     * @param array                $unsortedChoices
196
     * @param ChoicesAwareViewData $data
197
     *
198
     * @return array
199
     */
200
    protected function addPriorityChoices(array $unsortedChoices, ChoicesAwareViewData $data)
201
    {
202
        foreach ($this->getSortType()['priorities'] as $name) {
203
            if (array_key_exists($name, $unsortedChoices)) {
204
                $data->addChoice($unsortedChoices[$name]);
205
                unset($unsortedChoices[$name]);
206
            }
207
        }
208
209
        return $unsortedChoices;
210
    }
211
212
    /**
213
     * Fetches buckets from search results.
214
     *
215
     * @param DocumentIterator $result Search results.
216
     * @param string           $name   Filter name.
217
     *
218
     * @return array Buckets.
219
     */
220
    protected function fetchAggregation(DocumentIterator $result, $name)
221
    {
222
        $aggregation = $result->getAggregation($name);
223
        if (isset($aggregation)) {
224
            return $aggregation;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $aggregation; (ONGR\ElasticsearchBundle...gation\AggregationValue) is incompatible with the return type documented by ONGR\FilterManagerBundle...hoice::fetchAggregation of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
225
        }
226
227
        $aggregation = $result->getAggregation(sprintf('%s-filter', $name));
228
        if (isset($aggregation)) {
229
            return $aggregation->find($name);
230
        }
231
232
        return [];
233
    }
234
235
    /**
236
     * @param string   $key
237
     * @param ViewData $data
238
     *
239
     * @return array
240
     */
241
    protected function getOptionUrlParameters($key, ViewData $data)
242
    {
243
        $parameters = $data->getResetUrlParameters();
244
        $parameters[$this->getRequestField()] = $key;
245
246
        return $parameters;
247
    }
248
249
    /**
250
     * Returns url with selected term disabled.
251
     *
252
     * @param string   $key
253
     * @param ViewData $data
254
     *
255
     * @return array
256
     */
257
    protected function getUnsetUrlParameters($key, ViewData $data)
258
    {
259
        return $data->getResetUrlParameters();
260
    }
261
262
    /**
263
     * Returns whether choice with the specified key is active.
264
     *
265
     * @param string   $key
266
     * @param ViewData $data
267
     *
268
     * @return bool
269
     */
270
    protected function isChoiceActive($key, ViewData $data)
271
    {
272
        return $data->getState()->isActive() && $data->getState()->getValue() == $key;
273
    }
274
}
275