Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
CustomFilterListener
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
3 / 3
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 attach
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 onSearchPre
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2
3/**
4 * Solr custom filter listener.
5 *
6 * This can translate a simple filter into a complex set of filters, and it can
7 * "invert" filters by applying Solr filters only when a VuFind filter is absent.
8 *
9 * PHP version 8
10 *
11 * Copyright (C) Villanova University 2022.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2,
15 * as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
25 *
26 * @category VuFind
27 * @package  Search
28 * @author   Demian Katz <demian.katz@villanova.edu>
29 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
30 * @link     https://vufind.org Main Site
31 */
32
33namespace VuFind\Search\Solr;
34
35use Laminas\EventManager\EventInterface;
36use Laminas\EventManager\SharedEventManagerInterface;
37use VuFindSearch\Backend\BackendInterface;
38use VuFindSearch\Service;
39
40/**
41 * Solr custom filter listener.
42 *
43 * @category VuFind
44 * @package  Search
45 * @author   Demian Katz <demian.katz@villanova.edu>
46 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
47 * @link     https://vufind.org Main Site
48 */
49class CustomFilterListener
50{
51    /**
52     * Backend.
53     *
54     * @var BackendInterface
55     */
56    protected $backend;
57
58    /**
59     * Normal filters
60     *
61     * @var array
62     */
63    protected $normalFilters;
64
65    /**
66     * Inverted filters
67     *
68     * @var array
69     */
70    protected $invertedFilters;
71
72    /**
73     * Name of parameter used to store filters
74     *
75     * @var string
76     */
77    protected $filterParam = 'fq';
78
79    /**
80     * Constructor.
81     *
82     * @param BackendInterface $backend  Backend
83     * @param array            $normal   Normal custom filters (placeholder => full
84     * filter)
85     * @param array            $inverted Inverted custom filters (applied unless set)
86     *
87     * @return void
88     */
89    public function __construct(
90        BackendInterface $backend,
91        array $normal,
92        array $inverted
93    ) {
94        $this->backend = $backend;
95        $this->normalFilters = $normal;
96        $this->invertedFilters = $inverted;
97    }
98
99    /**
100     * Attach listener to shared event manager.
101     *
102     * @param SharedEventManagerInterface $manager Shared event manager
103     *
104     * @return void
105     */
106    public function attach(SharedEventManagerInterface $manager)
107    {
108        $manager->attach(
109            Service::class,
110            Service::EVENT_PRE,
111            [$this, 'onSearchPre']
112        );
113    }
114
115    /**
116     * Apply/translate custom filters.
117     *
118     * @param EventInterface $event Event
119     *
120     * @return EventInterface
121     */
122    public function onSearchPre(EventInterface $event)
123    {
124        $command = $event->getParam('command');
125        if (
126            $command->getContext() === 'search'
127            && $command->getTargetIdentifier() === $this->backend->getIdentifier()
128            && ($params = $command->getSearchParameters())
129        ) {
130            $invertedFiltersMatched = [];
131            $finalFilters = [];
132            foreach ($params->get($this->filterParam) ?? [] as $filter) {
133                if (isset($this->invertedFilters[$filter])) {
134                    // Make note of matched inverted filters for later:
135                    $invertedFiltersMatched[$filter] = true;
136                } elseif (isset($this->normalFilters[$filter])) {
137                    // Translate normal custom filters:
138                    $finalFilters[] = $this->normalFilters[$filter];
139                } else {
140                    // Keep all unmatched filters:
141                    $finalFilters[] = $filter;
142                }
143            }
144            // Now apply any inverted filters that were not matched above:
145            foreach ($this->invertedFilters as $placeholder => $result) {
146                if (!($invertedFiltersMatched[$placeholder] ?? false)) {
147                    $finalFilters[] = $result;
148                }
149            }
150            $params->set($this->filterParam, $finalFilters);
151        }
152        return $event;
153    }
154}