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

Pages::__construct()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 2
nop 9
dl 0
loc 20
rs 9.9666
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace App\Model;
6
7
use App\Repository\PagesRepository;
8
use DateTime;
9
10
/**
11
 * A Pages provides statistics about the pages created by a given User.
12
 */
13
class Pages extends Model
14
{
15
    private const RESULTS_LIMIT_SINGLE_NAMESPACE = 1000;
16
    private const RESULTS_LIMIT_ALL_NAMESPACES = 50;
17
18
    /** @var string One of 'noredirects', 'onlyredirects' or 'all' for both. */
19
    protected string $redirects;
20
21
    /** @var string One of 'live', 'deleted' or 'all' for both. */
22
    protected string $deleted;
23
24
    /** @var array The list of pages including various statistics, keyed by namespace. */
25
    protected array $pages;
26
27
    /** @var array Number of redirects/pages that were created/deleted, broken down by namespace. */
28
    protected array $countsByNamespace;
29
30
    /**
31
     * Pages constructor.
32
     * @param PagesRepository $repository
33
     * @param Project $project
34
     * @param User $user
35
     * @param string|int $namespace Namespace ID or 'all'.
36
     * @param string $redirects One of 'noredirects', 'onlyredirects' or 'all' for both.
37
     * @param string $deleted One of 'live', 'deleted' or 'all' for both.
38
     * @param int|false $start Start date as Unix timestamp.
39
     * @param int|false $end End date as Unix timestamp.
40
     * @param int|false $offset Unix timestamp. Used for pagination.
41
     */
42
    public function __construct(
43
        PagesRepository $repository,
44
        Project $project,
45
        User $user,
46
        $namespace = 0,
47
        string $redirects = 'noredirects',
48
        string $deleted = 'all',
49
        $start = false,
50
        $end = false,
51
        $offset = false
52
    ) {
53
        $this->repository = $repository;
54
        $this->project = $project;
55
        $this->user = $user;
56
        $this->namespace = 'all' === $namespace ? 'all' : (int)$namespace;
57
        $this->start = $start;
58
        $this->end = $end;
59
        $this->redirects = $redirects ?: 'noredirects';
60
        $this->deleted = $deleted ?: 'all';
61
        $this->offset = $offset;
62
    }
63
64
    /**
65
     * The redirects option associated with this Pages instance.
66
     * @return string
67
     */
68
    public function getRedirects(): string
69
    {
70
        return $this->redirects;
71
    }
72
73
    /**
74
     * The deleted pages option associated with this Page instance.
75
     * @return string
76
     */
77
    public function getDeleted(): string
78
    {
79
        return $this->deleted;
80
    }
81
82
    /**
83
     * Fetch and prepare the pages created by the user.
84
     * @param bool $all Whether to get *all* results. This should only be used for
85
     *     export options. HTTP and JSON should paginate.
86
     * @return array
87
     * @codeCoverageIgnore
88
     */
89
    public function prepareData(bool $all = false): array
90
    {
91
        $this->pages = [];
92
93
        foreach ($this->getNamespaces() as $ns) {
94
            $data = $this->fetchPagesCreated($ns, $all);
95
            $this->pages[$ns] = count($data) > 0
96
                ? $this->formatPages($data)[$ns]
97
                : [];
98
        }
99
100
        return $this->pages;
101
    }
102
103
    /**
104
     * The public function to get the list of all pages created by the user,
105
     * up to self::resultsPerPage(), across all namespaces.
106
     * @param bool $all Whether to get *all* results. This should only be used for
107
     *     export options. HTTP and JSON should paginate.
108
     * @return array
109
     */
110
    public function getResults(bool $all = false): array
111
    {
112
        if (!isset($this->pages)) {
113
            $this->prepareData($all);
114
        }
115
        return $this->pages;
116
    }
117
118
    /**
119
     * Return a ISO 8601 timestamp of the last result. This is used for pagination purposes.
120
     * @return string|null
121
     */
122
    public function getLastTimestamp(): ?string
123
    {
124
        if ($this->isMultiNamespace()) {
125
            // No pagination in multi-namespace view.
126
            return null;
127
        }
128
129
        $numResults = count($this->getResults()[$this->getNamespace()]);
130
        $timestamp = new DateTime($this->getResults()[$this->getNamespace()][$numResults - 1]['rev_timestamp']);
131
        return $timestamp->format('Y-m-d\TH:i:s');
132
    }
133
134
    /**
135
     * Get the total number of pages the user has created.
136
     * @return int
137
     */
138
    public function getNumPages(): int
139
    {
140
        $total = 0;
141
        foreach (array_values($this->getCounts()) as $values) {
142
            $total += $values['count'];
143
        }
144
        return $total;
145
    }
146
147
    /**
148
     * Get the total number of pages we're showing data for.
149
     * @return int
150
     */
151
    public function getNumResults(): int
152
    {
153
        $total = 0;
154
        foreach (array_values($this->getResults()) as $pages) {
155
            $total += count($pages);
156
        }
157
        return $total;
158
    }
159
160
    /**
161
     * Get the total number of pages that are currently deleted.
162
     * @return int
163
     */
164
    public function getNumDeleted(): int
165
    {
166
        $total = 0;
167
        foreach (array_values($this->getCounts()) as $values) {
168
            $total += $values['deleted'];
169
        }
170
        return $total;
171
    }
172
173
    /**
174
     * Get the total number of pages that are currently redirects.
175
     * @return int
176
     */
177
    public function getNumRedirects(): int
178
    {
179
        $total = 0;
180
        foreach (array_values($this->getCounts()) as $values) {
181
            $total += $values['redirects'];
182
        }
183
        return $total;
184
    }
185
186
    /**
187
     * Get the namespaces in which this user has created pages.
188
     * @return int[] The IDs.
189
     */
190
    public function getNamespaces(): array
191
    {
192
        return array_keys($this->getCounts());
193
    }
194
195
    /**
196
     * Number of namespaces being reported.
197
     * @return int
198
     */
199
    public function getNumNamespaces(): int
200
    {
201
        return count(array_keys($this->getCounts()));
202
    }
203
204
    /**
205
     * Are there more than one namespace in the results?
206
     * @return bool
207
     */
208
    public function isMultiNamespace(): bool
209
    {
210
        return $this->getNumNamespaces() > 1 || ('all' === $this->getNamespace() && 1 === $this->getNumNamespaces());
211
    }
212
213
    /**
214
     * Get the sum of all page sizes, across all specified namespaces.
215
     * @return int
216
     */
217
    public function getTotalPageSize(): int
218
    {
219
        return array_sum(array_column($this->getCounts(), 'total_length'));
220
    }
221
222
    /**
223
     * Get average size across all pages.
224
     * @return float
225
     */
226
    public function averagePageSize(): float
227
    {
228
        return $this->getTotalPageSize() / $this->getNumPages();
229
    }
230
231
    /**
232
     * Number of redirects/pages that were created/deleted, broken down by namespace.
233
     * @return array Namespace IDs as the keys, with values 'count', 'deleted' and 'redirects'.
234
     */
235
    public function getCounts(): array
236
    {
237
        if (isset($this->countsByNamespace)) {
238
            return $this->countsByNamespace;
239
        }
240
241
        $counts = [];
242
243
        foreach ($this->countPagesCreated() as $row) {
244
            $ns = (int)$row['namespace'];
245
            $count = (int)$row['count'];
246
            $totalLength = (int)$row['total_length'];
247
            $counts[$ns] = [
248
                'count' => $count,
249
                'total_length' => $totalLength,
250
                'avg_length' => round($count > 0 ?$totalLength / $count : 0, 1),
251
            ];
252
            if ('live' !== $this->deleted) {
253
                $counts[$ns]['deleted'] = (int)$row['deleted'];
254
            }
255
            if ('noredirects' !== $this->redirects) {
256
                $counts[$ns]['redirects'] = (int)$row['redirects'];
257
            }
258
        }
259
260
        $this->countsByNamespace = $counts;
261
        return $this->countsByNamespace;
262
    }
263
264
    /**
265
     * Get the number of pages the user created by assessment.
266
     * @return array Keys are the assessment class, values are the counts.
267
     */
268
    public function getAssessmentCounts(): array
269
    {
270
        if ($this->getNumPages() > $this->resultsPerPage()) {
271
            $counts = $this->repository->getAssessmentCounts(
0 ignored issues
show
Bug introduced by
The method getAssessmentCounts() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

271
            /** @scrutinizer ignore-call */ 
272
            $counts = $this->repository->getAssessmentCounts(
Loading history...
272
                $this->project,
273
                $this->user,
274
                $this->namespace,
275
                $this->redirects
276
            );
277
        } else {
278
            $counts = [];
279
            foreach ($this->pages as $nsPages) {
280
                foreach ($nsPages as $page) {
281
                    if (!isset($counts[$page['assessment']['class'] ?? 'Unknown'])) {
282
                        $counts[$page['assessment']['class'] ?? 'Unknown'] = 1;
283
                    } else {
284
                        $counts[$page['assessment']['class'] ?? 'Unknown']++;
285
                    }
286
                }
287
            }
288
        }
289
290
        arsort($counts);
291
292
        return $counts;
293
    }
294
295
    /**
296
     * Number of results to show, depending on the namespace.
297
     * @param bool $all Whether to get *all* results. This should only be used for
298
     *     export options. HTTP and JSON should paginate.
299
     * @return int|false
300
     */
301
    public function resultsPerPage(bool $all = false)
302
    {
303
        if (true === $all) {
304
            return false;
305
        }
306
        if ('all' === $this->namespace) {
307
            return self::RESULTS_LIMIT_ALL_NAMESPACES;
308
        }
309
        return self::RESULTS_LIMIT_SINGLE_NAMESPACE;
310
    }
311
312
    /**
313
     * Run the query to get pages created by the user with options.
314
     * This is ran independently for each namespace if $this->namespace is 'all'.
315
     * @param int $namespace Namespace ID.
316
     * @param bool $all Whether to get *all* results. This should only be used for
317
     *     export options. HTTP and JSON should paginate.
318
     * @return array
319
     */
320
    private function fetchPagesCreated(int $namespace, bool $all = false): array
321
    {
322
        return $this->repository->getPagesCreated(
0 ignored issues
show
Bug introduced by
The method getPagesCreated() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

322
        return $this->repository->/** @scrutinizer ignore-call */ getPagesCreated(
Loading history...
323
            $this->project,
324
            $this->user,
325
            $namespace,
326
            $this->redirects,
327
            $this->deleted,
328
            $this->start,
329
            $this->end,
330
            $this->resultsPerPage($all),
331
            $this->offset
332
        );
333
    }
334
335
    /**
336
     * Run the query to get the number of pages created by the user with given options.
337
     * @return array
338
     */
339
    private function countPagesCreated(): array
340
    {
341
        return $this->repository->countPagesCreated(
0 ignored issues
show
Bug introduced by
The method countPagesCreated() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

341
        return $this->repository->/** @scrutinizer ignore-call */ countPagesCreated(
Loading history...
342
            $this->project,
343
            $this->user,
344
            $this->namespace,
345
            $this->redirects,
346
            $this->deleted,
347
            $this->start,
348
            $this->end
349
        );
350
    }
351
352
    /**
353
     * Format the data, adding page titles, assessment badges,
354
     * and sorting by namespace and then timestamp.
355
     * @param array $pages As returned by self::fetchPagesCreated()
356
     * @return array
357
     */
358
    private function formatPages(array $pages): array
359
    {
360
        $results = [];
361
362
        foreach ($pages as $row) {
363
            $fullPageTitle = $row['namespace'] > 0
364
                ? $this->project->getNamespaces()[$row['namespace']].':'.$row['page_title']
365
                : $row['page_title'];
366
            $pageData = [
367
                'deleted' => 'arc' === $row['type'],
368
                'namespace' => $row['namespace'],
369
                'page_title' => $row['page_title'],
370
                'full_page_title' => $fullPageTitle,
371
                'redirect' => (bool)$row['redirect'],
372
                'timestamp' => $row['timestamp'],
373
                'rev_id' => $row['rev_id'],
374
                'rev_length' => $row['rev_length'],
375
                'length' => $row['length'],
376
            ];
377
378
            if ($row['recreated']) {
379
                $pageData['recreated'] = (bool)$row['recreated'];
380
            } else {
381
                // This is always NULL for live pages, in which case 'recreated' doesn't apply.
382
                unset($pageData['recreated']);
383
            }
384
385
            if ($this->project->hasPageAssessments()) {
386
                $attrs = $this->project
387
                    ->getPageAssessments()
388
                    ->getClassAttrs($row['pa_class'] ?: 'Unknown');
389
                $pageData['assessment'] = [
390
                    'class' => $row['pa_class'] ?: 'Unknown',
391
                    'badge' => $this->project
392
                        ->getPageAssessments()
393
                        ->getBadgeURL($row['pa_class'] ?: 'Unknown'),
394
                    'color' => $attrs['color'],
395
                    'category' => $attrs['category'],
396
                ];
397
            }
398
399
            $results[$row['namespace']][] = $pageData;
400
        }
401
402
        return $results;
403
    }
404
}
405