Passed
Push — master ( 5f2715...9adfbc )
by MusikAnimal
06:16
created

TopEditsRepository::getDisplayTitles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the TopEditsRepository class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace AppBundle\Repository;
9
10
use AppBundle\Model\Page;
11
use AppBundle\Model\Project;
12
use AppBundle\Model\User;
13
use Symfony\Component\DependencyInjection\ContainerInterface;
14
15
/**
16
 * TopEditsRepository is responsible for retrieving data from the database
17
 * about the top-edited pages of a user. It doesn't do any post-processing
18
 * of that information.
19
 * @codeCoverageIgnore
20
 */
21
class TopEditsRepository extends Repository
22
{
23
    /**
24
     * Expose the container to the TopEdits class.
25
     * @return ContainerInterface
26
     */
27
    public function getContainer(): ContainerInterface
28
    {
29
        return $this->container;
30
    }
31
32
    /**
33
     * Get the top edits by a user in a single namespace.
34
     * @param Project $project
35
     * @param User $user
36
     * @param int $namespace Namespace ID.
37
     * @param int $limit Number of edits to fetch.
38
     * @param int $offset Number of results past the initial dataset. Used for pagination.
39
     * @return string[] page_namespace, page_title, page_is_redirect,
40
     *   count (number of edits), assessment (page assessment).
41
     */
42
    public function getTopEditsNamespace(
43
        Project $project,
44
        User $user,
45
        int $namespace = 0,
46
        int $limit = 1000,
47
        int $offset = 0
48
    ): array {
49
        // Set up cache.
50
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_ns');
51
        if ($this->cache->hasItem($cacheKey)) {
52
            return $this->cache->getItem($cacheKey)->get();
53
        }
54
55
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
56
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
57
58
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments() && 0 === $namespace;
59
        $paTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
60
        $paSelect = $hasPageAssessments
61
            ?  ", (
62
                    SELECT pa_class
63
                    FROM $paTable
64
                    WHERE pa_page_id = page_id
65
                    AND pa_class != 'Unknown'
66
                    LIMIT 1
67
                ) AS pa_class"
68
            : '';
69
70
        $sql = "SELECT page_namespace, page_title, page_is_redirect, COUNT(page_title) AS count
71
                    $paSelect
72
                FROM $pageTable
73
                JOIN $revisionTable ON page_id = rev_page
74
                WHERE rev_user_text = :username
75
                AND page_namespace = :namespace
76
                GROUP BY page_namespace, page_title
77
                ORDER BY count DESC
78
                LIMIT $limit
79
                OFFSET $offset";
80
        $result = $this->executeProjectsQuery($sql, [
81
            'username' => $user->getUsername(),
82
            'namespace' => $namespace,
83
        ])->fetchAll();
84
85
        // Cache and return.
86
        return $this->setCache($cacheKey, $result);
87
    }
88
89
    /**
90
     * Count the number of edits in the given namespace.
91
     * @param Project $project
92
     * @param User $user
93
     * @param int|string $namespace
94
     * @return mixed
95
     */
96
    public function countEditsNamespace(Project $project, User $user, $namespace)
97
    {
98
        // Set up cache.
99
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_count_ns');
100
        if ($this->cache->hasItem($cacheKey)) {
101
            return $this->cache->getItem($cacheKey)->get();
102
        }
103
104
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
105
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
106
107
        $sql = "SELECT COUNT(DISTINCT page_id) AS count
108
                FROM $pageTable
109
                JOIN $revisionTable ON page_id = rev_page
110
                WHERE rev_user_text = :username
111
                AND page_namespace = :namespace";
112
        $result = $this->executeProjectsQuery($sql, [
113
            'username' => $user->getUsername(),
114
            'namespace' => $namespace,
115
        ])->fetch()['count'];
116
117
        // Cache and return.
118
        return $this->setCache($cacheKey, $result);
119
    }
120
121
    /**
122
     * Get the top edits by a user across all namespaces.
123
     * @param Project $project
124
     * @param User $user
125
     * @param int $limit Number of edits to fetch.
126
     * @return string[] page_namespace, page_title, page_is_redirect,
127
     *   count (number of edits), assessment (page assessment).
128
     */
129
    public function getTopEditsAllNamespaces(Project $project, User $user, int $limit = 10): array
130
    {
131
        // Set up cache.
132
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_all');
133
        if ($this->cache->hasItem($cacheKey)) {
134
            return $this->cache->getItem($cacheKey)->get();
135
        }
136
137
        $pageTable = $this->getTableName($project->getDatabaseName(), 'page');
138
        $revisionTable = $this->getTableName($project->getDatabaseName(), 'revision');
139
        $hasPageAssessments = $this->isLabs() && $project->hasPageAssessments();
140
        $pageAssessmentsTable = $this->getTableName($project->getDatabaseName(), 'page_assessments');
141
        $paSelect = $hasPageAssessments
142
            ?  ", (
143
                    SELECT pa_class
144
                    FROM $pageAssessmentsTable
145
                    WHERE pa_page_id = e.page_id
146
                    LIMIT 1
147
                ) AS pa_class"
