Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Rebuild
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
2 / 2
9
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\References;
6
7use Celema\Quma\Database;
8use Cosray\Field;
9
10/**
11 * Rebuilds both reference indexes from scratch: wipe, then rescan all
12 * live nodes and every menu item's references (image icons, asset links,
13 * and the node a `node` or `children` item points at).
14 * Everything in the indexes is derived, so a rebuild is always safe; it
15 * is the recovery path after restores, imports, or content migrations.
16 */
17final class Rebuild
18{
19    private readonly Scanner $scanner;
20    private readonly Sync $sync;
21
22    public function __construct(
23        private readonly Database $db,
24        ?Field\Index $index = null,
25    ) {
26        $this->scanner = new Scanner($index);
27        $this->sync = new Sync($db);
28    }
29
30    /** @return array{owners: int, assets: int, nodes: int, skipped: int} */
31    public function run(): array
32    {
33        $this->db->references->wipe()->run();
34
35        $owners = 0;
36        $scanned = 0;
37
38        foreach ($this->db->references->nodeContents()->lazy() as $row) {
39            $content = json_decode((string) $row['content'], true);
40            $refs = $this->scanner->scan(is_array($content) ? $content : []);
41
42            if ($refs['assets'] === [] && $refs['nodes'] === []) {
43                continue;
44            }
45
46            $this->sync->replace('node', (string) $row['uid'], $refs);
47            $owners++;
48            $scanned += count($refs['assets']) + count($refs['nodes']);
49        }
50
51        // Merged per item before writing: `Sync::replace()` is a full replace
52        // per owner, so two passes would have the second wipe the first.
53        $menus = [];
54
55        foreach ($this->db->references->menuAssets()->lazy() as $row) {
56            $menus[(string) $row['item']]['assets'][] = (string) $row['uid'];
57        }
58
59        foreach ($this->db->references->menuNodes()->lazy() as $row) {
60            $menus[(string) $row['item']]['nodes'][] = (string) $row['uid'];
61        }
62
63        foreach ($menus as $item => $refs) {
64            $refs += ['assets' => [], 'nodes' => []];
65            $this->sync->replace('menu', (string) $item, $refs);
66            $owners++;
67            $scanned += count($refs['assets']) + count($refs['nodes']);
68        }
69
70        $counts = $this->db->references->counts()->one();
71        $assets = (int) $counts['assets'];
72        $nodes = (int) $counts['nodes'];
73
74        return [
75            'owners' => $owners,
76            'assets' => $assets,
77            'nodes' => $nodes,
78            'skipped' => $scanned - $assets - $nodes,
79        ];
80    }
81}