Completed
Push — master ( 2037e3...db265d )
by Timo
48s
created

Faceting::modifyQuery()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 4
nop 1
crap 3
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Query\Modifier;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Query;
28
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
29
use ApacheSolrForTypo3\Solr\Util;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * Modifies a query to add faceting parameters
34
 *
35
 * @author Ingo Renner <[email protected]>
36
 * @author Daniel Poetzinger <[email protected]>
37
 * @author Sebastian Kurfuerst <[email protected]>
38
 */
39
class Faceting implements Modifier
40
{
41
    /**
42
     * @var \ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration|array
43
     */
44
    protected $configuration;
45
46
    protected $facetParameters = array();
47
48
    protected $facetFilters = array();
49
50
    protected $facetRendererFactory = null;
51
52
    /**
53
     * @var array
54
     */
55
    protected $allConfiguredFacets = array();
56
57
    /**
58
     * @param TypoScriptConfiguration $solrConfiguration
59
     */
60 27
    public function __construct($solrConfiguration = null)
61
    {
62 27
        if (!is_null($solrConfiguration)) {
63 7
            $this->configuration = $solrConfiguration;
64 7
        } else {
65 20
            $this->configuration = Util::getSolrConfiguration();
66
        }
67
68 27
        $this->allConfiguredFacets = $this->configuration->getSearchFacetingFacets();
69 27
        $this->facetRendererFactory = GeneralUtility::makeInstance('ApacheSolrForTypo3\\Solr\\Facet\\FacetRendererFactory',
70 27
            $this->allConfiguredFacets);
71 27
    }
72
73
    /**
74
     * Modifies the given query and adds the parameters necessary for faceted
75
     * search.
76
     *
77
     * @param Query $query The query to modify
78
     * @return Query The modified query with faceting parameters
79
     */
80 27
    public function modifyQuery(Query $query)
81
    {
82 27
        $query->setFaceting();
83 27
        $this->buildFacetingParameters();
84 27
        $this->addFacetQueryFilters();
85
86 27
        foreach ($this->facetParameters as $facetParameter => $value) {
87 27
            $query->addQueryParameter($facetParameter, $value);
88 27
        }
89
90 27
        foreach ($this->facetFilters as $filter) {
91 4
            $query->addFilter($filter);
92 27
        }
93
94 27
        return $query;
95
    }
96
97
    /**
98
     * Delegates the parameter building to specialized functions depending on
99
     * the type of facet to add.
100
     *
101
     */
102 27
    protected function buildFacetingParameters()
103
    {
104 27
        foreach ($this->allConfiguredFacets as $facetName => $facetConfiguration) {
105 27
            $facetName = substr($facetName, 0, -1);
106
107 27
            $facetParameterBuilder = $this->facetRendererFactory->getQueryFacetBuilderByFacetName($facetName);
108 27
            if (!is_null($facetParameterBuilder)) {
109
                $facetParameters = $facetParameterBuilder->buildFacetParameters($facetName,
110
                    $facetConfiguration);
111
            } else {
112 27
                $facetParameters = $this->buildFacetParameters($facetConfiguration);
113
            }
114
115 27
            $this->facetParameters = array_merge_recursive($this->facetParameters,
116 27
                $facetParameters);
117 27
        }
118 27
    }
119
120
    /**
121
     * Builds facet parameters for field facets
122
     *
123
     * @param array $facetConfiguration The facet's configuration
124
     * @return array
125
     */
126 27
    protected function buildFacetParameters(array $facetConfiguration)
127
    {
128 27
        $facetParameters = array();
129
130
        // simple for now, may add overrides f.<field_name>.facet.* later
131 27
        if ($this->configuration->getSearchFacetingKeepAllFacetsOnSelection()) {
132 2
            $facets = array();
133 2
            foreach ($this->allConfiguredFacets as $facet) {
134 2
                $facets[] = $facet['field'];
135 2
            }
136
137 2
            $facetParameters['facet.field'][] =
138 2
                '{!ex=' . implode(',', $facets) . '}'
139 2
                . $facetConfiguration['field'];
140 27
        } elseif ($facetConfiguration['keepAllOptionsOnSelection'] == 1) {
141 22
            $facetParameters['facet.field'][] =
142 22
                '{!ex=' . $facetConfiguration['field'] . '}'
143 22
                . $facetConfiguration['field'];
144 22
        } else {
145 25
            $facetParameters['facet.field'][] = $facetConfiguration['field'];
146
        }
147
148 27
        if (in_array($facetConfiguration['sortBy'],
149 27
            array('alpha', 'index', 'lex'))) {
150 1
            $facetParameters['f.' . $facetConfiguration['field'] . '.facet.sort'] = 'lex';
151 1
        }
152
153 27
        return $facetParameters;
154
    }
155
156
    /**
157
     * Adds filters specified through HTTP GET as filter query parameters to
158
     * the Solr query.
159
     *
160
     */
161 27
    protected function addFacetQueryFilters()
162
    {
163
        // todo refactor to use a request object
164 27
        $resultParameters = GeneralUtility::_GET('tx_solr');
165
166
        // format for filter URL parameter:
167
        // tx_solr[filter]=$facetName0:$facetValue0,$facetName1:$facetValue1,$facetName2:$facetValue2
168 27
        if (is_array($resultParameters['filter'])) {
169 4
            $filters = array_map('urldecode', $resultParameters['filter']);
170
            // $filters look like array('name:value1','name:value2','fieldname2:foo')
171 4
            $configuredFacets = $this->getConfiguredFacets();
172
            // first group the filters by facetName - so that we can
173
            // decide later whether we need to do AND or OR for multiple
174
            // filters for a certain facet/field
175
            // $filtersByFacetName look like array('name' => array ('value1', 'value2'), 'fieldname2' => array('foo'))
176 4
            $filtersByFacetName = array();
177 4
            foreach ($filters as $filter) {
178
                // only split by the first colon to allow using colons in the filter value itself
179 4
                list($filterFacetName, $filterValue) = explode(':', $filter, 2);
180 4
                if (in_array($filterFacetName, $configuredFacets)) {
181 4
                    $filtersByFacetName[$filterFacetName][] = $filterValue;
182 4
                }
183 4
            }
184
185 4
            foreach ($filtersByFacetName as $facetName => $filterValues) {
186 4
                $facetConfiguration = $this->allConfiguredFacets[$facetName . '.'];
187 4
                $filterEncoder = $this->facetRendererFactory->getFacetFilterEncoderByFacetName($facetName);
188
189 4
                $tag = '';
190 4
                if ($facetConfiguration['keepAllOptionsOnSelection'] == 1
191 4
                   || $this->configuration->getSearchFacetingKeepAllFacetsOnSelection()
192 4
                ) {
193 3
                    $tag = '{!tag=' . addslashes($facetConfiguration['field']) . '}';
194 3
                }
195
196 4
                $filterParts = array();
197 4
                foreach ($filterValues as $filterValue) {
198 4
                    if (!is_null($filterEncoder)) {
199
                        $filterOptions = $facetConfiguration[$facetConfiguration['type'] . '.'];
200
                        if (empty($filterOptions)) {
201
                            $filterOptions = array();
202
                        }
203
204
                        $filterValue = $filterEncoder->decodeFilter($filterValue,
205
                            $filterOptions);
206
                        $filterParts[] = $facetConfiguration['field'] . ':' . $filterValue;
207
                    } else {
208 4
                        $filterParts[] = $facetConfiguration['field'] . ':"' . addslashes($filterValue) . '"';
209
                    }
210 4
                }
211
212 4
                $operator = ($facetConfiguration['operator'] == 'OR') ? ' OR ' : ' AND ';
213 4
                $this->facetFilters[] = $tag . '(' . implode($operator,
214 4
                        $filterParts) . ')';
215 4
            }
216 4
        }
217 27
    }
218
219
    /**
220
     * Gets the facets as configured through TypoScript
221
     *
222
     * @return array An array of facet names as specified in TypoScript
223
     */
224 4
    protected function getConfiguredFacets()
225
    {
226 4
        $facets = array();
227
228 4
        foreach ($this->allConfiguredFacets as $facetName => $facetConfiguration) {
229 4
            $facetName = substr($facetName, 0, -1);
230
231 4
            if (empty($facetConfiguration['field'])) {
232
                // TODO later check for query and date, too
233
                continue;
234
            }
235
236 4
            $facets[] = $facetName;
237 4
        }
238
239 4
        return $facets;
240
    }
241
}
242