Skip to content

Commit f8f56a1

Browse files
authored
Report generation performance improvements (#310)
* Clean up controller logic by extracting to `data()` function. * Add basic test coverage. * Check all files instead of directories, because of how they're generated into a random structure. * Actual implementation has to be using carbon too. * Windows, yuck. * This stuff is required on Page. * Chunking overhaul, almost there. * Defer validating of pages until after all yaml pages are generated. * Test chunking. * Suppress pending page results errors when running queue workers. * Ensure we only trigger generation once, even with async queue workers. * These aren’t being used anywhere. They are defined on report. * Massively improve performance around validating unique titles and descriptions. * Cleanup and more performance improvements. * Cache key cleanup. * Only cache `toArray()` when generated and WITH pages. * Make queue chunk size configurable. * Fix resource deleter refs issue. * Ensure caches are cleared when creating and deleting, in case IDs get reused. * Update test.
1 parent e655e84 commit f8f56a1

File tree

9 files changed

+512
-141
lines changed

9 files changed

+512
-141
lines changed

config/seo-pro.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@
4343
'excluded_sites' => [],
4444
],
4545

46+
'reports' => [
47+
'queue_chunk_size' => 1000,
48+
],
49+
4650
];

resources/views/reports/index.blade.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
@can('delete seo reports')
2929
<td class="float-right">
3030
<dropdown-list>
31-
<dropdown-item :text="__('seo-pro::messages.delete_report')" class="warning" @click="$refs.deleter.confirm()">
31+
<dropdown-item :text="__('seo-pro::messages.delete_report')" class="warning" @click="$refs.deleter_{{ $report->id() }}.confirm()">
3232
<resource-deleter
33-
ref="deleter"
33+
ref="deleter_{{ $report->id() }}"
3434
resource-title="Report"
3535
route="{{ cp_route('seo-pro.reports.destroy', $report->id()) }}"
3636
redirect="{{ cp_route('seo-pro.reports.index') }}"

src/Http/Controllers/ReportController.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function show(Request $request, $id)
3636
$report = Report::find($id);
3737

3838
if ($request->ajax()) {
39-
return $this->showOrGenerateReport($report);
39+
return $report->data();
4040
}
4141

4242
return view('seo-pro::reports.show', ['report' => $report]);
@@ -48,15 +48,4 @@ public function destroy($id)
4848

4949
return Report::find($id)->delete();
5050
}
51-
52-
private function showOrGenerateReport($report)
53-
{
54-
if ($report->status() === 'pending') {
55-
$report = $report->queueGenerate();
56-
} elseif ($report->isLegacyReport()) {
57-
$report = $report->updateLegacyReport();
58-
}
59-
60-
return $report->withPages();
61-
}
6251
}

