| @@ -306,8 +306,9 @@ | ||
| 306 | 306 | public static function onItineraryDayCreated(int $dayId, array $data): void |
| 307 | 307 | { |
| 308 | 308 | self::invalidateListingCaches(); |
| 309 | 309 | Cache::clearByPrefix('yatra_trip_'); |
| 310 | + self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), 0); | |
| 310 | 311 | Logger::info("Cache invalidated for itinerary day creation", ['day_id' => $dayId]); |
| 311 | 312 | } |
| 312 | 313 | |
| 313 | 314 | public static function onItineraryDayUpdated(int $dayId, array $data): void |
| @@ -314,8 +315,11 @@ | ||
| 314 | 315 | { |
| 315 | 316 | Cache::delete(Cache::PREFIX_TRIP_DATA . ($data['trip_id'] ?? '')); |
| 316 | 317 | self::invalidateListingCaches(); |
| 317 | 318 | Cache::clearByPrefix('yatra_trip_'); |
| 319 | + // Also blow the itinerary-prefixed cache so the day-edit page doesn't | |
| 320 | + // serve stale entries (same root cause as the activity hook below). | |
| 321 | + self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), 0); | |
| 318 | 322 | Logger::info("Cache invalidated for itinerary day update", ['day_id' => $dayId]); |
| 319 | 323 | } |
| 320 | 324 | |
| 321 | 325 | public static function onItineraryDayDeleted(int $dayId): void |
| @@ -321,8 +325,9 @@ | ||
| 321 | 325 | public static function onItineraryDayDeleted(int $dayId): void |
| 322 | 326 | { |
| 323 | 327 | self::invalidateListingCaches(); |
| 324 | 328 | Cache::clearByPrefix('yatra_trip_'); |
| 329 | + self::invalidateItineraryCaches(0, 0); | |
| 325 | 330 | Logger::info("Cache invalidated for itinerary day deletion", ['day_id' => $dayId]); |
| 326 | 331 | } |
| 327 | 332 | |
| 328 | 333 | /** |
| @@ -331,8 +336,9 @@ | ||
| 331 | 336 | public static function onItineraryActivityCreated(int $activityId, array $data): void |
| 332 | 337 | { |
| 333 | 338 | self::invalidateListingCaches(); |
| 334 | 339 | Cache::clearByPrefix('yatra_trip_'); |
| 340 | + self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), $activityId); | |
| 335 | 341 | Logger::info("Cache invalidated for itinerary activity creation", ['activity_id' => $activityId]); |
| 336 | 342 | } |
| 337 | 343 | |
| 338 | 344 | public static function onItineraryActivityUpdated(int $activityId, array $data): void |
| @@ -339,8 +345,9 @@ | ||
| 339 | 345 | { |
| 340 | 346 | Cache::delete(Cache::PREFIX_TRIP_DATA . ($data['trip_id'] ?? '')); |
| 341 | 347 | self::invalidateListingCaches(); |
| 342 | 348 | Cache::clearByPrefix('yatra_trip_'); |
| 349 | + self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), $activityId); | |
| 343 | 350 | Logger::info("Cache invalidated for itinerary activity update", ['activity_id' => $activityId]); |
| 344 | 351 | } |
| 345 | 352 | |
| 346 | 353 | public static function onItineraryActivityDeleted(int $activityId): void |
| @@ -346,7 +353,52 @@ | ||
| 346 | 353 | public static function onItineraryActivityDeleted(int $activityId): void |
| 347 | 354 | { |
| 348 | 355 | self::invalidateListingCaches(); |
| 349 | 356 | Cache::clearByPrefix('yatra_trip_'); |
| 357 | + self::invalidateItineraryCaches(0, $activityId); | |
| 350 | 358 | Logger::info("Cache invalidated for itinerary activity deletion", ['activity_id' => $activityId]); |
| 359 | + } | |
| 360 | + | |
| 361 | + /** | |
| 362 | + * Clear every cache layer that can serve stale itinerary data after a write. | |
| 363 | + * | |
| 364 | + * The day-edit page reads via `GET /trips/{id}` → TripRepository::findWithRelationsCached | |
| 365 | + * → cached under `yatra_query_trip_with_relations_<id>` (PREFIX_QUERY_RESULT). | |
| 366 | + * That entire object — including `itinerary_days[].entries[].order` — is cached | |
| 367 | + * for the trip-data TTL. Until *that* key is dropped, the React load mapper sees | |
| 368 | + * the OLD order and a drag-sort reorder appears not to persist on reload. | |
| 369 | + * | |
| 370 | + * Three prefix families touch this data; we have to hit all of them: | |
| 371 | + * 1. `yatra_query_*` (PREFIX_QUERY_RESULT) — caches `trip_with_relations_<id>`, | |
| 372 | + * `trips_with_filters_*`, etc. The actual culprit for reorder reverts. | |
| 373 | + * 2. `yatra_trip_*` (PREFIX_TRIP_DATA) — `yatra_trip_<id>` from findByIdCached. | |
| 374 | + * 3. `yatra_itinerary_*` (KEY_ITINERARY_BY_TRIP_ID + per-entry keys) — used by | |
| 375 | + * direct ItineraryRepository::getByTripId reads (admin itinerary screen). | |
| 376 | + * | |
| 377 | + * `Cache::invalidateTrip($tripId)` already covers (1) + (2); we add (3) for the | |
| 378 | + * itinerary-specific keys it doesn't know about. | |
| 379 | + * | |
| 380 | + * @param int $tripId Trip whose caches to clear (0 = unknown). | |
| 381 | + * @param int $activityId Activity row id for per-entry cache keys (0 = skip). | |
| 382 | + */ | |
| 383 | + private static function invalidateItineraryCaches(int $tripId, int $activityId): void | |
| 384 | + { | |
| 385 | + // (1) + (2): drops PREFIX_TRIP_DATA + PREFIX_QUERY_RESULT — the actual fix | |
| 386 | + // for "reorder doesn't survive reload". The React day-edit page reads from | |
| 387 | + // the trip-with-relations cache, so until this clears, save→reload returns | |
| 388 | + // the pre-reorder snapshot. | |
| 389 | + if ($tripId > 0) { | |
| 390 | + Cache::invalidateTrip($tripId); | |
| 391 | + } | |
| 392 | + | |
| 393 | + // (3): itinerary-prefixed caches that PREFIX_QUERY_RESULT doesn't include. | |
| 394 | + if ($tripId > 0) { | |
| 395 | + Cache::delete(Cache::KEY_ITINERARY_BY_TRIP_ID . '_' . $tripId); | |
| 396 | + } | |
| 397 | + Cache::clearByPrefix('yatra_itinerary_'); | |
| 398 | + | |
| 399 | + if ($activityId > 0) { | |
| 400 | + Cache::delete(Cache::KEY_ACTIVITY_ENTRY . '_' . $activityId); | |
| 401 | + Cache::delete(Cache::KEY_ITINERARY_ENTRY_WITH_RELATIONS . '_' . $activityId); | |
| 402 | + } | |
| 351 | 403 | } |
| 352 | 404 | } |