perf: cache fichier pour les rétroliens, invalidé sur create/update/delete/link
This commit is contained in:
+73
-19
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user