Ajout du système de cache pour les notifications

Problème : Les notifications étaient collectées à chaque requête sur
projects.json, causant des problèmes de performance et de mémoire.

Solution : Mise en cache des notifications par projet et par utilisateur
- Nouvelle méthode getNotificationsLight() dans ProjectPage avec cache
- Cache invalidé automatiquement via les hooks existants (page/file update)
- Cache par utilisateur pour inclure le isRead spécifique

Performance : Les notifications sont calculées une fois puis servies depuis
le cache jusqu'à ce qu'un changement survienne (commentaire, brief, etc.)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-01-15 11:42:20 +01:00
parent e73e25b1da
commit bb71da081b
3 changed files with 62 additions and 20 deletions

View file

@ -1,9 +1,11 @@
<?php <?php
// page.update:after && page.changeStatus:after // page.update:after && page.changeStatus:after
return function($newPage, $oldPage) { return function($newPage, $oldPage) {
$project = $newPage->template() == 'project' ? $newPage : $newPage->parents()->findBy('template', 'project'); $project = $newPage->template() == 'project' ? $newPage : $newPage->parents()->findBy('template', 'project');
if ($project) { if ($project) {
$steps = $project->rebuildStepsCache(); $project->rebuildStepsCache();
// Invalider aussi le cache des notifications (briefs validés, etc.)
$project->invalidateNotificationsCache();
} }
}; };

View file

@ -3,18 +3,62 @@
use adrienpayet\notifications\NotificationsPage; use adrienpayet\notifications\NotificationsPage;
class ProjectPage extends NotificationsPage { class ProjectPage extends NotificationsPage {
public function getSteps() { public function getSteps() {
$apiCache = kirby()->cache('api'); $apiCache = kirby()->cache('api');
$stepsData = $apiCache?->get($this->slug() . '_' . 'steps'); $stepsData = $apiCache?->get($this->slug() . '_' . 'steps');
if ($stepsData === null || count($stepsData) === 0) { if ($stepsData === null || count($stepsData) === 0) {
$this->rebuildStepsCache(); $this->rebuildStepsCache();
}; };
$stepsData = $apiCache->get($this->slug() . '_' . 'steps'); $stepsData = $apiCache->get($this->slug() . '_' . 'steps');
return $stepsData; return $stepsData;
} }
/**
* Récupère les notifications pour ce projet (version allégée avec cache).
* Cache par utilisateur pour inclure le isRead.
*/
public function getNotificationsLight($user) {
if (!$user) {
return [];
}
$apiCache = kirby()->cache('api');
$cacheKey = $this->slug() . '_notifications_' . $user->uuid();
$notifications = $apiCache?->get($cacheKey);
// Si pas en cache, collecter et cacher
if ($notifications === null) {
$collector = kirby()->option('adrienpayet.pdc-notifications.collector');
if (!$collector) {
return [];
}
try {
$notifications = $collector->collectLight($this, $user);
$apiCache->set($cacheKey, $notifications);
} catch (\Throwable $e) {
error_log("Error caching notifications for {$this->slug()}: " . $e->getMessage());
return [];
}
}
return $notifications;
}
/**
* Invalide le cache des notifications de ce projet pour tous les utilisateurs.
*/
public function invalidateNotificationsCache() {
$apiCache = kirby()->cache('api');
// Invalider pour tous les users
foreach (kirby()->users() as $user) {
$cacheKey = $this->slug() . '_notifications_' . $user->uuid();
$apiCache->remove($cacheKey);
}
}
public function rebuildStepsCache() { public function rebuildStepsCache() {
// Create steps // Create steps

View file

@ -7,17 +7,15 @@ if (!$kirby->user()) {
]); ]);
} }
function getProjectData($project, $user, $collector) function getProjectData($project, $user)
{ {
// Utiliser collectLight() pour économiser la mémoire (seulement id, type, isRead, date) // Utiliser getNotificationsLight() avec cache pour optimiser les performances
$notifications = []; $notifications = [];
if ($collector) { try {
try { $notifications = $project->getNotificationsLight($user);
$notifications = $collector->collectLight($project, $user); } catch (\Throwable $e) {
} catch (\Throwable $e) { error_log("Error getting notifications for project {$project->uri()}: " . $e->getMessage());
error_log("Error collecting light notifications for project {$project->uri()}: " . $e->getMessage()); $notifications = [];
$notifications = [];
}
} }
$data = [ $data = [
@ -43,14 +41,12 @@ function getProjectData($project, $user, $collector)
return $data; return $data;
} }
// Récupérer le collector de notifications
$notificationCollector = $kirby->option('adrienpayet.pdc-notifications.collector');
$currentUser = $kirby->user(); $currentUser = $kirby->user();
try { try {
$children = $currentUser->role() == 'admin' $children = $currentUser->role() == 'admin'
? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values() ? $page->childrenAndDrafts()->map(fn($project) => getProjectData($project, $currentUser))->values()
: $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser, $notificationCollector))->values(); : $currentUser->projects()->toPages()->map(fn($project) => getProjectData($project, $currentUser))->values();
} catch (\Throwable $th) { } catch (\Throwable $th) {
throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1); throw new Exception($th->getMessage() . ' line ' . $th->getLine() . ' in file ' . $th->getFile(), 1);
$children = []; $children = [];