perf: cache fichier pour les rétroliens, invalidé sur create/update/delete/link

This commit is contained in:
Cedric Abonnel
2026-05-12 23:25:31 +02:00
parent 288a620788
commit 668a50e714
+73 -19
View File
@@ -117,6 +117,7 @@ class ArticleManager
$this->writeMeta($dir, $meta); $this->writeMeta($dir, $meta);
file_put_contents($dir . '/index.md', ltrim($content)); file_put_contents($dir . '/index.md', ltrim($content));
$this->rebuildSearchIndex(); $this->rebuildSearchIndex();
$this->rebuildBacklinksCache();
return $uuid; return $uuid;
} }
@@ -181,6 +182,7 @@ class ArticleManager
$this->writeMeta($dir, $meta); $this->writeMeta($dir, $meta);
file_put_contents($dir . '/index.md', ltrim($content)); file_put_contents($dir . '/index.md', ltrim($content));
$this->rebuildSearchIndex(); $this->rebuildSearchIndex();
$this->rebuildBacklinksCache();
} }
public function autosave(string $uuid, string $title, string $content, string $slug): bool public function autosave(string $uuid, string $title, string $content, string $slug): bool
@@ -414,6 +416,7 @@ class ArticleManager
} }
$meta['external_links'][] = $entry; $meta['external_links'][] = $entry;
$this->writeMeta($dir, $meta); $this->writeMeta($dir, $meta);
$this->rebuildBacklinksCache();
return true; return true;
} }
@@ -466,6 +469,7 @@ class ArticleManager
static fn ($l) => $l['url'] !== $url static fn ($l) => $l['url'] !== $url
)); ));
$this->writeMeta($dir, $meta); $this->writeMeta($dir, $meta);
$this->rebuildBacklinksCache();
return true; return true;
} }
@@ -547,6 +551,75 @@ class ArticleManager
$this->removeDir($dir); $this->removeDir($dir);
} }
$this->rebuildSearchIndex(); $this->rebuildSearchIndex();
$this->rebuildBacklinksCache();
}
// ------------------------------------------------------------------ //
// Cache des rétroliens
// ------------------------------------------------------------------ //
private function backlinksPath(): string
{
return $this->dataDir . '/_cache/backlinks.json';
}
/**
* Reconstruit le cache des rétroliens.
* Produit un index slug → [article minimal, ...] pour tous les articles publiés
* qui pointent vers un autre article interne via leurs external_links.
*/
public function rebuildBacklinksCache(): void
{
$cacheDir = $this->dataDir . '/_cache';
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
$index = [];
foreach ($this->getAll(publishedOnly: true) as $article) {
foreach ($article['external_links'] ?? [] as $link) {
$path = rtrim(parse_url($link['url'] ?? '', PHP_URL_PATH) ?? '', '/');
if (!preg_match('#^/post/([a-z0-9][a-z0-9-]*)$#', $path, $m)) {
continue;
}
$target = $m[1];
$index[$target][] = [
'uuid' => $article['uuid'],
'slug' => $article['slug'] ?? '',
'title' => $article['title'] ?? '',
'cover' => $article['cover'] ?? '',
'category' => $article['category'] ?? '',
'published_at' => $article['published_at'] ?? '',
'created_at' => $article['created_at'] ?? '',
];
}
}
file_put_contents(
$this->backlinksPath(),
json_encode($index, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
);
}
/**
* Retourne les articles qui pointent vers /post/<slug>, depuis le cache.
* Reconstruit le cache si absent.
*/
public function getBacklinks(string $slug, string $excludeUuid = ''): array
{
$path = $this->backlinksPath();
if (!file_exists($path)) {
$this->rebuildBacklinksCache();
}
$index = json_decode((string) file_get_contents($path), true);
if (!is_array($index)) {
return [];
}
$result = $index[$slug] ?? [];
if ($excludeUuid !== '') {
$result = array_values(array_filter($result, static fn ($a) => $a['uuid'] !== $excludeUuid));
}
return $result;
} }
// ------------------------------------------------------------------ // // ------------------------------------------------------------------ //
@@ -887,25 +960,6 @@ class ArticleManager
/** /**
* Retourne les articles publiés qui contiennent un lien vers /post/<slug>. * Retourne les articles publiés qui contiennent un lien vers /post/<slug>.
*/ */
public function getBacklinks(string $slug, string $excludeUuid = ''): array
{
$pattern = '/post/' . $slug;
$result = [];
foreach ($this->getAll(publishedOnly: true) as $article) {
if ($excludeUuid !== '' && $article['uuid'] === $excludeUuid) {
continue;
}
foreach ($article['external_links'] ?? [] as $link) {
$path = parse_url($link['url'] ?? '', PHP_URL_PATH) ?? '';
if ($path === $pattern || rtrim($path, '/') === $pattern) {
$result[] = $article;
break;
}
}
}
return $result;
}
private function removeDir(string $dir): void private function removeDir(string $dir): void
{ {
foreach (scandir($dir) as $entry) { foreach (scandir($dir) as $entry) {