Passed
Push — swagger-docs ( 6837d7 )
by MusikAnimal
11:33
created

PagesRepository   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 120
dl 0
loc 293
rs 10
c 0
b 0
f 0
wmc 21

5 Methods

Rating   Name   Duplication   Size   Complexity  
A countPagesCreated() 0 41 2
A getPagesCreated() 0 50 5
A getNamespaceRedirectAndDeletedPagesConditions() 0 20 4
B getPagesCreatedInnerSql() 0 70 6
A getAssessmentCounts() 0 47 4
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace App\Repository;
6
7
use App\Model\Project;
8
use App\Model\User;
9
10
/**
11
 * An PagesRepository is responsible for retrieving information from the
12
 * databases for the Pages Created tool. It does not do any post-processing
13
 * of that data.
14
 * @codeCoverageIgnore
15
 */
16
class PagesRepository extends UserRepository
17
{
18
    /**
19
     * Count the number of pages created by a user.
20
     * @param Project $project
21
     * @param User $user
22
     * @param string|int $namespace Namespace ID or 'all'.
23
     * @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both.
24
     * @param string $deleted One of 'live', 'deleted' or blank for both.
25
     * @param int|false $start Start date as Unix timestamp.
26
     * @param int|false $end End date as Unix timestamp.
27
     * @return string[] Result of query, see below. Includes live and deleted pages.
28
     */
29
    public function countPagesCreated(
30
        Project $project,
31
        User $user,
32
        $namespace,
33
        string $redirects,
34
        string $deleted,
35
        $start = false,
36
        $end = false
37
    ): array {
38
        $cacheKey = $this->getCacheKey(func_get_args(), 'num_user_pages_created');
39
        if ($this->cache->hasItem($cacheKey)) {
40
            return $this->cache->getItem($cacheKey)->get();
41
        }
42
43
        $conditions = [
44
            'paSelects' => '',
45
            'paSelectsArchive' => '',
46
            'paJoin' => '',
47
            'revPageGroupBy' => '',
48
        ];
49
        $conditions = array_merge(
50
            $conditions,
51
            $this->getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects),
52
            $this->getUserConditions('' !== $start.$end)
53
        );
54
55
        $sql = "SELECT `namespace`,
56
                    COUNT(page_title) AS `count`,
57
                    SUM(IF(type = 'arc', 1, 0)) AS `deleted`,
58
                    SUM(redirect) AS `redirects`,
59
                    SUM(rev_length) AS `total_length`
60
                FROM (" .
61
            $this->getPagesCreatedInnerSql($project, $conditions, $redirects, $deleted, $start, $end, false, true)."
62
                ) a ".
63
                "GROUP BY `namespace`";
64
65
        $result = $this->executeQuery($sql, $project, $user, $namespace)
66
            ->fetchAllAssociative();
67
68
        // Cache and return.
69
        return $this->setCache($cacheKey, $result);
70
    }
71
72
    /**
73
     * Get pages created by a user.
74
     * @param Project $project
75
     * @param User $user
76
     * @param string|int $namespace Namespace ID or 'all'.
77
     * @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both.
78
     * @param string $deleted One of 'live', 'deleted' or blank for both.
79
     * @param int|false $start Start date as Unix timestamp.
80
     * @param int|false $end End date as Unix timestamp.
81
     * @param int|null $limit Number of results to return, or blank to return all.
82
     * @param false|int $offset Unix timestamp. Used for pagination.
83
     * @return string[] Result of query, see below. Includes live and deleted pages.
84
     */
85
    public function getPagesCreated(
86
        Project $project,
87
        User $user,
88
        $namespace,
89
        string $redirects,
90
        string $deleted,
91
        $start = false,
92
        $end = false,
93
        ?int $limit = 1000,
94
        $offset = false
95
    ): array {
96
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_pages_created');
97
        if ($this->cache->hasItem($cacheKey)) {
98
            return $this->cache->getItem($cacheKey)->get();
99
        }
100
101
        $conditions = [
102
            'paSelects' => '',
103
            'paSelectsArchive' => '',
104
            'paJoin' => '',
105
            'revPageGroupBy' => '',
106
        ];
107
108
        $conditions = array_merge(
109
            $conditions,
110
            $this->getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects),
111
            $this->getUserConditions('' !== $start.$end)
112
        );
