# Story: PageRepository::findPublishedByTemplate() and Previous/Next Enricher Logic

## 1. Contexte (Context)

Each Actualité article's "related posts" footer must show the chronologically previous and next published Actualité article, ordered by `publishedAt` (per the finalized spec's Decision #2 — confirmed as `publishedAt`, not `menuOrder`). No existing repository method filters `Page` entities by `template`, so a new `PageRepository::findPublishedByTemplate()` method is required as the foundation for this lookup. The previous/next resolution itself lives in `ActualitePageEnricher`, which locates the current page's position in the ordered list and returns its neighbors.

This story depends on the Actualité DTO/Model story landing first, since the enricher needs the typed `ActualitePageContentModel`/`ActualiteBlockModel` classes for template-side access, and on the Tag entity story landing first, since each neighbor's card in the related-posts footer must render its own tag pills.

## 2. Le besoin (User Story)

```
En tant que visiteur du site consultant un article Actualité
Je souhaite voir un lien vers l'article précédent et l'article suivant (par ordre chronologique de publication)
Afin de naviguer facilement entre les articles d'actualité sans revenir à une liste
```

## 3. Liens utiles (Useful Links)

- **Figma**: https://www.figma.com/design/ldjPgBvJ6j2I9RVTa17H33/Galta_2026?node-id=146-1824 (section "related posts footer", `Autres news à découvrir`)
- **Spec source**: `spec-driven-development/2026-07-07-actualite-news-page-template.md`, "Decisions" #2, "Technical Requirements" (Previous/next lookup), task groups "Backend — Repository & Previous/Next logic" and "Backend — Enricher"
- **Existing precedent to mirror**:
  - `src/Repository/PageRepository.php` (existing query methods for locale-aware lookups, e.g. `findBySlugAndLocale`)
  - `AbstractPageEnricher` and `ListPageEnricher` (auto-discovery via `PageEnricherInterface` tagging)
  - `enrichMediaReferences()` convention (`str_ends_with($key, 'Image')`/`'Media'` heuristic for auto-resolving media UUIDs — relevant for `mainImage` resolution reuse)
- `src/Service/TranslationHelper.php` (must be used to resolve localized title/slug for both neighbors, same locale as current page)
- **Blocking dependencies**: Story "Actualité DTO/Model + CreationService + TemplateMatcher registration" (`stories/actualite-dto-model-creation-service.md`); Story "Tag entity + admin CRUD" (`stories/actualite-tag-entity-admin-crud.md`)

## 4. Critères d'acceptation (Acceptance Criteria)

### Scénario 1: Nouvelle méthode de repository filtrant par template
```gherkin
Scénario: findPublishedByTemplate retourne les pages publiées d'un template, triées par publishedAt
  Étant donné 3 pages de template 'actualite' publiées avec des dates `publishedAt` différentes, et 1 page 'actualite' non publiée
  Quand `PageRepository::findPublishedByTemplate('actualite')` est appelé (paramètres par défaut : `orderByField = 'publishedAt'`, `direction = 'DESC'`)
  Alors seules les 3 pages publiées sont retournées (la page non publiée est exclue)
  Et elles sont ordonnées par `publishedAt` décroissant
  Et un test d'intégration confirme ce comportement avec 3+ pages actualite
```

### Scénario 2: Résolution de l'article précédent et suivant
```gherkin
Scénario: L'enricher calcule previousPost et nextPost par position chronologique
  Étant donné 3 articles Actualité publiés, triés par publishedAt : A (plus ancien), B, C (plus récent)
  Quand `ActualitePageEnricher::enrich()` est appelé pour l'article B
  Alors `previousPost` correspond à l'article chronologiquement plus ancien (A ou C selon la convention de direction choisie, à documenter clairement dans le code)
  Et `nextPost` correspond à l'article chronologiquement plus récent restant
  Et chaque objet `previousPost`/`nextPost` inclut le titre localisé, le slug localisé, le mainImage, le publishedAt et les tags de cet article, résolus via `TranslationHelper` dans la même locale que la page courante
```

### Scénario 3: Cas limite — premier article (pas de précédent)
```gherkin
Scénario: Le premier article publié n'a pas d'article précédent
  Étant donné que l'article consulté est le plus ancien parmi les articles Actualité publiés
  Quand l'enricher calcule le voisinage
  Alors `previousPost` est `null`
  Et `nextPost` contient l'article suivant s'il existe
```

### Scénario 4: Cas limite — dernier article (pas de suivant)
```gherkin
Scénario: Le dernier article publié n'a pas d'article suivant
  Étant donné que l'article consulté est le plus récent parmi les articles Actualité publiés
  Quand l'enricher calcule le voisinage
  Alors `nextPost` est `null`
  Et `previousPost` contient l'article précédent s'il existe
```

### Scénario 5: Cas limite — article unique
```gherkin
Scénario: Un seul article Actualité publié n'a ni précédent ni suivant
  Étant donné qu'un seul article de template 'actualite' est publié
  Quand l'enricher calcule le voisinage pour cet article
  Alors `previousPost` et `nextPost` sont tous les deux `null`
  Et le rendu du template ne produit aucune erreur (0 carte affichée dans le footer "related posts")
```

### Scénario 6: Couverture de test de l'enricher
```gherkin
Scénario: Le test unitaire de l'enricher couvre tous les cas
  Étant donné `tests/Page/Actualite/Service/ActualitePageEnricherTest.php`
  Quand la suite de tests est exécutée
  Alors elle couvre : une page avec 0 bloc, une page avec N blocs de types mixtes, une page avec 0/1/plusieurs tags, le cas premier article (pas de précédent), le cas dernier article (pas de suivant)
  Et tous les tests passent sans erreur
```
