Completed
Pull Request — master (#1319)
by Timo
31:48
created

Faceting::getConfiguredFacets()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.4609

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 11
cp 0.4545
cc 3
eloc 8
nc 3
nop 0
crap 4.4609
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\Domain\Search\ResultSet\Facets\FacetQueryBuilderRegistry;
28
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetRegistry;
29
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetUrlDecoderRegistry;
30
use ApacheSolrForTypo3\Solr\Query;
31
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
32
use ApacheSolrForTypo3\Solr\Util;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
use TYPO3\CMS\Extbase\Object\ObjectManager;
35
36
/**
37
 * Modifies a query to add faceting parameters
38
 *
39
 * @author Ingo Renner <[email protected]>
40
 * @author Daniel Poetzinger <[email protected]>
41
 * @author Sebastian Kurfuerst <[email protected]>
42
 */
43
class Faceting implements Modifier
44
{
45
    /**
46
     * @var \ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration
47
     */
48
    protected $configuration;
49
50
    protected $facetParameters = [];
51
52
    protected $facetFilters = [];
53
54
    /**
55
     * @var array
56
     */
57
    protected $allConfiguredFacets = [];
58
59
    /**
60
     * @var FacetRegistry
61 28
     */
62
    protected $facetRegistry = null;
63 28
64 7
    /**
65
     * @param TypoScriptConfiguration $solrConfiguration
66 21
     * @param FacetRegistry $facetRegistry
67
     */
68
    public function __construct($solrConfiguration = null, FacetRegistry $facetRegistry = null)
69 28
    {
70 28
        if (!is_null($solrConfiguration)) {
71 28
            $this->configuration = $solrConfiguration;
72 28
        } else {
73
            $this->configuration = Util::getSolrConfiguration();
74
        }
75
76
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
77
        $this->facetRegistry = is_null($facetRegistry) ? $objectManager->get(FacetRegistry::class) : $facetRegistry;
78
79
        $this->allConfiguredFacets = $this->configuration->getSearchFacetingFacets();
80
    }
81 28
82
    /**
83 28
     * Modifies the given query and adds the parameters necessary for faceted
84 28
     * search.
85 28
     *
86
     * @param Query $query The query to modify
87 28
     * @return Query The modified query with faceting parameters
88 28
     */
89
    public function modifyQuery(Query $query)
90
    {
91 28
        $query->setFaceting();
92 3
        $this->buildFacetingParameters();
93
        $this->addFacetQueryFilters();
94
95 28
        foreach ($this->facetParameters as $facetParameter => $value) {
96
            $query->addQueryParameter($facetParameter, $value);
97
        }
98
99
        foreach ($this->facetFilters as $filter) {
100
            $query->addFilter($filter);
101
        }
102
103 28
        return $query;
104
    }
105 28
106 28
    /**
107
     * Delegates the parameter building to specialized functions depending on
108 28
     * the type of facet to add.
109 28
     *
110 21
     */
111 21
    protected function buildFacetingParameters()
112
    {
113 28
        foreach ($this->allConfiguredFacets as $facetName => $facetConfiguration) {
114
            $facetName = substr($facetName, 0, -1);
115
            $type = isset($facetConfiguration['type']) ? $facetConfiguration['type'] : 'options';
116 28
            $facetParameterBuilder = $this->facetRegistry->getPackage($type)->getQueryBuilder();
117 28
118
            if (is_null($facetParameterBuilder)) {
119 28
                throw new \InvalidArgumentException('No query build configured for facet ' . htmlspecialchars($facetName));
120
            }
121
122
            $facetParameters = $facetParameterBuilder->build($facetName, $this->configuration);
123
            $this->facetParameters = array_merge_recursive($this->facetParameters, $facetParameters);
124
        }
125
    }
126
127 28
    /**
128
     * Adds filters specified through HTTP GET as filter query parameters to
129 28
     * the Solr query.
130
     *
131
     */
132 28
    protected function addFacetQueryFilters()
133 2
    {
134 2
        // todo refactor to use a request object
135 2
        $resultParameters = GeneralUtility::_GET('tx_solr');
136
137
        // format for filter URL parameter:
138 2
        // tx_solr[filter]=$facetName0:$facetValue0,$facetName1:$facetValue1,$facetName2:$facetValue2
139 2
        if (is_array($resultParameters['filter'])) {
140 2
            $filters = array_map('urldecode', $resultParameters['filter']);
141 26
            // $filters look like ['name:value1','name:value2','fieldname2:foo']
142 23
            $configuredFacets = $this->getConfiguredFacets();
143 23
            // first group the filters by facetName - so that we can
144 23
            // decide later whether we need to do AND or OR for multiple
145
            // filters for a certain facet/field
146 26
            // $filtersByFacetName look like ['name' =>  ['value1', 'value2'], 'fieldname2' => ['foo']]
147
            $filtersByFacetName = [];
148
            foreach ($filters as $filter) {
149 28
                // only split by the first colon to allow using colons in the filter value itself
150 28
                list($filterFacetName, $filterValue) = explode(':', $filter, 2);
151 2
                if (in_array($filterFacetName, $configuredFacets)) {
152
                    $filtersByFacetName[$filterFacetName][] = $filterValue;
153
                }
154 28
            }
155
156
            foreach ($filtersByFacetName as $facetName => $filterValues) {
157
                $facetConfiguration = $this->allConfiguredFacets[$facetName . '.'];
158
                $type = isset($facetConfiguration['type']) ? $facetConfiguration['type'] : 'options';
159
                $filterEncoder = $this->facetRegistry->getPackage($type)->getUrlDecoder();
160
161
                if (is_null($filterEncoder)) {
162 28
                    throw new \InvalidArgumentException('No encoder configured for facet ' . htmlspecialchars($facetName));
163
                }
164
165 28
                $tag = '';
166
                if ($facetConfiguration['keepAllOptionsOnSelection'] == 1 || $this->configuration->getSearchFacetingKeepAllFacetsOnSelection()) {
167
                    $tag = '{!tag=' . addslashes($facetConfiguration['field']) . '}';
168
                }
169 28
170 3
                $filterParts = [];
171
                foreach ($filterValues as $filterValue) {
172 3
                    $filterOptions = $facetConfiguration[$facetConfiguration['type'] . '.'];
173
                    if (empty($filterOptions)) {
174
                        $filterOptions = [];
175
                    }
176
177 3
                    $filterValue = $filterEncoder->decode($filterValue, $filterOptions);
178 3
                    $filterParts[] = $facetConfiguration['field'] . ':' . $filterValue;
179
                }
180 3
181 3
                $operator = ($facetConfiguration['operator'] == 'OR') ? ' OR ' : ' AND ';
182 3
                $this->facetFilters[] = $tag . '(' . implode($operator, $filterParts) . ')';
183
            }
184
        }
185
    }
186 3
187 3
    /**
188 3
     * Gets the facets as configured through TypoScript
189
     *
190 3
     * @return array An array of facet names as specified in TypoScript
191 3
     */
192 3
    protected function getConfiguredFacets()
193
    {
194 2
        $facets = [];
195
196
        foreach ($this->allConfiguredFacets as $facetName => $facetConfiguration) {
197 3
            $facetName = substr($facetName, 0, -1);
198 3
199 3
            if (empty($facetConfiguration['field'])) {
200
                // TODO later check for query and date, too
201
                continue;
202
            }
203
204
            $facets[] = $facetName;
205
        }
206
207
        return $facets;
208
    }
209
}
210