113
114
        $pageAssessmentsTable = $project->getTableName('page_assessments');
115
116
        $hasPageAssessments = $this->isWMF && $project->hasPageAssessments();
117
        if ($hasPageAssessments) {
118
            $conditions['paSelects'] = ', pa_class';
119
            $conditions['paSelectsArchive'] = ', NULL AS pa_class';
120
            $conditions['paJoin'] = "LEFT JOIN $pageAssessmentsTable ON rev_page = pa_page_id";
121
            $conditions['revPageGroupBy'] = 'GROUP BY rev_page';
122
        }
123
124
        $sql = "SELECT * FROM (".
125
            $this->getPagesCreatedInnerSql($project, $conditions, $redirects, $deleted, $start, $end, $offset)."
126
                ) a ".
127
                "ORDER BY `timestamp` DESC
128
                ".(!empty($limit) ? "LIMIT $limit" : '');
129
130
        $result = $this->executeQuery($sql, $project, $user, $namespace)
131
            ->fetchAllAssociative();
132
133
        // Cache and return.
134
        return $this->setCache($cacheKey, $result);
135
    }
136
137
    /**
138
     * Get SQL fragments for the namespace and redirects,
139
     * to be used in self::getPagesCreatedInnerSql().
140
     * @param string|int $namespace Namespace ID or 'all'.
141
     * @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both.
142
     * @return string[] With keys 'namespaceRev', 'namespaceArc' and 'redirects'
143
     */
144
    private function getNamespaceRedirectAndDeletedPagesConditions($namespace, string $redirects): array
145
    {
146
        $conditions = [
147
            'namespaceArc' => '',
148
            'namespaceRev' => '',
149
            'redirects' => '',
150
        ];
151
152
        if ('all' !== $namespace) {
153
            $conditions['namespaceRev'] = " AND page_namespace = '".intval($namespace)."' ";
154
            $conditions['namespaceArc'] = " AND ar_namespace = '".intval($namespace)."' ";
155
        }
156
157
        if ('onlyredirects' == $redirects) {
158
            $conditions['redirects'] = " AND page_is_redirect = '1' ";
159
        } elseif ('noredirects' == $redirects) {
160
            $conditions['redirects'] = " AND page_is_redirect = '0' ";
161
        }
162
163
        return $conditions;
164
    }
165
166
    /**
167
     * Inner SQL for getting or counting pages created by the user.
168
     * @param Project $project
169
     * @param string[] $conditions Conditions for the SQL, must include 'paSelects',
170
     *     'paSelectsArchive', 'paJoin', 'whereRev', 'whereArc', 'namespaceRev', 'namespaceArc',
171
     *     'redirects' and 'revPageGroupBy'.
172
     * @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both.
173
     * @param string $deleted One of 'live', 'deleted' or blank for both.
174
     * @param int|false $start Start date as Unix timestamp.
175
     * @param int|false $end End date as Unix timestamp.
176
     * @param int|false $offset Unix timestamp, used for pagination.
177
     * @param bool $count Omit unneeded columns from the SELECT clause.
178
     * @return string Raw SQL.
179
     */
180
    private function getPagesCreatedInnerSql(
181
        Project $project,
182
        array $conditions,
183
        string $redirects,
184
        string $deleted,
185
        $start,
186
        $end,
187
        $offset = false,
188
        bool $count = false
189
    ): string {
190
        $pageTable = $project->getTableName('page');
191
        $revisionTable = $project->getTableName('revision');
192
        $archiveTable = $project->getTableName('archive');
193
        $logTable = $project->getTableName('logging', 'logindex');
194
195
        // Only SELECT things that are needed, based on whether or not we're doing a COUNT.
196
        $revSelects = "DISTINCT page_namespace AS `namespace`, 'rev' AS `type`, page_title, "
197
            . "page_is_redirect AS `redirect`, rev_len AS `rev_length`";
198
        if (!$count) {
199
            $revSelects .= ", page_len AS `length`, rev_timestamp AS `timestamp`, "
200
                . "rev_id, NULL AS `recreated` ";
201
        }
202
203
        $revDateConditions = $this->getDateConditions($start, $end, $offset);
204
        $arDateConditions = $this->getDateConditions($start, $end, $offset, '', 'ar_timestamp');
205
206
        $revisionsSelect = "
207
            SELECT $revSelects ".$conditions['paSelects']."
208
            FROM $pageTable
209
            JOIN $revisionTable ON page_id = rev_page ".
210
            $conditions['paJoin']."
211
            WHERE ".$conditions['whereRev']."
212
                AND rev_parent_id = '0'".
213
                $conditions['namespaceRev'].
214
                $conditions['redirects'].
215
                $revDateConditions.
216
            $conditions['revPageGroupBy'];
217
218
        // Only SELECT things that are needed, based on whether or not we're doing a COUNT.
219
        $arSelects = "ar_namespace AS `namespace`, 'arc' AS `type`, ar_title AS `page_title`, "
220
            . "'0' AS `redirect`, ar_len AS `rev_length`";
221
        if (!$count) {
222
            $arSelects .= ", NULL AS `length`, MIN(ar_timestamp) AS `timestamp`, ".
223
                "ar_rev_id AS `rev_id`, EXISTS(
224
                    SELECT 1 FROM $pageTable
225
                    WHERE namespace = ar_namespace
226
                    AND page_title = ar_title
227
                ) AS `recreated`";
228
        }
