Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
1.64% |
1 / 61 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
UserResource | |
1.64% |
1 / 61 |
|
14.29% |
1 / 7 |
259.62 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSavedData | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
createOrUpdateLink | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
destroyLinks | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
getStatistics | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
2 | |||
getDuplicates | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
deduplicate | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | /** |
4 | * Table Definition for user_resource |
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 Page |
28 | */ |
29 | |
30 | namespace VuFind\Db\Table; |
31 | |
32 | use Laminas\Db\Adapter\Adapter; |
33 | use Laminas\Db\Sql\Expression; |
34 | use Laminas\Db\Sql\Select; |
35 | use VuFind\Db\Row\RowGateway; |
36 | use VuFind\Db\Service\DbServiceAwareInterface; |
37 | use VuFind\Db\Service\DbServiceAwareTrait; |
38 | use VuFind\Db\Service\ResourceTagsServiceInterface; |
39 | use VuFind\Db\Service\UserResourceServiceInterface; |
40 | |
41 | /** |
42 | * Table Definition for user_resource |
43 | * |
44 | * @category VuFind |
45 | * @package Db_Table |
46 | * @author Demian Katz <demian.katz@villanova.edu> |
47 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
48 | * @link https://vufind.org Main Page |
49 | */ |
50 | class UserResource extends Gateway implements DbServiceAwareInterface |
51 | { |
52 | use DbServiceAwareTrait; |
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 = 'user_resource' |
69 | ) { |
70 | parent::__construct($adapter, $tm, $cfg, $rowObj, $table); |
71 | } |
72 | |
73 | /** |
74 | * Get information saved in a user's favorites for a particular record. |
75 | * |
76 | * @param string $resourceId ID of record being checked. |
77 | * @param string $source Source of record to look up |
78 | * @param int $listId Optional list ID (to limit results to a particular |
79 | * list). |
80 | * @param int $userId Optional user ID (to limit results to a particular |
81 | * user). |
82 | * |
83 | * @return \Laminas\Db\ResultSet\AbstractResultSet |
84 | */ |
85 | public function getSavedData( |
86 | $resourceId, |
87 | $source = DEFAULT_SEARCH_BACKEND, |
88 | $listId = null, |
89 | $userId = null |
90 | ) { |
91 | $callback = function ($select) use ($resourceId, $source, $listId, $userId) { |
92 | $select->columns( |
93 | [ |
94 | new Expression( |
95 | 'DISTINCT(?)', |
96 | ['user_resource.id'], |
97 | [Expression::TYPE_IDENTIFIER] |
98 | ), Select::SQL_STAR, |
99 | ] |
100 | ); |
101 | $select->join( |
102 | ['r' => 'resource'], |
103 | 'r.id = user_resource.resource_id', |
104 | [] |
105 | ); |
106 | $select->join( |
107 | ['ul' => 'user_list'], |
108 | 'user_resource.list_id = ul.id', |
109 | ['list_title' => 'title', 'list_id' => 'id'] |
110 | ); |
111 | $select->where->equalTo('r.source', $source) |
112 | ->equalTo('r.record_id', $resourceId); |
113 | |
114 | if (null !== $userId) { |
115 | $select->where->equalTo('user_resource.user_id', $userId); |
116 | } |
117 | if (null !== $listId) { |
118 | $select->where->equalTo('user_resource.list_id', $listId); |
119 | } |
120 | }; |
121 | return $this->select($callback); |
122 | } |
123 | |
124 | /** |
125 | * Create link if one does not exist; update notes if one does. |
126 | * |
127 | * @param string $resource_id ID of resource to link up |
128 | * @param string $user_id ID of user creating link |
129 | * @param string $list_id ID of list to link up |
130 | * @param string $notes Notes to associate with link |
131 | * |
132 | * @return \VuFind\Db\Row\UserResource |
133 | * |
134 | * @deprecated Use UserResourceServiceInterface::createOrUpdateLink() |
135 | */ |
136 | public function createOrUpdateLink( |
137 | $resource_id, |
138 | $user_id, |
139 | $list_id, |
140 | $notes = '' |
141 | ) { |
142 | return $this->getDbService(UserResourceServiceInterface::class) |
143 | ->createOrUpdateLink($resource_id, $user_id, $list_id, $notes); |
144 | } |
145 | |
146 | /** |
147 | * Unlink rows for the specified resource. This will also automatically remove |
148 | * any tags associated with the relationship. |
149 | * |
150 | * @param string|array $resource_id ID (or array of IDs) of resource(s) to |
151 | * unlink (null for ALL matching resources) |
152 | * @param string $user_id ID of user removing links |
153 | * @param string $list_id ID of list to unlink |
154 | * (null for ALL matching lists, with the destruction of all tags associated |
155 | * with the $resource_id value; true for ALL matching lists, but retaining |
156 | * any tags associated with the $resource_id independently of lists) |
157 | * |
158 | * @return void |
159 | * |
160 | * @deprecated |
161 | */ |
162 | public function destroyLinks($resource_id, $user_id, $list_id = null) |
163 | { |
164 | // Remove any tags associated with the links we are removing; we don't |
165 | // want to leave orphaned tags in the resource_tags table after we have |
166 | // cleared out favorites in user_resource! |
167 | $resourceTagsService = $this->getDbService(ResourceTagsServiceInterface::class); |
168 | if ($list_id === true) { |
169 | $resourceTagsService->destroyAllListResourceTagsLinksForUser($resource_id, $user_id); |
170 | } else { |
171 | $resourceTagsService->destroyResourceTagsLinksForUser($resource_id, $user_id, $list_id); |
172 | } |
173 | |
174 | // Now build the where clause to figure out which rows to remove: |
175 | $callback = function ($select) use ($resource_id, $user_id, $list_id) { |
176 | $select->where->equalTo('user_id', $user_id); |
177 | if (null !== $resource_id) { |
178 | $select->where->in('resource_id', (array)$resource_id); |
179 | } |
180 | // null or true values of $list_id have different meanings in the |
181 | // context of the destroyResourceTagsLinksForUser() call above, since |
182 | // some tags have a null $list_id value. In the case of user_resource |
183 | // rows, however, every row has a non-null $list_id value, so the |
184 | // two cases are equivalent and may be handled identically. |
185 | if (null !== $list_id && true !== $list_id) { |
186 | $select->where->equalTo('list_id', $list_id); |
187 | } |
188 | }; |
189 | |
190 | // Delete the rows: |
191 | $this->delete($callback); |
192 | } |
193 | |
194 | /** |
195 | * Get statistics on use of lists. |
196 | * |
197 | * @return array |
198 | */ |
199 | public function getStatistics() |
200 | { |
201 | $select = $this->sql->select(); |
202 | $select->columns( |
203 | [ |
204 | 'users' => new Expression( |
205 | 'COUNT(DISTINCT(?))', |
206 | ['user_id'], |
207 | [Expression::TYPE_IDENTIFIER] |
208 | ), |
209 | 'lists' => new Expression( |
210 | 'COUNT(DISTINCT(?))', |
211 | ['list_id'], |
212 | [Expression::TYPE_IDENTIFIER] |
213 | ), |
214 | 'resources' => new Expression( |
215 | 'COUNT(DISTINCT(?))', |
216 | ['resource_id'], |
217 | [Expression::TYPE_IDENTIFIER] |
218 | ), |
219 | 'total' => new Expression('COUNT(*)'), |
220 | ] |
221 | ); |
222 | $statement = $this->sql->prepareStatementForSqlObject($select); |
223 | $result = $statement->execute(); |
224 | return (array)$result->current(); |
225 | } |
226 | |
227 | /** |
228 | * Get a list of duplicate rows (this sometimes happens after merging IDs, |
229 | * for example after a Summon resource ID changes). |
230 | * |
231 | * @return mixed |
232 | */ |
233 | public function getDuplicates() |
234 | { |
235 | $callback = function ($select) { |
236 | $select->columns( |
237 | [ |
238 | 'resource_id' => new Expression( |
239 | 'MIN(?)', |
240 | ['resource_id'], |
241 | [Expression::TYPE_IDENTIFIER] |
242 | ), |
243 | 'list_id' => new Expression( |
244 | 'MIN(?)', |
245 | ['list_id'], |
246 | [Expression::TYPE_IDENTIFIER] |
247 | ), |
248 | 'user_id' => new Expression( |
249 | 'MIN(?)', |
250 | ['user_id'], |
251 | [Expression::TYPE_IDENTIFIER] |
252 | ), |
253 | 'cnt' => new Expression( |
254 | 'COUNT(?)', |
255 | ['resource_id'], |
256 | [Expression::TYPE_IDENTIFIER] |
257 | ), |
258 | 'id' => new Expression( |
259 | 'MIN(?)', |
260 | ['id'], |
261 | [Expression::TYPE_IDENTIFIER] |
262 | ), |
263 | ] |
264 | ); |
265 | $select->group(['resource_id', 'list_id', 'user_id']); |
266 | $select->having('COUNT(resource_id) > 1'); |
267 | }; |
268 | return $this->select($callback); |
269 | } |
270 | |
271 | /** |
272 | * Deduplicate rows (sometimes necessary after merging foreign key IDs). |
273 | * |
274 | * @return void |
275 | */ |
276 | public function deduplicate() |
277 | { |
278 | foreach ($this->getDuplicates() as $dupe) { |
279 | // Do this as a transaction to prevent odd behavior: |
280 | $connection = $this->getAdapter()->getDriver()->getConnection(); |
281 | $connection->beginTransaction(); |
282 | |
283 | // Merge notes together... |
284 | $mainCriteria = [ |
285 | 'resource_id' => $dupe['resource_id'], |
286 | 'list_id' => $dupe['list_id'], |
287 | 'user_id' => $dupe['user_id'], |
288 | ]; |
289 | $dupeRows = $this->select($mainCriteria); |
290 | $notes = []; |
291 | foreach ($dupeRows as $row) { |
292 | if (!empty($row['notes'])) { |
293 | $notes[] = $row['notes']; |
294 | } |
295 | } |
296 | $this->update( |
297 | ['notes' => implode(' ', $notes)], |
298 | ['id' => $dupe['id']] |
299 | ); |
300 | // Now delete extra rows... |
301 | $callback = function ($select) use ($dupe, $mainCriteria) { |
302 | // match on all relevant IDs in duplicate group |
303 | $select->where($mainCriteria); |
304 | // getDuplicates returns the minimum id in the set, so we want to |
305 | // delete all of the duplicates with a higher id value. |
306 | $select->where->greaterThan('id', $dupe['id']); |
307 | }; |
308 | $this->delete($callback); |
309 | |
310 | // Done -- commit the transaction: |
311 | $connection->commit(); |
312 | } |
313 | } |
314 | } |