Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
78.08% |
57 / 73 |
|
66.67% |
6 / 9 |
CRAP | |
0.00% |
0 / 1 |
Cache | |
78.08% |
57 / 73 |
|
66.67% |
6 / 9 |
37.85 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
createOrUpdate | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
lookup | |
53.85% |
7 / 13 |
|
0.00% |
0 / 1 |
5.57 | |||
lookupBatch | |
66.67% |
14 / 21 |
|
0.00% |
0 / 1 |
5.93 | |||
setContext | |
83.33% |
15 / 18 |
|
0.00% |
0 / 1 |
8.30 | |||
isPrimary | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isFallback | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isCachable | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getVuFindRecord | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | /** |
4 | * Record Cache |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) University of Freiburg 2014. |
9 | * Copyright (C) The National Library of Finland 2015. |
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 Record |
26 | * @author Markus Beh <markus.beh@ub.uni-freiburg.de> |
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 | |
32 | namespace VuFind\Record; |
33 | |
34 | use Laminas\Config\Config as Config; |
35 | use VuFind\Db\Entity\RecordEntityInterface; |
36 | use VuFind\Db\Service\RecordServiceInterface; |
37 | use VuFind\RecordDriver\PluginManager as RecordFactory; |
38 | |
39 | /** |
40 | * Record Cache |
41 | * |
42 | * @category VuFind |
43 | * @package Record |
44 | * @author Markus Beh <markus.beh@ub.uni-freiburg.de> |
45 | * @author Ere Maijala <ere.maijala@helsinki.fi> |
46 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
47 | * @link https://vufind.org Main Site |
48 | */ |
49 | class Cache implements \Laminas\Log\LoggerAwareInterface |
50 | { |
51 | use \VuFind\Log\LoggerAwareTrait; |
52 | |
53 | public const CONTEXT_DISABLED = ''; |
54 | public const CONTEXT_DEFAULT = 'Default'; |
55 | public const CONTEXT_FAVORITE = 'Favorite'; |
56 | |
57 | /** |
58 | * Record sources which may be cached. |
59 | * |
60 | * @var array |
61 | */ |
62 | protected $cachableSources = []; |
63 | |
64 | /** |
65 | * Constructor |
66 | * |
67 | * @param RecordFactory $recordFactoryManager Record driver plugin manager |
68 | * @param Config $cacheConfig RecordCache.ini contents |
69 | * @param RecordServiceInterface $recordService Record database service |
70 | */ |
71 | public function __construct( |
72 | protected RecordFactory $recordFactoryManager, |
73 | protected Config $cacheConfig, |
74 | protected RecordServiceInterface $recordService |
75 | ) { |
76 | $this->setContext(Cache::CONTEXT_DEFAULT); |
77 | } |
78 | |
79 | /** |
80 | * Create a new or update an existing cache entry |
81 | * |
82 | * @param string $recordId Record id |
83 | * @param string $source Source name |
84 | * @param mixed $rawData Raw data from source (must be serializable) |
85 | * |
86 | * @return void |
87 | */ |
88 | public function createOrUpdate($recordId, $source, $rawData) |
89 | { |
90 | if (isset($this->cachableSources[$source])) { |
91 | $this->debug("Updating {$source}|{$recordId}"); |
92 | $this->recordService->updateRecord($recordId, $source, $rawData); |
93 | } |
94 | } |
95 | |
96 | /** |
97 | * Given a record ID, look up a record for that source. |
98 | * |
99 | * @param string $id Record ID |
100 | * @param string $source Record source |
101 | * |
102 | * @return array Array of \VuFind\RecordDriver\AbstractBase |
103 | */ |
104 | public function lookup($id, $source) |
105 | { |
106 | $this->debug("Checking {$source}|{$id}"); |
107 | $record = $this->recordService->getRecord($id, $source); |
108 | $this->debug( |
109 | "Cached record {$source}|{$id} " |
110 | . ($record ? 'found' : 'not found') |
111 | ); |
112 | try { |
113 | return $record ? [$this->getVuFindRecord($record)] : []; |
114 | } catch (\Exception $e) { |
115 | $this->logError( |
116 | 'Could not load record {$source}|{$id} from the record cache: ' |
117 | . $e->getMessage() |
118 | ); |
119 | } |
120 | return []; |
121 | } |
122 | |
123 | /** |
124 | * Given an array of IDs and a record source, look up a batch of records for |
125 | * that source. |
126 | * |
127 | * @param array $ids Record IDs |
128 | * @param string $source Record source |
129 | * |
130 | * @return array Array of \VuFind\RecordDriver\AbstractBase |
131 | */ |
132 | public function lookupBatch($ids, $source) |
133 | { |
134 | if (empty($ids)) { |
135 | return []; |
136 | } |
137 | |
138 | $this->debug("Checking $source batch: " . implode(', ', $ids)); |
139 | $vufindRecords = []; |
140 | $cachedRecords = $this->recordService->getRecords($ids, $source); |
141 | foreach ($cachedRecords as $cachedRecord) { |
142 | try { |
143 | $vufindRecords[] = $this->getVuFindRecord($cachedRecord); |
144 | } catch (\Exception $e) { |
145 | $this->logError( |
146 | 'Could not load record ' . $cachedRecord->getSource() . '|' |
147 | . $cachedRecord->getRecordId() . ' from the record cache: ' |
148 | . $e->getMessage() |
149 | ); |
150 | } |
151 | } |
152 | |
153 | $extractIdCallback = function ($record) { |
154 | return $record->getUniqueID(); |
155 | }; |
156 | $foundIds = array_map($extractIdCallback, $vufindRecords); |
157 | $this->debug( |
158 | "Cached records for $source " |
159 | . ($foundIds ? 'found: ' . implode(', ', $foundIds) : 'not found') |
160 | ); |
161 | |
162 | return $vufindRecords; |
163 | } |
164 | |
165 | /** |
166 | * Set the context for controlling cache behaviour |
167 | * |
168 | * @param string $context Cache context |
169 | * |
170 | * @return void |
171 | */ |
172 | public function setContext($context) |
173 | { |
174 | $this->debug("Setting context to '$context'"); |
175 | if (empty($context)) { |
176 | $this->cachableSources = []; |
177 | return; |
178 | } |
179 | $context = ucfirst($context); |
180 | if (!isset($this->cacheConfig->$context)) { |
181 | $context = Cache::CONTEXT_DEFAULT; |
182 | } |
183 | $this->cachableSources = isset($this->cacheConfig->$context) |
184 | ? $this->cacheConfig->$context->toArray() : []; |
185 | if ( |
186 | $context != Cache::CONTEXT_DEFAULT |
187 | && isset($this->cacheConfig->{Cache::CONTEXT_DEFAULT}) |
188 | ) { |
189 | // Inherit settings from Default section |
190 | $this->cachableSources = array_merge( |
191 | $this->cacheConfig->{Cache::CONTEXT_DEFAULT}->toArray(), |
192 | $this->cachableSources |
193 | ); |
194 | } |
195 | |
196 | foreach ($this->cachableSources as &$cachableSource) { |
197 | if (!isset($cachableSource['operatingMode'])) { |
198 | $cachableSource['operatingMode'] = 'disabled'; |
199 | } |
200 | } |
201 | } |
202 | |
203 | /** |
204 | * Convenience method for checking if cache is used as primary data data source |
205 | * |
206 | * @param string $source Record source |
207 | * |
208 | * @return bool |
209 | */ |
210 | public function isPrimary($source) |
211 | { |
212 | return isset($this->cachableSources[$source]['operatingMode']) |
213 | ? $this->cachableSources[$source]['operatingMode'] === 'primary' |
214 | : false; |
215 | } |
216 | |
217 | /** |
218 | * Convenience method for checking if cache is used as fallback data source |
219 | * |
220 | * @param string $source Record source |
221 | * |
222 | * @return bool |
223 | */ |
224 | public function isFallback($source) |
225 | { |
226 | return isset($this->cachableSources[$source]['operatingMode']) |
227 | ? $this->cachableSources[$source]['operatingMode'] === 'fallback' |
228 | : false; |
229 | } |
230 | |
231 | /** |
232 | * Check whether a record source is cachable |
233 | * |
234 | * @param string $source Record source |
235 | * |
236 | * @return bool |
237 | */ |
238 | public function isCachable($source) |
239 | { |
240 | return isset($this->cachableSources[$source]['operatingMode']) |
241 | ? $this->cachableSources[$source]['operatingMode'] !== 'disabled' |
242 | : false; |
243 | } |
244 | |
245 | /** |
246 | * Helper function to get records from cached source-specific record data |
247 | * |
248 | * @param RecordEntityInterface $cachedRecord Record data |
249 | * |
250 | * @return \VuFind\RecordDriver\AbstractBase |
251 | */ |
252 | protected function getVuFindRecord(RecordEntityInterface $cachedRecord) |
253 | { |
254 | $source = $cachedRecord->getSource(); |
255 | $doc = unserialize($cachedRecord->getData()); |
256 | |
257 | // Solr records are loaded in special-case fashion: |
258 | if ($source === 'VuFind' || $source === 'Solr') { |
259 | $driver = $this->recordFactoryManager->getSolrRecord($doc); |
260 | } else { |
261 | $driver = $this->recordFactoryManager->get($source); |
262 | $driver->setRawData($doc); |
263 | } |
264 | |
265 | $driver->setSourceIdentifiers($source); |
266 | |
267 | return $driver; |
268 | } |
269 | } |