Issues (98)

application/mQueue/Model/StatusMapper.php (5 issues)

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
            $id = (int) $id;
102 1
            if (!array_key_exists($id, $statuses)) {
103 1
                $status = self::getDbTable()->createRow();
104 1
                if ($user) {
105
                    $status->idUser = $user->id;
106
                }
107 1
                $status->idMovie = $id;
108 1
                $statuses[$status->idMovie] = $status;
109
            }
110
        }
111
112 1
        return $statuses;
113
    }
114
115
    /**
116
     * Build statistic for the given user.
117
     *
118
     * @param User $user
119
     *
120
     * @return array statistics
121
     */
122 1
    public static function getStatistics(User $user)
123
    {
124 1
        $select = self::getDbTable()->select()->setIntegrityCheck(false)
125 1
            ->from('status', [
126 1
                'rating' => 'IFNULL(rating, 0)',
127
                'count' => 'COUNT(IFNULL(rating, 0))',
128
            ])
129 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...
130 1
            ->where('isLatest = 1 OR isLatest IS NULL')
131 1
            ->group('IFNULL(rating, 0)');
132
133 1
        $records = self::getDbTable()->fetchAll($select);
134
135
        // Set all count to 0
136 1
        $result = ['total' => 0, 'rated' => 0, Status::Nothing => 0];
137 1
        foreach (Status::$ratings as $val => $name) {
138 1
            $result[$val] = 0;
139
        }
140
141
        // Fetch real counts
142 1
        foreach ($records->toArray() as $row) {
143 1
            $result[$row['rating']] = $row['count'];
144 1
            if ($row['rating'] != Status::Nothing) {
145
                $result['rated'] += $row['count'];
146
            }
147 1
            $result['total'] += $row['count'];
148
        }
149
150 1
        return $result;
151
    }
152
153
    /**
154
     * Build statistic for the given user.
155
     *
156
     * @param User $user
157
     * @param bool $percent
158
     *
159
     * @return array statistics
160
     */
161
    public static function getGraph(User $user = null, $percent = false)
162
    {
163
        $select = self::getDbTable()->select()
164
            ->order('dateUpdate');
165
166
        if ($user) {
167
            $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...
168
        }
169
170
        $records = self::getDbTable()->fetchAll($select);
171
172
        // Set all count to 0
173
        $cumulatedStatuses = [Status::Nothing => 0];
174
        $graphData = [];
175
        foreach (Status::$ratings as $val => $name) {
176
            $cumulatedStatuses[$val] = 0;
177
            $graphData[$val] = [];
178
        }
179
180
        // Fetch real counts
181
        $lastStatuses = [];
182
        foreach ($records as $row) {
183
            // Add new status
184
            ++$cumulatedStatuses[$row->rating];
185
            $changed = [$row->rating];
186
187
            // Substract old status
188
            if (isset($lastStatuses[$row->idUser][$row->idMovie])) {
189
                --$cumulatedStatuses[$lastStatuses[$row->idUser][$row->idMovie]];
190
                $changed[] = $lastStatuses[$row->idUser][$row->idMovie];
191
            }
192
            $lastStatuses[$row->idUser][$row->idMovie] = $row->rating;
193
194
            $time = new DateTime($row->dateUpdate);
195
            $time->setTimezone(new DateTimeZone('GMT'));
196
            $epoch = (int) $time->format('U') * 1000;
197
198
            // If we are in percent mode, we need all status for each timestamp
199
            if ($percent) {
200
                $changed = array_keys(Status::$ratings);
201
            }
202
203
            // Keep for the graph only the changed values (and overwrite previous value if it happened at exactly the same time)
204
            foreach ($changed as $val) {
205
                $graphData[$val][$epoch] = [
206
                    $epoch,
207
                    $cumulatedStatuses[$val],
208
                ];
209
            }
210
        }
211
212
        // Format everything in a more output friendly way
213
        $result = [];
214
        foreach (Status::$ratings as $val => $name) {
215
            $result[] = [
216
                'name' => $name,
217
                'data' => array_values($graphData[$val]),
218
            ];
219
        }
220
221
        return $result;
222
    }
223
224
    /**
225
     * Returns the query to get activity for either the whole system, or a specific user, or a specific movie
226
     *
227
     * @param null|Movie|User $item
228
     *
229
     * @return Zend_Db_Table_Select
230
     */
231 3
    public static function getActivityQuery($item = null)
232
    {
233 3
        $select = self::getDbTable()->select()
234 3
            ->from('status')
235 3
            ->order('dateUpdate DESC');
236
237 3
        if ($item instanceof User) {
238 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...
239 2
        } elseif ($item instanceof Movie) {
240 1
            $select->where('idMovie = ?', $item->id);
241
        }
242
243 3
        return $select;
244
    }
245
}
246