148
            : ', NULL as pa_class';
149
150
        $sql = "SELECT c.page_namespace, e.page_title, c.page_is_redirect, c.count $paSelect
151
                FROM
152
                (
153
                    SELECT b.page_namespace, b.page_is_redirect, b.rev_page, b.count
154
                        ,@rn := if(@ns = b.page_namespace, @rn + 1, 1) AS row_number
155
                        ,@ns := b.page_namespace AS dummy
156
                    FROM
157
                    (
158
                        SELECT page_namespace, page_is_redirect, rev_page, count(rev_page) AS count
159
                        FROM $revisionTable
160
                        JOIN $pageTable ON page_id = rev_page
161
                        WHERE rev_user_text = :username
162
                        GROUP BY page_namespace, rev_page
163
                    ) AS b
164
                    JOIN (SELECT @ns := NULL, @rn := 0) AS vars
165
                    ORDER BY b.page_namespace ASC, b.count DESC
166
                ) AS c
167
                JOIN $pageTable e ON e.page_id = c.rev_page
168
                WHERE c.row_number < $limit";
169
        $result = $this->executeProjectsQuery($sql, [
170
            'username' => $user->getUsername(),
171
        ])->fetchAll();
172
173
        // Cache and return.
174
        return $this->setCache($cacheKey, $result);
175
    }
176
177
    /**
178
     * Get the top edits by a user to a single page.
179
     * @param Page $page
180
     * @param User $user
181
     * @return string[] Each row with keys 'id', 'timestamp', 'minor', 'length',
182
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
183
     */
184
    public function getTopEditsPage(Page $page, User $user): array
185
    {
186
        // Set up cache.
187
        $cacheKey = $this->getCacheKey(func_get_args(), 'topedits_page');
188
        if ($this->cache->hasItem($cacheKey)) {
189
            return $this->cache->getItem($cacheKey)->get();
190
        }
191
192
        $results = $this->queryTopEditsPage($page, $user, true);
193
194
        // Now we need to get the most recent revision, since the childrevs stuff excludes it.
195
        $lastRev = $this->queryTopEditsPage($page, $user, false);
196
        if (empty($results) || $lastRev[0]['id'] !== $results[0]['id']) {
197
            $results = array_merge($lastRev, $results);
198
        }
199
200
        // Cache and return.
201
        return $this->setCache($cacheKey, $results);
202
    }
203
204
    /**
205
     * The actual query to get the top edits by the user to the page.
206
     * Because of the way the main query works, we aren't given the most recent revision,
207
     * so we have to call this twice, once with $childRevs set to true and once with false.
208
     * @param Page $page
209
     * @param User $user
210
     * @param boolean $childRevs Whether to include child revisions.
211
     * @return array Each row with keys 'id', 'timestamp', 'minor', 'length',
212
     *   'length_change', 'reverted', 'user_id', 'username', 'comment', 'parent_comment'
213
     */
214
    private function queryTopEditsPage(Page $page, User $user, bool $childRevs = false): array
215
    {
216
        $revTable = $this->getTableName($page->getProject()->getDatabaseName(), 'revision');
217
218
        if ($childRevs) {
219
            $childSelect = ', (CASE WHEN childrevs.rev_sha1 = parentrevs.rev_sha1 THEN 1 ELSE 0 END) AS reverted,
220
                    childrevs.rev_comment AS parent_comment';
221
            $childJoin = "LEFT JOIN $revTable AS childrevs ON (revs.rev_id = childrevs.rev_parent_id)";
222
            $childWhere = 'AND childrevs.rev_page = :pageid';
223
            $childLimit = '';
224
        } else {
225
            $childSelect = ', "" AS parent_comment, 0 AS reverted';
226
            $childJoin = '';
227
            $childWhere = '';
228
            $childLimit = 'LIMIT 1';
229
        }
230
231
        $sql = "SELECT
232
                    revs.rev_id AS id,
233
                    revs.rev_timestamp AS timestamp,
234
                    revs.rev_minor_edit AS minor,
235
                    revs.rev_len AS length,
236
                    (CAST(revs.rev_len AS SIGNED) - IFNULL(parentrevs.rev_len, 0)) AS length_change,
237
                    revs.rev_user AS user_id,
238
                    revs.rev_user_text AS username,
239
                    revs.rev_comment AS comment
240
                    $childSelect
241
                FROM $revTable AS revs
242
                LEFT JOIN $revTable AS parentrevs ON (revs.rev_parent_id = parentrevs.rev_id)
243
                $childJoin
244
                WHERE revs.rev_user_text = :username
245
                AND revs.rev_page = :pageid
246
                $childWhere
247
                ORDER BY revs.rev_timestamp DESC
248
                $childLimit";
249
250
        return $this->executeProjectsQuery($sql, [
251
            'pageid' => $page->getId(),
252
            'username' => $user->getUsername(),
253
        ])->fetchAll();
254
    }
255
}
256