229
230
        $archiveSelect = "
231
            SELECT $arSelects ".$conditions['paSelectsArchive']."
232
            FROM $archiveTable
233
            LEFT JOIN $logTable ON log_namespace = ar_namespace AND log_title = ar_title
234
                AND log_actor = ar_actor AND (log_action = 'move' OR log_action = 'move_redir')
235
                AND log_type = 'move'
236
            WHERE ".$conditions['whereArc']."
237
                AND ar_parent_id = '0' ".
238
                $conditions['namespaceArc']."
239
                AND log_action IS NULL
240
                $arDateConditions
241
            GROUP BY ar_namespace, ar_title";
242
243
        if ('live' === $deleted || 'onlyredirects' === $redirects) {
244
            return $revisionsSelect;
245
        } elseif ('deleted' === $deleted) {
246
            return $archiveSelect;
247
        }
248
249
        return "($revisionsSelect) UNION ($archiveSelect)";
250
    }
251
252
    /**
253
     * Get the number of pages the user created by assessment.
254
     * @param Project $project
255
     * @param User $user
256
     * @param int|string $namespace
257
     * @param string $redirects
258
     * @param int|false $start Start date as Unix timestamp.
259
     * @param int|false $end End date as Unix timestamp.
260
     * @return array Keys are the assessment class, values are the counts.
261
     */
262
    public function getAssessmentCounts(
263
        Project $project,
264
        User $user,
265
        $namespace,
266
        string $redirects,
267
        $start = false,
268
        $end = false
269
    ): array {
270
        $cacheKey = $this->getCacheKey(func_get_args(), 'user_pages_created_assessments');
271
        if ($this->cache->hasItem($cacheKey)) {
272
            return $this->cache->getItem($cacheKey)->get();
273
        }
274
275
        $pageTable = $project->getTableName('page');
276
        $revisionTable = $project->getTableName('revision');
277
        $pageAssessmentsTable = $project->getTableName('page_assessments');
278
279
        $conditions = array_merge(
280
            $this->getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects),
281
            $this->getUserConditions('' !== $start.$end)
282
        );
283
        $revDateConditions = $this->getDateConditions($start, $end);
284
285
        $sql = "SELECT pa_class AS `class`, COUNT(pa_class) AS `count` FROM (
286
                    SELECT DISTINCT page_id, IFNULL(pa_class, '') AS pa_class
287
                    FROM $pageTable
288
                    JOIN $revisionTable ON page_id = rev_page
289
                    LEFT JOIN $pageAssessmentsTable ON rev_page = pa_page_id
290
                    WHERE ".$conditions['whereRev']."
291
                    AND rev_parent_id = '0'".
292
                    $conditions['namespaceRev'].
293
                    $conditions['redirects'].
294
                    $revDateConditions."
295
                    GROUP BY page_id
296
                ) a
297
                GROUP BY pa_class";
298
299
        $resultQuery = $this->executeQuery($sql, $project, $user, $namespace);
300
301
        $assessments = [];
302
        while ($result = $resultQuery->fetchAssociative()) {
303
            $class = '' == $result['class'] ? '' : $result['class'];
304
            $assessments[$class] = $result['count'];
305
        }
306
307
        // Cache and return.
308
        return $this->setCache($cacheKey, $assessments);
309
    }
310
}
311