Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Search
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 11
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 initializeFeatures
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 onPreInit
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 destroySession
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearches
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRowById
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getOwnedRowById
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getScheduledSearches
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchRowsMatchingNormalizedSearch
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
30
 saveSearch
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 expirationCallback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Table Definition for search
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
9 * Copyright (C) The National Library of Finland 2016-2017.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2,
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 *
24 * @category VuFind
25 * @package  Db_Table
26 * @author   Demian Katz <demian.katz@villanova.edu>
27 * @author   Ere Maijala <ere.maijala@helsinki.fi>
28 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
29 * @link     https://vufind.org Main Page
30 */
31
32namespace VuFind\Db\Table;
33
34use Laminas\Db\Adapter\Adapter;
35use Laminas\Db\Adapter\ParameterContainer;
36use Laminas\Db\TableGateway\Feature;
37use VuFind\Db\Row\RowGateway;
38use VuFind\Db\Service\DbServiceAwareInterface;
39use VuFind\Db\Service\DbServiceAwareTrait;
40use VuFind\Db\Service\SearchServiceInterface;
41use VuFind\Search\NormalizedSearch;
42use VuFind\Search\SearchNormalizer;
43
44use function count;
45use function is_object;
46
47/**
48 * Table Definition for search
49 *
50 * @category VuFind
51 * @package  Db_Table
52 * @author   Demian Katz <demian.katz@villanova.edu>
53 * @author   Ere Maijala <ere.maijala@helsinki.fi>
54 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
55 * @link     https://vufind.org Main Site
56 */
57class Search extends Gateway implements DbServiceAwareInterface
58{
59    use DbServiceAwareTrait;
60    use ExpirationTrait;
61
62    /**
63     * Constructor
64     *
65     * @param Adapter       $adapter Database adapter
66     * @param PluginManager $tm      Table manager
67     * @param array         $cfg     Laminas configuration
68     * @param RowGateway    $rowObj  Row prototype object (null for default)
69     * @param string        $table   Name of database table to interface with
70     */
71    public function __construct(
72        Adapter $adapter,
73        PluginManager $tm,
74        $cfg,
75        ?RowGateway $rowObj = null,
76        $table = 'search'
77    ) {
78        parent::__construct($adapter, $tm, $cfg, $rowObj, $table);
79    }
80
81    /**
82     * Initialize features
83     *
84     * @param array $cfg Laminas configuration
85     *
86     * @return void
87     */
88    public function initializeFeatures($cfg)
89    {
90        // Special case for PostgreSQL inserts -- we need to provide an extra
91        // clue so that the database knows how to write bytea data correctly:
92        if ($this->adapter->getDriver()->getDatabasePlatformName() == 'Postgresql') {
93            if (!is_object($this->featureSet)) {
94                $this->featureSet = new Feature\FeatureSet();
95            }
96            $eventFeature = new Feature\EventFeature();
97            $eventFeature->getEventManager()->attach(
98                Feature\EventFeature::EVENT_PRE_INITIALIZE,
99                [$this, 'onPreInit']
100            );
101            $this->featureSet->addFeature($eventFeature);
102        }
103
104        parent::initializeFeatures($cfg);
105    }
106
107    /**
108     * Customize the database object to include extra metadata about the
109     * search_object field so that it will be written correctly. This is
110     * triggered only when we're interacting with PostgreSQL; MySQL works fine
111     * without the extra hint.
112     *
113     * @param object $event Event object
114     *
115     * @return void
116     */
117    public function onPreInit($event)
118    {
119        $driver = $event->getTarget()->getAdapter()->getDriver();
120        $statement = $driver->createStatement();
121        $params = new ParameterContainer();
122        $params->offsetSetErrata('search_object', ParameterContainer::TYPE_LOB);
123        $statement->setParameterContainer($params);
124        $driver->registerStatementPrototype($statement);
125    }
126
127    /**
128     * Destroy unsaved searches belonging to the specified session/user.
129     *
130     * @param string $sid Session ID of current user.
131     * @param int    $uid User ID of current user (optional).
132     *
133     * @return void
134     *
135     * @deprecated Use SessionServiceInterface::destroySession()
136     */
137    public function destroySession($sid, $uid = null)
138    {
139        $this->getDbService(SearchServiceInterface::class)->destroySession($sid, $uid);
140    }
141
142    /**
143     * Get an array of rows for the specified user.
144     *
145     * @param string $sid Session ID of current user.
146     * @param int    $uid User ID of current user (optional).
147     *
148     * @return array      Matching SearchEntry objects.
149     *
150     * @deprecated Use SessionServiceInterface::getSearches()
151     */
152    public function getSearches($sid, $uid = null)
153    {
154        return $this->getDbService(SearchServiceInterface::class)->getSearches($sid, $uid);
155    }
156
157    /**
158     * Get a single row matching a primary key value.
159     *
160     * @param int  $id                 Primary key value
161     * @param bool $exceptionIfMissing Should we throw an exception if the row is
162     * missing?
163     *
164     * @throws \Exception
165     * @return ?\VuFind\Db\Row\Search
166     *
167     * @deprecated
168     */
169    public function getRowById($id, $exceptionIfMissing = true)
170    {
171        $row = $this->select(['id' => $id])->current();
172        if (empty($row) && $exceptionIfMissing) {
173            throw new \Exception('Cannot find id ' . $id);
174        }
175        return $row;
176    }
177
178    /**
179     * Get a single row, enforcing user ownership. Returns row if found, null
180     * otherwise.
181     *
182     * @param int    $id     Primary key value
183     * @param string $sessId Current user session ID
184     * @param int    $userId Current logged-in user ID (or null if none)
185     *
186     * @return ?\VuFind\Db\Row\Search
187     *
188     * @deprecated Use SessionServiceInterface::getSearchByIdAndOwner()
189     */
190    public function getOwnedRowById($id, $sessId, $userId)
191    {
192        return $this->getDbService(SearchServiceInterface::class)->getSearchByIdAndOwner($id, $sessId, $userId);
193    }
194
195    /**
196     * Get scheduled searches.
197     *
198     * @return array Array of VuFind\Db\Row\Search objects.
199     *
200     * @deprecated Use SessionServiceInterface::getScheduledSearches()
201     */
202    public function getScheduledSearches()
203    {
204        return $this->getDbService(SearchServiceInterface::class)->getScheduledSearches();
205    }
206
207    /**
208     * Return existing search table rows matching the provided normalized search.
209     *
210     * @param NormalizedSearch $normalized Normalized search to match against
211     * @param string           $sessionId  Current session ID
212     * @param int|null         $userId     Current user ID
213     * @param int              $limit      Max rows to retrieve
214     * (default = no limit)
215     *
216     * @return \VuFind\Db\Row\Search[]
217     *
218     * @deprecated Use SearchNormalizer::getSearchesMatchingNormalizedSearch()
219     */
220    public function getSearchRowsMatchingNormalizedSearch(
221        NormalizedSearch $normalized,
222        string $sessionId,
223        ?int $userId,
224        int $limit = PHP_INT_MAX
225    ) {
226        // Fetch all rows with the same CRC32 and try to match with the URL
227        $checksum = $normalized->getChecksum();
228        $callback = function ($select) use ($checksum, $sessionId, $userId) {
229            $nest = $select->where
230                ->equalTo('checksum', $checksum)
231                ->and
232                ->nest
233                ->equalTo('session_id', $sessionId)->and->equalTo('saved', 0);
234            if (!empty($userId)) {
235                $nest->or->equalTo('user_id', $userId);
236            }
237        };
238        $results = [];
239        foreach ($this->select($callback) as $match) {
240            $minified = $match->getSearchObjectOrThrowException();
241            if ($normalized->isEquivalentToMinifiedSearch($minified)) {
242                $results[] = $match;
243                if (count($results) >= $limit) {
244                    break;
245                }
246            }
247        }
248        return $results;
249    }
250
251    /**
252     * Add a search into the search table (history)
253     *
254     * @param SearchNormalizer            $normalizer Search manager
255     * @param \VuFind\Search\Base\Results $results    Search to save
256     * @param string                      $sessionId  Current session ID
257     * @param int|null                    $userId     Current user ID
258     *
259     * @return \VuFind\Db\Row\Search
260     *
261     * @deprecated Use SearchNormalizer::saveNormalizedSearch()
262     */
263    public function saveSearch(
264        SearchNormalizer $normalizer,
265        $results,
266        $sessionId,
267        $userId
268    ) {
269        return $normalizer->saveNormalizedSearch($results, $sessionId, $userId);
270    }
271
272    /**
273     * Update the select statement to find records to delete.
274     *
275     * @param Select $select    Select clause
276     * @param string $dateLimit Date threshold of an "expired" record in format
277     * 'Y-m-d H:i:s'.
278     *
279     * @return void
280     */
281    protected function expirationCallback($select, $dateLimit)
282    {
283        $select->where->lessThan('created', $dateLimit)->equalTo('saved', 0);
284    }
285}