Failed Conditions
Push — master ( 674f38...37557b )
by Adrien
03:06
created

StatusMapper::findAll()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 11.4651

Importance

Changes 0
Metric Value
eloc 19
c 0
b 0
f 0
dl 0
loc 34
rs 8.8333
ccs 11
cts 20
cp 0.55
cc 7
nc 9
nop 2
crap 11.4651
1
<?php
2
3
namespace mQueue\Model;
4
5
use DateTime;
6
use DateTimeZone;
7
use Zend_Db_Table_Select;
8
9
abstract class StatusMapper extends AbstractMapper
10
{
11
    /**
12
     * Define the status for a movie-user tuple. If an existing satus exists and
13
     * is very recent, it will be updated, otherwise a new status will be created.
14
     * IMPORTANT: This is the only allowed way to modify status.
15
     *
16
     * @param Movie $movie
17
     * @param User $user
18
     * @param int $rating @see \mQueue\Model\Status
19
     *
20
     * @return Status
21
     */
22
    public static function set(Movie $movie, User $user, $rating)
23
    {
24
        $db = self::getDbTable()->getAdapter();
25
        $db->beginTransaction();
26
27
        // Find out if a very recent status exist to be replaced, so user can change their mind "quickly"
28
        $select = self::getDbTable()->select()
29
            ->where('idUser = ?', $user->id)
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on mQueue\Model\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
30
            ->where('idMovie = ?', $movie->id)
31
            ->where('dateUpdate > DATE_SUB(NOW(), INTERVAL 5 MINUTE)');
32
33
        $status = self::getDbTable()->fetchRow($select);
34
35
        // Otherwise create a brand new one and set all existing one as "old"
36
        if (!$status) {
37
            $status = self::getDbTable()->createRow();
38
            $status->idUser = $user->id;
39
            $status->idMovie = $movie->id;
40
            $status->isLatest = true;
41
42
            // Here we must set dateUpdate to itself to avoid auto-update of the timestamp field by MySql
43
            $db->query('UPDATE `status` SET isLatest = 0, dateUpdate = dateUpdate WHERE idUser = ? AND idMovie = ?', [$user->id, $movie->id]);
44
        }
45
46
        $status->rating = $rating;
47
        $status->save();
48
49
        $db->commit();
50
51
        return $status;
52
    }
53
54
    /**
55
     * Find a status by its user and movie. If not found it will be created (but not saved).
56
     *
57
     * @param int $idMovie
58
     * @param null|User $user
59
     *
60
     * @return Status
61
     */
62 1
    public static function find($idMovie, User $user = null)
63
    {
64 1
        $statuses = self::findAll([$idMovie], $user);
65
66 1
        return reset($statuses);
67
    }
68
69
    /**
70
     * Returns an array of Status containing all statuses for specified ids
71
     * (if they don't exist in database, they will be created with default values but not saved)
72
     *
73
     * @param array $idMovies
74
     * @param null|User $user
75
     *
76
     * @return array of \mQueue\Model\Status
77
     */
78 1
    public static function findAll(array $idMovies, User $user = null)
79
    {
80 1
        $statuses = [];
81 1
        if (!count($idMovies)) {
82
            return $statuses;
83
        }
84
85
        // Do not hit database if we know there won't be any result anyway
86 1
        if ($user) {
87
            $select = self::getDbTable()->select()
88
                ->where('idUser = ?', $user->id)
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on mQueue\Model\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
89
                ->where('idMovie IN (?)', $idMovies)
90
                ->where('isLatest = 1');
91
92
            $records = self::getDbTable()->fetchAll($select);
93
94
            foreach ($records as $record) {
95
                $statuses[$record->idMovie] = $record;
96
            }
97
        }
98
99
        // Fill non existing statuses in databases
100 1
        foreach ($idMovies as $id) {
101 1
            if (!array_key_exists($id, $statuses)) {
102 1
                $status = self::getDbTable()->createRow();
103 1
                if ($user) {
104
                    $status->idUser = $user->id;
105
                }
106 1
                $status->idMovie = $id;
107 1
                $statuses[$status->idMovie] = $status;
108
            }
109
        }
110
111 1
        return $statuses;
112
    }
113
114
    /**
115
     * Build statistic for the given user.
116
     *
117
     * @param User $user
118
     *
119
     * @return array statistics
120
     */
121 1
    public static function getStatistics(User $user)
122
    {
123 1
        $select = self::getDbTable()->select()->setIntegrityCheck(false)
124 1
            ->from('status', [
125 1
                'rating' => 'IFNULL(rating, 0)',
126
                'count' => 'COUNT(IFNULL(rating, 0))',
127
            ])
128 1
            ->joinRight('movie', 'movie.id = status.idMovie AND status.idUser = ' . $user->id, [])
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on mQueue\Model\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
129 1
            ->where('isLatest = 1 OR isLatest IS NULL')
130 1
            ->group('IFNULL(rating, 0)');
131
132 1
        $records = self::getDbTable()->fetchAll($select);
133
134
        // Set all count to 0
135 1
        $result = ['total' => 0, 'rated' => 0, Status::Nothing => 0];
136 1
        foreach (Status::$ratings as $val => $name) {
137 1
            $result[$val] = 0;
138
        }
139
140
        // Fetch real counts
141 1
        foreach ($records->toArray() as $row) {
142 1
            $result[$row['rating']] = $row['count'];
143 1
            if ($row['rating'] != Status::Nothing) {
144
                $result['rated'] += $row['count'];
145
            }
146 1
            $result['total'] += $row['count'];
147
        }
148
149 1
        return $result;
150
    }
151
152
    /**
153
     * Build statistic for the given user.
154
     *
155
     * @param User $user
156
     * @param bool $percent
157
     *
158
     * @return array statistics
159
     */
160
    public static function getGraph(User $user = null, $percent = false)
161
    {
162
        $select = self::getDbTable()->select()
163
            ->order('dateUpdate');
164
165
        if ($user) {
166
            $select->where('idUser = ?', $user->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on mQueue\Model\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
167
        }
168
169
        $records = self::getDbTable()->fetchAll($select);
170
171
        // Set all count to 0
172
        $cumulatedStatuses = [Status::Nothing => 0];
173
        $graphData = [];
174
        foreach (Status::$ratings as $val => $name) {
175
            $cumulatedStatuses[$val] = 0;
176
            $graphData[$val] = [];
177
        }
178
179
        // Fetch real counts
180
        $lastStatuses = [];
181
        foreach ($records as $row) {
182
            // Add new status
183
            ++$cumulatedStatuses[$row->rating];
184
            $changed = [$row->rating];
185
186
            // Substract old status
187
            if (isset($lastStatuses[$row->idUser][$row->idMovie])) {
188
                --$cumulatedStatuses[$lastStatuses[$row->idUser][$row->idMovie]];
189
                $changed[] = $lastStatuses[$row->idUser][$row->idMovie];
190
            }
191
            $lastStatuses[$row->idUser][$row->idMovie] = $row->rating;
192
193
            $time = new DateTime($row->dateUpdate);
194
            $time->setTimezone(new DateTimeZone('GMT'));
195
            $epoch = (int) $time->format('U') * 1000;
196
197
            // If we are in percent mode, we need all status for each timestamp
198
            if ($percent) {
199
                $changed = array_keys(Status::$ratings);
200
            }
201
202
            // Keep for the graph only the changed values (and overwrite previous value if it happened at exactly the same time)
203
            foreach ($changed as $val) {
204
                $graphData[$val][$epoch] = [
205
                    $epoch,
206
                    $cumulatedStatuses[$val],
207
                ];
208
            }
209
        }
210
211
        // Format everything in a more output friendly way
212
        $result = [];
213
        foreach (Status::$ratings as $val => $name) {
214
            $result[] = [
215
                'name' => $name,
216
                'data' => array_values($graphData[$val]),
217
            ];
218
        }
219
220
        return $result;
221
    }
222
223
    /**
224
     * Returns the query to get activity for either the whole system, or a specific user, or a specific movie
225
     *
226
     * @param null|Movie|User $item
227
     *
228
     * @return Zend_Db_Table_Select
229
     */
230 3
    public static function getActivityQuery($item = null)
231
    {
232 3
        $select = self::getDbTable()->select()
233 3
            ->from('status')
234 3
            ->order('dateUpdate DESC');
235
236 3
        if ($item instanceof User) {
237 1
            $select->where('idUser = ?', $item->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on mQueue\Model\User. Since you implemented __get, consider adding a @property annotation.
Loading history...
238 2
        } elseif ($item instanceof Movie) {
239 1
            $select->where('idMovie = ?', $item->id);
240
        }
241
242 3
        return $select;
243
    }
244
}
245