Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
60 / 60
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
Backend
100.00% covered (success)
100.00%
60 / 60
100.00% covered (success)
100.00%
9 / 9
22
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
2
 search
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
4
 retrieve
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
4
 setQueryBuilder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQueryBuilder
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getRecordCollectionFactory
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getConnector
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createRecordCollection
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 paramBagToPrimoQuery
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3/**
4 * Primo backend.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Search
25 * @author   David Maus <maus@hab.de>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org
28 */
29
30namespace VuFindSearch\Backend\Primo;
31
32use VuFindSearch\Backend\AbstractBackend;
33use VuFindSearch\Backend\Exception\BackendException;
34use VuFindSearch\ParamBag;
35use VuFindSearch\Query\AbstractQuery;
36use VuFindSearch\Response\RecordCollectionFactoryInterface;
37use VuFindSearch\Response\RecordCollectionInterface;
38
39use function in_array;
40
41/**
42 * Primo Central backend.
43 *
44 * @category VuFind
45 * @package  Search
46 * @author   David Maus <maus@hab.de>
47 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
48 * @link     https://vufind.org
49 */
50class Backend extends AbstractBackend
51{
52    /**
53     * Connector.
54     *
55     * @var ConnectorInterface
56     */
57    protected $connector;
58
59    /**
60     * Query builder.
61     *
62     * @var QueryBuilder
63     */
64    protected $queryBuilder = null;
65
66    /**
67     * Constructor.
68     *
69     * @param ConnectorInterface               $connector Primo connector
70     * @param RecordCollectionFactoryInterface $factory   Record collection factory
71     * (null for default)
72     *
73     * @return void
74     */
75    public function __construct(
76        ConnectorInterface $connector,
77        RecordCollectionFactoryInterface $factory = null
78    ) {
79        if (null !== $factory) {
80            $this->setRecordCollectionFactory($factory);
81        }
82        $this->connector    = $connector;
83    }
84
85    /**
86     * Perform a search and return record collection.
87     *
88     * @param AbstractQuery $query  Search query
89     * @param int           $offset Search offset
90     * @param int           $limit  Search limit
91     * @param ParamBag      $params Search backend parameters
92     *
93     * @return RecordCollectionInterface
94     */
95    public function search(
96        AbstractQuery $query,
97        $offset,
98        $limit,
99        ParamBag $params = null
100    ) {
101        $baseParams = $this->getQueryBuilder()->build($query);
102        if (null !== $params) {
103            $baseParams->mergeWith($params);
104        }
105        $baseParams->set('limit', $limit);
106        $page = $limit > 0 ? floor($offset / $limit) + 1 : 1;
107        $baseParams->set('pageNumber', $page);
108
109        $primoQuery = $this->paramBagToPrimoQuery($baseParams);
110        try {
111            $response = $this->connector->query(
112                $this->connector->getInstitutionCode(),
113                $primoQuery['query'],
114                $primoQuery
115            );
116        } catch (\Exception $e) {
117            throw new BackendException(
118                $e->getMessage(),
119                $e->getCode(),
120                $e
121            );
122        }
123        $collection = $this->createRecordCollection($response);
124        $this->injectSourceIdentifier($collection);
125
126        return $collection;
127    }
128
129    /**
130     * Retrieve a single document.
131     *
132     * @param string   $id     Document identifier
133     * @param ParamBag $params Search backend parameters
134     *
135     * @return RecordCollectionInterface
136     */
137    public function retrieve($id, ParamBag $params = null)
138    {
139        $onCampus = (null !== $params) ? $params->get('onCampus') : [false];
140        $onCampus = $onCampus ? $onCampus[0] : false;
141        try {
142            $response   = $this->connector
143                ->getRecord(
144                    $id,
145                    $this->connector->getInstitutionCode(),
146                    $onCampus
147                );
148        } catch (\Exception $e) {
149            throw new BackendException(
150                $e->getMessage(),
151                $e->getCode(),
152                $e
153            );
154        }
155        $collection = $this->createRecordCollection($response);
156        $this->injectSourceIdentifier($collection);
157        return $collection;
158    }
159
160    /**
161     * Set the query builder.
162     *
163     * @param QueryBuilder $queryBuilder Query builder
164     *
165     * @return void
166     */
167    public function setQueryBuilder(QueryBuilder $queryBuilder)
168    {
169        $this->queryBuilder = $queryBuilder;
170    }
171
172    /**
173     * Return query builder.
174     *
175     * Lazy loads an empty QueryBuilder if none was set.
176     *
177     * @return QueryBuilder
178     */
179    public function getQueryBuilder()
180    {
181        if (!$this->queryBuilder) {
182            $this->queryBuilder = new QueryBuilder();
183        }
184        return $this->queryBuilder;
185    }
186
187    /**
188     * Return the record collection factory.
189     *
190     * Lazy loads a generic collection factory.
191     *
192     * @return RecordCollectionFactoryInterface
193     */
194    public function getRecordCollectionFactory()
195    {
196        if ($this->collectionFactory === null) {
197            $this->collectionFactory = new Response\RecordCollectionFactory();
198        }
199        return $this->collectionFactory;
200    }
201
202    /**
203     * Return the Primo connector.
204     *
205     * @return Connector
206     */
207    public function getConnector()
208    {
209        return $this->connector;
210    }
211
212    /// Internal API
213
214    /**
215     * Create record collection.
216     *
217     * @param array $records Records to process
218     *
219     * @return RecordCollectionInterface
220     */
221    protected function createRecordCollection($records)
222    {
223        return $this->getRecordCollectionFactory()->factory($records);
224    }
225
226    /**
227     * Convert a ParamBag to a Primo query array.
228     *
229     * @param ParamBag $params ParamBag to convert
230     *
231     * @return array
232     */
233    protected function paramBagToPrimoQuery(ParamBag $params)
234    {
235        $params = $params->getArrayCopy();
236
237        // Convert the options:
238        $options = [];
239
240        // Most parameters need to be flattened from array format, but a few
241        // should remain as arrays:
242        $arraySettings = [
243            'query', 'facets', 'filterList', 'groupFilters', 'rangeFilters',
244        ];
245        foreach ($params as $key => $param) {
246            $options[$key] = in_array($key, $arraySettings) ? $param : $param[0];
247        }
248
249        // Use special pcAvailability filter if it has been set:
250        foreach ($options['filterList'] ?? [] as $i => $filter) {
251            if ('pcAvailability' === $filter['field']) {
252                $value = reset($filter['values']);
253                // Note that '' is treated as true for the simple case with no value
254                $options['pcAvailability'] = !in_array($value, [false, 0, '0', 'false'], true);
255                unset($options['filterList'][$i]);
256                break;
257            }
258        }
259
260        return $options;
261    }
262}