Archi perf : ne pas charger le contenu Markdown dans getAll() #24

Closed
opened 2026-05-13 22:09:39 +00:00 by cedricAbonnel · 2 comments
Owner

Problème

Dans ArticleManager::loadArticle(), le contenu Markdown (index.md) est lu et chargé dans le tableau pour chaque article, même quand seules les métadonnées sont nécessaires (page liste, slug lookup, unicité de slug).

// src/ArticleManager.php — loadArticle(), ligne ~249
$meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : '';

Sur un blog avec 50 articles de 5 Ko chacun, getAll() lit 250 Ko de Markdown inutilement à chaque chargement de la page d'accueil.

Solution proposée

Introduire un paramètre $withContent = false dans loadArticle() et getAll() :

private function loadArticle(string $dir, bool $withContent = false): ?array
{
    // ... lecture meta.json ...
    if ($withContent) {
        $meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : '';
    }
    return $meta;
}

public function getAll(bool $withContent = false): array { ... }

Seuls getByUuid() et getBySlug() (appelés depuis la vue article ou l'édition) ont besoin du contenu. La liste et les lookups de slug n'en ont pas besoin.

Impact

Appel Avant Après
Page liste (N articles) N lectures index.md 0 lectures index.md
Vue article 1 lecture index.md 1 lecture index.md (inchangé)
uniqueSlug() via getAll() N lectures index.md 0 lectures index.md

Critères d'acceptation

  • getAll() ne lit plus index.md par défaut
  • getBySlug() et getByUuid() passent withContent: true ou font une lecture ciblée
  • La page liste ne régresse pas (le contenu n'y était de toute façon pas affiché)
  • PHPStan passe sans erreur de type

Note

Ce ticket est indépendant du ticket #26 (cache de la liste) mais les deux se complètent : le cache évite les scans répétés entre requêtes, ce ticket réduit le coût de chaque scan.


Migré depuis varlog#35

## Problème Dans `ArticleManager::loadArticle()`, le contenu Markdown (`index.md`) est lu et chargé dans le tableau pour **chaque article**, même quand seules les métadonnées sont nécessaires (page liste, slug lookup, unicité de slug). ```php // src/ArticleManager.php — loadArticle(), ligne ~249 $meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : ''; ``` Sur un blog avec 50 articles de 5 Ko chacun, `getAll()` lit 250 Ko de Markdown inutilement à chaque chargement de la page d'accueil. ## Solution proposée Introduire un paramètre `$withContent = false` dans `loadArticle()` et `getAll()` : ```php private function loadArticle(string $dir, bool $withContent = false): ?array { // ... lecture meta.json ... if ($withContent) { $meta['content'] = file_exists($contentPath) ? (string)file_get_contents($contentPath) : ''; } return $meta; } public function getAll(bool $withContent = false): array { ... } ``` Seuls `getByUuid()` et `getBySlug()` (appelés depuis la vue article ou l'édition) ont besoin du contenu. La liste et les lookups de slug n'en ont pas besoin. ## Impact | Appel | Avant | Après | |---|---|---| | Page liste (N articles) | N lectures `index.md` | 0 lectures `index.md` | | Vue article | 1 lecture `index.md` | 1 lecture `index.md` (inchangé) | | `uniqueSlug()` via `getAll()` | N lectures `index.md` | 0 lectures `index.md` | ## Critères d'acceptation - [ ] `getAll()` ne lit plus `index.md` par défaut - [ ] `getBySlug()` et `getByUuid()` passent `withContent: true` ou font une lecture ciblée - [ ] La page liste ne régresse pas (le contenu n'y était de toute façon pas affiché) - [ ] PHPStan passe sans erreur de type ## Note Ce ticket est indépendant du ticket #26 (cache de la liste) mais les deux se complètent : le cache évite les scans répétés entre requêtes, ce ticket réduit le coût de chaque scan. --- *Migré depuis [varlog#35](https://git.abonnel.fr/cedricAbonnel/varlog/issues/35)*
Author
Owner

Analyse : plus complexe que prévu. getAll() est consommée par 3 templates qui rendent le contenu Markdown complet : post_list.php (excerpt), author_articles.php et author_profile.php (rendu intégral). Retirer content de getAll() casse ces trois pages sans un refactoring complet.

Pré-requis pour implémenter ce ticket :

  1. Ajouter featured au search_index.json (actuellement absent).
  2. Modifier author_articles.php et author_profile.php pour charger le contenu via getByUuid() article par article.
  3. Modifier post_list.php pour utiliser le champ plain du search_index comme excerpt.
  4. Modifier la route listing dans index.php pour préférer getSearchIndex() à getAll().

Le ticket #24 reste ouvert — la valeur est réelle mais le coût est plus élevé que prévu.

Analyse : plus complexe que prévu. `getAll()` est consommée par 3 templates qui rendent le contenu Markdown complet : `post_list.php` (excerpt), `author_articles.php` et `author_profile.php` (rendu intégral). Retirer `content` de `getAll()` casse ces trois pages sans un refactoring complet. Pré-requis pour implémenter ce ticket : 1. Ajouter `featured` au `search_index.json` (actuellement absent). 2. Modifier `author_articles.php` et `author_profile.php` pour charger le contenu via `getByUuid()` article par article. 3. Modifier `post_list.php` pour utiliser le champ `plain` du search_index comme excerpt. 4. Modifier la route listing dans `index.php` pour préférer `getSearchIndex()` à `getAll()`. Le ticket #24 reste ouvert — la valeur est réelle mais le coût est plus élevé que prévu.
Author
Owner

Implémenté :

  • loadArticle(string $dir, bool $withContent = true) — quand false, lit uniquement meta.json, sans cache article
  • loadAll() appelle loadArticle($dir, false) puis enrichit chaque article du champ plain depuis search_index.json (1 seule lecture JSON)
  • rebuildSearchIndex() lit index.md directement par UUID — plus de dépendance à $article['content'] dans getAll()
  • search_index.json reçoit le champ featured ; getSearchIndex() déclenche un rebuild si absent
  • post_list.php, author_articles.php, author_profile.php : excerpts via $post['plain'] — Parsedown supprimé de ces templates

Résultat : getAll() lit N meta.json + 1 search_index.json au lieu de N caches article (qui incluaient le contenu).

Implémenté : - `loadArticle(string $dir, bool $withContent = true)` — quand `false`, lit uniquement `meta.json`, sans cache article - `loadAll()` appelle `loadArticle($dir, false)` puis enrichit chaque article du champ `plain` depuis `search_index.json` (1 seule lecture JSON) - `rebuildSearchIndex()` lit `index.md` directement par UUID — plus de dépendance à `$article['content']` dans getAll() - `search_index.json` reçoit le champ `featured` ; `getSearchIndex()` déclenche un rebuild si absent - `post_list.php`, `author_articles.php`, `author_profile.php` : excerpts via `$post['plain']` — Parsedown supprimé de ces templates Résultat : `getAll()` lit N `meta.json` + 1 `search_index.json` au lieu de N caches article (qui incluaient le contenu).
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: cedricAbonnel/folio#24