Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
3.28% covered (danger)
3.28%
2 / 61
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Ratings
3.28% covered (danger)
3.28%
2 / 61
40.00% covered (danger)
40.00%
2 / 5
218.59
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getForResource
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 getCountsForResource
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
90
 deleteByUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStatistics
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Table Definition for ratings
5 *
6 * PHP version 8
7 *
8 * Copyright (C) The National Library of Finland 2022.
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   Ere Maijala <ere.maijala@helsinki.fi>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org Main Site
28 */
29
30namespace VuFind\Db\Table;
31
32use Laminas\Db\Adapter\Adapter;
33use Laminas\Db\Sql\Expression;
34use Laminas\Db\Sql\Select;
35use VuFind\Db\Row\RowGateway;
36use VuFind\Db\Service\DbServiceAwareInterface;
37use VuFind\Db\Service\DbServiceAwareTrait;
38use VuFind\Db\Service\ResourceServiceInterface;
39
40/**
41 * Table Definition for ratings
42 *
43 * @category VuFind
44 * @package  Db_Table
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 */
49class Ratings extends Gateway implements DbServiceAwareInterface
50{
51    use DbServiceAwareTrait;
52
53    /**
54     * Constructor
55     *
56     * @param Adapter       $adapter Database adapter
57     * @param PluginManager $tm      Table manager
58     * @param array         $cfg     Laminas configuration
59     * @param RowGateway    $rowObj  Row prototype object (null for default)
60     * @param string        $table   Name of database table to interface with
61     */
62    public function __construct(
63        Adapter $adapter,
64        PluginManager $tm,
65        $cfg,
66        ?RowGateway $rowObj = null,
67        $table = 'ratings'
68    ) {
69        parent::__construct($adapter, $tm, $cfg, $rowObj, $table);
70    }
71
72    /**
73     * Get average rating and rating count associated with the specified resource.
74     *
75     * @param string $id     Record ID to look up
76     * @param string $source Source of record to look up
77     * @param ?int   $userId User ID, or null for all users
78     *
79     * @return array Array with keys count and rating (between 0 and 100)
80     */
81    public function getForResource(string $id, string $source, ?int $userId): array
82    {
83        $resourceService = $this->getDbService(ResourceServiceInterface::class);
84        $resource = $resourceService->getResourceByRecordId($id, $source);
85        if (!$resource) {
86            return [
87                'count' => 0,
88                'rating' => 0,
89            ];
90        }
91
92        $callback = function ($select) use ($resource, $userId) {
93            $select->columns(
94                [
95                    // RowGateway requires an id field:
96                    'id' => new Expression(
97                        '1',
98                        [],
99                        [Expression::TYPE_IDENTIFIER]
100                    ),
101                    'count' => new Expression(
102                        'COUNT(?)',
103                        [Select::SQL_STAR],
104                        [Expression::TYPE_IDENTIFIER]
105                    ),
106                    'rating' => new Expression(
107                        'FLOOR(AVG(?))',
108                        ['rating'],
109                        [Expression::TYPE_IDENTIFIER]
110                    ),
111                ]
112            );
113            $select->where->equalTo('ratings.resource_id', $resource->id);
114            if (null !== $userId) {
115                $select->where->equalTo('ratings.user_id', $userId);
116            }
117        };
118
119        $result = $this->select($callback)->current();
120        return [
121            'count' => $result->count ?? 0,
122            'rating' => $result->rating ?? 0,
123        ];
124    }
125
126    /**
127     * Get rating breakdown for the specified resource.
128     *
129     * @param string $id     Record ID to look up
130     * @param string $source Source of record to look up
131     * @param array  $groups Group definition (key => [min, max])
132     *
133     * @return array Array with keys count and rating (between 0 and 100) as well as
134     * an groups array with ratings from lowest to highest
135     */
136    public function getCountsForResource(
137        string $id,
138        string $source,
139        array $groups
140    ): array {
141        $result = [
142            'count' => 0,
143            'rating' => 0,
144            'groups' => [],
145        ];
146        foreach (array_keys($groups) as $key) {
147            $result['groups'][$key] = 0;
148        }
149
150        $resourceService = $this->getDbService(ResourceServiceInterface::class);
151        $resource = $resourceService->getResourceByRecordId($id, $source);
152        if (!$resource) {
153            return $result;
154        }
155
156        $callback = function ($select) use ($resource) {
157            $select->columns(
158                [
159                    // RowGateway requires an id field:
160                    'id' => new Expression(
161                        '1',
162                        [],
163                        [Expression::TYPE_IDENTIFIER]
164                    ),
165                    'count' => new Expression(
166                        'COUNT(?)',
167                        [Select::SQL_STAR],
168                        [Expression::TYPE_IDENTIFIER]
169                    ),
170                    'rating' => 'rating',
171                ]
172            );
173            $select->where->equalTo('ratings.resource_id', $resource->id);
174            $select->group('rating');
175        };
176
177        $ratingTotal = 0;
178        $groupCount = 0;
179        foreach ($this->select($callback) as $rating) {
180            $result['count'] += $rating->count;
181            $ratingTotal += $rating->rating;
182            ++$groupCount;
183            if ($groups) {
184                foreach ($groups as $key => $range) {
185                    if (
186                        $rating->rating >= $range[0] && $rating->rating <= $range[1]
187                    ) {
188                        $result['groups'][$key] = ($result['groups'][$key] ?? 0)
189                            + $rating->count;
190                    }
191                }
192            }
193        }
194        $result['rating'] = $groupCount ? floor($ratingTotal / $groupCount) : 0;
195        return $result;
196    }
197
198    /**
199     * Deletes all ratings by a user.
200     *
201     * @param \VuFind\Db\Row\User $user User object
202     *
203     * @return void
204     */
205    public function deleteByUser(\VuFind\Db\Row\User $user): void
206    {
207        $this->delete(['user_id' => $user->id]);
208    }
209
210    /**
211     * Get statistics on use of ratings.
212     *
213     * @return array
214     */
215    public function getStatistics(): array
216    {
217        $select = $this->sql->select();
218        $select->columns(
219            [
220                'users' => new Expression(
221                    'COUNT(DISTINCT(?))',
222                    ['user_id'],
223                    [Expression::TYPE_IDENTIFIER]
224                ),
225                'resources' => new Expression(
226                    'COUNT(DISTINCT(?))',
227                    ['resource_id'],
228                    [Expression::TYPE_IDENTIFIER]
229                ),
230                'total' => new Expression('COUNT(*)'),
231            ]
232        );
233        $statement = $this->sql->prepareStatementForSqlObject($select);
234        $result = $statement->execute();
235        return (array)$result->current();
236    }
237}