perf : getAll() sans contenu, search_index + featured, excerpts via plain (v1.6.14)

- loadArticle($dir, false) dans loadAll() — meta.json seulement, pas d'index.md
- loadAll() enrichit les articles avec plain depuis search_index (1 lecture JSON)
- rebuildSearchIndex() lit index.md directement + ajoute featured au schéma
- getSearchIndex() rebuilde automatiquement si featured absent
- post_list, author_articles, author_profile : excerpts via plain, plus de Parsedown
- Ferme #24

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-15 23:50:58 +02:00
parent c17cad9c66
commit 347e4be0b7
6 changed files with 67 additions and 32 deletions
+48 -22
View File
@@ -44,7 +44,7 @@ class ArticleManager
continue;
}
$article = $this->loadArticle($dir);
$article = $this->loadArticle($dir, false);
if (!$article) {
continue;
}
@@ -53,6 +53,19 @@ class ArticleManager
usort($articles, static fn ($a, $b) => strcmp($b['published_at'] ?? '', $a['published_at'] ?? ''));
// Enrichir avec le plain text pré-calculé (pour les excerpts sans charger index.md)
$siPath = $this->dataDir . '/search_index.json';
if (file_exists($siPath)) {
$si = json_decode((string)file_get_contents($siPath), true);
if (is_array($si)) {
$plainByUuid = array_column($si, 'plain', 'uuid');
foreach ($articles as &$a) {
$a['plain'] = $plainByUuid[$a['uuid']] ?? '';
}
unset($a);
}
}
return $articles;
}
@@ -944,19 +957,25 @@ class ArticleManager
{
$index = [];
foreach ($this->getAll() as $article) {
$uuid = $article['uuid'] ?? '';
$contentPath = $this->dataDir . '/' . $uuid . '/index.md';
$content = $uuid !== '' && file_exists($contentPath)
? (string)file_get_contents($contentPath)
: '';
$index[] = [
'uuid' => $article['uuid'],
'uuid' => $uuid,
'slug' => $article['slug'] ?? '',
'title' => $article['title'] ?? '',
'category' => $article['category'] ?? '',
'author' => $article['author'] ?? '',
'cover' => $article['cover'] ?? '',
'featured' => (bool)($article['featured'] ?? false),
'published' => $article['published'],
'published_at' => $article['published_at'] ?? '',
'created_at' => $article['created_at'] ?? '',
'updated_at' => $article['updated_at'] ?? '',
'tags' => $article['tags'] ?? [],
'plain' => $this->stripForIndex($article['content'] ?? ''),
'plain' => $this->stripForIndex($content),
];
}
file_put_contents(
@@ -1027,8 +1046,8 @@ class ArticleManager
if (!is_array($data) || empty($data)) {
return null;
}
// Rebuild automatique si le format est obsolète (champs cover/created_at absents)
if (!array_key_exists('cover', $data[0])) {
// Rebuild automatique si le format est obsolète (champs manquants)
if (!array_key_exists('cover', $data[0]) || !array_key_exists('featured', $data[0])) {
$this->rebuildSearchIndex();
return $this->searchIndexCache;
}
@@ -1200,21 +1219,22 @@ class ArticleManager
};
}
private function loadArticle(string $dir): ?array
private function loadArticle(string $dir, bool $withContent = true): ?array
{
$metaPath = $dir . '/meta.json';
$metaPath = $dir . '/meta.json';
if (!file_exists($metaPath)) {
return null;
}
$uuid = basename($dir);
$cachePath = $this->articleCachePath($uuid);
// Utiliser le cache si plus récent que meta.json ET index.md
$contentMtime = file_exists($dir . '/index.md') ? filemtime($dir . '/index.md') : 0;
if (file_exists($cachePath) && filemtime($cachePath) >= filemtime($metaPath) && filemtime($cachePath) >= $contentMtime) {
$cached = json_decode((string) file_get_contents($cachePath), true);
if (is_array($cached) && !empty($cached['uuid'])) {
return $cached;
if ($withContent) {
$uuid = basename($dir);
$cachePath = $this->articleCachePath($uuid);
$contentMtime = file_exists($dir . '/index.md') ? filemtime($dir . '/index.md') : 0;
if (file_exists($cachePath) && filemtime($cachePath) >= filemtime($metaPath) && filemtime($cachePath) >= $contentMtime) {
$cached = json_decode((string) file_get_contents($cachePath), true);
if (is_array($cached) && !empty($cached['uuid'])) {
return $cached;
}
}
}
@@ -1227,8 +1247,11 @@ class ArticleManager
return null;
}
$contentPath = $dir . '/index.md';
$meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : '';
if ($withContent) {
$contentPath = $dir . '/index.md';
$meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : '';
}
$meta['published'] = (bool)($meta['published'] ?? false);
$meta['featured'] = (bool)($meta['featured'] ?? false);
$meta['files_meta'] = $meta['files_meta'] ?? [];
@@ -1242,12 +1265,15 @@ class ArticleManager
}
}
// Écrire le cache
$cacheDir = dirname($cachePath);
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
if ($withContent) {
$uuid = $meta['uuid'];
$cachePath = $this->articleCachePath($uuid);
$cacheDir = dirname($cachePath);
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
file_put_contents($cachePath, json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
file_put_contents($cachePath, json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
return $meta;
}