src/Reporting/Chunk.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace Statamic\SeoPro\Reporting;
4+
5+
use Illuminate\Support\Facades\Cache;
6+
use Statamic\Facades\Data;
7+
use Statamic\Facades\File;
8+
use Statamic\Facades\YAML;
9+
use Statamic\SeoPro\Cascade;
10+
use Statamic\SeoPro\GetsSectionDefaults;
11+
use Statamic\SeoPro\SiteDefaults;
12+
13+
class Chunk
14+
{
15+
use GetsSectionDefaults;
16+
17+
public $id;
18+
public $contentIds;
19+
public $report;
20+
21+
public function __construct($id, $contentIds, Report $report)
22+
{
23+
$this->id = $id;
24+
$this->contentIds = $contentIds;
25+
$this->report = $report;
26+
}
27+
28+
protected function folderPath()
29+
{
30+
return $this->report->chunksFolder()."/{$this->id}";
31+
}
32+
33+
protected function yamlPath()
34+
{
35+
return $this->folderPath().'/chunk.yaml';
36+
}
37+
38+
public function save()
39+
{
40+
File::put($this->yamlPath(), YAML::dump([
41+
'ids' => $this->contentIds,
42+
]));
43+
44+
return $this;
45+
}
46+
47+
public function queueGenerate()
48+
{
49+
$ids = $this->contentIds;
50+
51+
dispatch(function () use ($ids) {
52+
$this->generate($ids);
53+
});
54+
}
55+
56+
protected function generate($ids)
57+
{
58+
$content = Cache::get($this->report->cacheKey(Report::CONTENT_CACHE_KEY_SUFFIX));
59+
60+
foreach ($ids as $id) {
61+
$this->createPage($content[$id] ?? Data::find($id))->save();
62+
}
63+
64+
File::delete($this->folderPath());
65+
66+
if ($this->wasLastChunk()) {
67+
$this->report->finalize();
68+
}
69+
}
70+
71+
protected function wasLastChunk()
72+
{
73+
return File::getFolders($this->report->chunksFolder())->isEmpty();
74+
}
75+
76+
protected function createPage($content)
77+
{
78+
if ($content->value('seo') === false || is_null($content->uri())) {
79+
return;
80+
}
81+
82+
$data = (new Cascade)
83+
->with(SiteDefaults::load()->augmented())
84+
->with($this->getAugmentedSectionDefaults($content))
85+
->with($content->augmentedValue('seo')->value())
86+
->withCurrent($content)
87+
->get();
88+
89+
return new Page($content->id(), $data, $this->report);
90+
}
91+
}

src/Reporting/Page.php

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,20 @@
55
use Statamic\Facades\Data;
66
use Statamic\Facades\File;
77
use Statamic\Facades\YAML;
8+
use Statamic\Support\Arr;
89

910
class Page
1011
{
11-
protected $report;
12+
protected $id;
1213
protected $data;
14+
protected $report;
1315
protected $results;
14-
protected $id;
1516

16-
protected $rules = [
17-
Rules\UniqueTitleTag::class,
18-
Rules\UniqueMetaDescription::class,
19-
Rules\NoUnderscoresInUrl::class,
20-
Rules\ThreeSegmentUrls::class,
21-
];
22-
23-
public function setData($data)
17+
public function __construct($id, $data, Report $report)
2418
{
25-
$this->data = collect($data);
26-
27-
return $this;
19+
$this->id = $id;
20+
$this->data = $data;
21+
$this->report = $report;
2822
}
2923

3024
public function setResults($results)
@@ -34,13 +28,6 @@ public function setResults($results)
3428
return $this;
3529
}
3630

37-
public function setReport(Report $report)
38-
{
39-
$this->report = $report;
40-
41-
return $this;
42-
}
43-
4431
public function report()
4532
{
4633
return $this->report;
@@ -75,11 +62,15 @@ public function validate()
7562

7663
public function get($key)
7764
{
78-
return $this->data->get($key);
65+
return Arr::get($this->data, $key);
7966
}
8067

8168
public function status()
8269
{
70+
if (! $this->results) {
71+
return 'pending';
72+
}
73+
8374
$status = 'pass';
8475

8576
foreach ($this->getRuleResults() as $result) {
@@ -99,6 +90,10 @@ public function getRuleResults()
9990
{
10091
$results = collect();
10192

93+
if (! $this->results) {
94+
return $results;
95+
}
96+
10297
foreach ($this->results as $class => $array) {
10398
$class = "Statamic\\SeoPro\\Reporting\\Rules\\$class";
10499
$rule = new $class;
@@ -129,18 +124,11 @@ public function id()
129124
return $this->id;
130125
}
131126

132-
public function setId($id)
133-
{
134-
$this->id = $id;
135-
136-
return $this;
137-
}
138-
139127
public function save()
140128
{
141129
$data = [
142130
'id' => $this->id,
143-
'data' => $this->data->all(),
131+
'data' => $this->data,
144132
'results' => $this->results,
145133
];
146134

0 commit comments

Comments
 (0)