Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
56.52% |
39 / 69 |
|
50.00% |
5 / 10 |
CRAP | |
0.00% |
0 / 1 |
ChangeTracker | |
56.52% |
39 / 69 |
|
50.00% |
5 / 10 |
57.25 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
retrieve | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRetrieveDeletedCallback | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
retrieveDeletedCount | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
retrieveDeleted | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
retrieveOrCreate | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
markDeleted | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
getUtcDate | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
strToUtcTime | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
index | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
6.01 |
1 | <?php |
2 | |
3 | /** |
4 | * Table Definition for change_tracker |
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 Db_Table |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org Main Site |
28 | */ |
29 | |
30 | namespace VuFind\Db\Table; |
31 | |
32 | use Laminas\Db\Adapter\Adapter; |
33 | use Laminas\Db\Sql\Expression; |
34 | use VuFind\Db\Row\RowGateway; |
35 | |
36 | /** |
37 | * Table Definition for change_tracker |
38 | * |
39 | * @category VuFind |
40 | * @package Db_Table |
41 | * @author Demian Katz <demian.katz@villanova.edu> |
42 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
43 | * @link https://vufind.org Main Site |
44 | */ |
45 | class ChangeTracker extends Gateway |
46 | { |
47 | /** |
48 | * Date/time format for database |
49 | * |
50 | * @var string |
51 | */ |
52 | protected $dateFormat = 'Y-m-d H:i:s'; |
53 | |
54 | /** |
55 | * Constructor |
56 | * |
57 | * @param Adapter $adapter Database adapter |
58 | * @param PluginManager $tm Table manager |
59 | * @param array $cfg Laminas configuration |
60 | * @param RowGateway $rowObj Row prototype object (null for default) |
61 | * @param string $table Name of database table to interface with |
62 | */ |
63 | public function __construct( |
64 | Adapter $adapter, |
65 | PluginManager $tm, |
66 | $cfg, |
67 | ?RowGateway $rowObj = null, |
68 | $table = 'change_tracker' |
69 | ) { |
70 | parent::__construct($adapter, $tm, $cfg, $rowObj, $table); |
71 | } |
72 | |
73 | /** |
74 | * Retrieve a row from the database based on primary key; return null if it |
75 | * is not found. |
76 | * |
77 | * @param string $core The Solr core holding the record. |
78 | * @param string $id The ID of the record being indexed. |
79 | * |
80 | * @return ?\VuFind\Db\Row\ChangeTracker |
81 | */ |
82 | public function retrieve($core, $id) |
83 | { |
84 | return $this->select(['core' => $core, 'id' => $id])->current(); |
85 | } |
86 | |
87 | /** |
88 | * Build a callback function for use by the retrieveDeleted* methods. |
89 | * |
90 | * @param string $core The Solr core holding the record. |
91 | * @param string $from The beginning date of the range to search. |
92 | * @param string $until The end date of the range to search. |
93 | * @param int $offset Record number to retrieve first. |
94 | * @param int $limit Retrieval limit (null for no limit) |
95 | * @param array $columns Columns to retrieve (null for all) |
96 | * @param string $order Sort order |
97 | * |
98 | * @return callable |
99 | */ |
100 | public function getRetrieveDeletedCallback( |
101 | $core, |
102 | $from, |
103 | $until, |
104 | $offset = 0, |
105 | $limit = null, |
106 | $columns = null, |
107 | $order = null |
108 | ) { |
109 | return function ($select) use ( |
110 | $core, |
111 | $from, |
112 | $until, |
113 | $offset, |
114 | $limit, |
115 | $columns, |
116 | $order |
117 | ) { |
118 | if ($columns !== null) { |
119 | $select->columns($columns); |
120 | } |
121 | $select->where |
122 | ->equalTo('core', $core) |
123 | ->greaterThanOrEqualTo('deleted', $from) |
124 | ->lessThanOrEqualTo('deleted', $until); |
125 | if ($order !== null) { |
126 | $select->order($order); |
127 | } |
128 | if ($offset > 0) { |
129 | $select->offset($offset); |
130 | } |
131 | if ($limit !== null) { |
132 | $select->limit($limit); |
133 | } |
134 | }; |
135 | } |
136 | |
137 | /** |
138 | * Retrieve a set of deleted rows from the database. |
139 | * |
140 | * @param string $core The Solr core holding the record. |
141 | * @param string $from The beginning date of the range to search. |
142 | * @param string $until The end date of the range to search. |
143 | * |
144 | * @return \Laminas\Db\ResultSet\AbstractResultSet |
145 | */ |
146 | public function retrieveDeletedCount($core, $from, $until) |
147 | { |
148 | $columns = ['count' => new Expression('COUNT(*)')]; |
149 | $callback = $this |
150 | ->getRetrieveDeletedCallback($core, $from, $until, 0, null, $columns); |
151 | $select = $this->sql->select(); |
152 | $callback($select); |
153 | $statement = $this->sql->prepareStatementForSqlObject($select); |
154 | $result = $statement->execute(); |
155 | return ((array)$result->current())['count']; |
156 | } |
157 | |
158 | /** |
159 | * Retrieve a set of deleted rows from the database. |
160 | * |
161 | * @param string $core The Solr core holding the record. |
162 | * @param string $from The beginning date of the range to search. |
163 | * @param string $until The end date of the range to search. |
164 | * @param int $offset Record number to retrieve first. |
165 | * @param int $limit Retrieval limit (null for no limit) |
166 | * |
167 | * @return \Laminas\Db\ResultSet\AbstractResultSet |
168 | */ |
169 | public function retrieveDeleted( |
170 | $core, |
171 | $from, |
172 | $until, |
173 | $offset = 0, |
174 | $limit = null |
175 | ) { |
176 | $callback = $this->getRetrieveDeletedCallback( |
177 | $core, |
178 | $from, |
179 | $until, |
180 | $offset, |
181 | $limit, |
182 | null, |
183 | 'deleted' |
184 | ); |
185 | return $this->select($callback); |
186 | } |
187 | |
188 | /** |
189 | * Retrieve a row from the database based on primary key; create a new |
190 | * row if no existing match is found. |
191 | * |
192 | * @param string $core The Solr core holding the record. |
193 | * @param string $id The ID of the record being indexed. |
194 | * |
195 | * @return \VuFind\Db\Row\ChangeTracker |
196 | */ |
197 | public function retrieveOrCreate($core, $id) |
198 | { |
199 | $row = $this->retrieve($core, $id); |
200 | if (empty($row)) { |
201 | $row = $this->createRow(); |
202 | $row->core = $core; |
203 | $row->id = $id; |
204 | $row->first_indexed = $row->last_indexed = $this->getUtcDate(); |
205 | } |
206 | return $row; |
207 | } |
208 | |
209 | /** |
210 | * Update the change tracker table to indicate that a record has been deleted. |
211 | * |
212 | * The method returns the updated/created row when complete. |
213 | * |
214 | * @param string $core The Solr core holding the record. |
215 | * @param string $id The ID of the record being indexed. |
216 | * |
217 | * @return \VuFind\Db\Row\ChangeTracker |
218 | */ |
219 | public function markDeleted($core, $id) |
220 | { |
221 | // Get a row matching the specified details: |
222 | $row = $this->retrieveOrCreate($core, $id); |
223 | |
224 | // If the record is already deleted, we don't need to do anything! |
225 | if (!empty($row->deleted)) { |
226 | return $row; |
227 | } |
228 | |
229 | // Save new value to the object: |
230 | $row->deleted = $this->getUtcDate(); |
231 | $row->save(); |
232 | return $row; |
233 | } |
234 | |
235 | /** |
236 | * Get a UTC time. |
237 | * |
238 | * @param int $ts Timestamp (null for current) |
239 | * |
240 | * @return string |
241 | */ |
242 | protected function getUtcDate($ts = null) |
243 | { |
244 | $oldTz = date_default_timezone_get(); |
245 | date_default_timezone_set('UTC'); |
246 | $date = date($this->dateFormat, $ts ?? time()); |
247 | date_default_timezone_set($oldTz); |
248 | return $date; |
249 | } |
250 | |
251 | /** |
252 | * Convert a string to time in UTC. |
253 | * |
254 | * @param string $str String to parse |
255 | * |
256 | * @return int |
257 | */ |
258 | protected function strToUtcTime($str) |
259 | { |
260 | $oldTz = date_default_timezone_get(); |
261 | date_default_timezone_set('UTC'); |
262 | $time = strtotime($str); |
263 | date_default_timezone_set($oldTz); |
264 | return $time; |
265 | } |
266 | |
267 | /** |
268 | * Update the change_tracker table to reflect that a record has been indexed. |
269 | * We need to know the date of the last change to the record (independent of |
270 | * its addition to the index) in order to tell the difference between a |
271 | * reindex of a previously-encountered record and a genuine change. |
272 | * |
273 | * The method returns the updated/created row when complete. |
274 | * |
275 | * @param string $core The Solr core holding the record. |
276 | * @param string $id The ID of the record being indexed. |
277 | * @param int $change The timestamp of the last record change. |
278 | * |
279 | * @return \VuFind\Db\Row\ChangeTracker |
280 | */ |
281 | public function index($core, $id, $change) |
282 | { |
283 | // Get a row matching the specified details: |
284 | $row = $this->retrieveOrCreate($core, $id); |
285 | |
286 | // Flag to indicate whether we need to save the contents of $row: |
287 | $saveNeeded = false; |
288 | |
289 | // Make sure there is a change date in the row (this will be empty |
290 | // if we just created a new row): |
291 | if (empty($row->last_record_change)) { |
292 | $row->last_record_change = $this->getUtcDate($change); |
293 | $saveNeeded = true; |
294 | } |
295 | |
296 | // Are we restoring a previously deleted record, or was the stored |
297 | // record change date before current record change date? Either way, |
298 | // we need to update the table! |
299 | if ( |
300 | !empty($row->deleted) |
301 | || $this->strToUtcTime($row->last_record_change) < $change |
302 | ) { |
303 | // Save new values to the object: |
304 | $row->last_indexed = $this->getUtcDate(); |
305 | $row->last_record_change = $this->getUtcDate($change); |
306 | |
307 | // If first indexed is null, we're restoring a deleted record, so |
308 | // we need to treat it as new -- we'll use the current time. |
309 | if (empty($row->first_indexed)) { |
310 | $row->first_indexed = $row->last_indexed; |
311 | } |
312 | |
313 | // Make sure the record is "undeleted" if necessary: |
314 | $row->deleted = null; |
315 | |
316 | $saveNeeded = true; |
317 | } |
318 | |
319 | // Save the row if changes were made: |
320 | if ($saveNeeded) { |
321 | $row->save(); |
322 | } |
323 | |
324 | // Send back the row: |
325 | return $row; |
326 | } |
327 | } |