PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / app / Hooks / CacheHooks.php

CacheHooks.php in Yatra – Travel Booking & Tour Operator Software trunk, at app/Hooks/CacheHooks.php

405 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Hooks;
6
7 use Yatra\Services\CacheService;
8 use Yatra\Utils\Cache;
9 use Yatra\Utils\Logger;
10
11 /**
12 * Cache Invalidation Hooks
13 *
14 * Automatically invalidate caches when data changes
15 */
16 class CacheHooks
17 {
18 /**
19 * Initialize cache hooks
20 */
21 public static function init(): void
22 {
23 // Trip-related hooks
24 add_action('yatra_trip_created', [self::class, 'onTripCreated'], 10, 1);
25 add_action('yatra_trip_updated', [self::class, 'onTripUpdated'], 10, 1);
26 add_action('yatra_trip_deleted', [self::class, 'onTripDeleted'], 10, 1);
27
28 // Booking-related hooks
29 add_action('yatra_booking_created', [self::class, 'onBookingCreated'], 10, 1);
30 add_action('yatra_booking_updated', [self::class, 'onBookingUpdated'], 10, 1);
31 add_action('yatra_booking_deleted', [self::class, 'onBookingDeleted'], 10, 1);
32 add_action('yatra_booking_status_changed', [self::class, 'onBookingStatusChanged'], 10, 3);
33
34 // Customer-related hooks
35 add_action('yatra_customer_created', [self::class, 'onCustomerCreated'], 10, 1);
36 add_action('yatra_customer_updated', [self::class, 'onCustomerUpdated'], 10, 1);
37 add_action('yatra_customer_deleted', [self::class, 'onCustomerDeleted'], 10, 1);
38
39 // Activity-related hooks
40 add_action('yatra_activity_created', [self::class, 'onActivityCreated'], 10, 1);
41 add_action('yatra_activity_updated', [self::class, 'onActivityUpdated'], 10, 1);
42 add_action('yatra_activity_deleted', [self::class, 'onActivityDeleted'], 10, 1);
43
44 // Itinerary-specific hooks
45 add_action('yatra_itinerary_day_created', [self::class, 'onItineraryDayCreated'], 10, 2);
46 add_action('yatra_itinerary_day_updated', [self::class, 'onItineraryDayUpdated'], 10, 2);
47 add_action('yatra_itinerary_day_deleted', [self::class, 'onItineraryDayDeleted'], 10, 1);
48 add_action('yatra_itinerary_activity_created', [self::class, 'onItineraryActivityCreated'], 10, 2);
49 add_action('yatra_itinerary_activity_updated', [self::class, 'onItineraryActivityUpdated'], 10, 2);
50 add_action('yatra_itinerary_activity_deleted', [self::class, 'onItineraryActivityDeleted'], 10, 1);
51
52 // Destination-related hooks
53 add_action('yatra_destination_created', [self::class, 'onDestinationCreated'], 10, 1);
54 add_action('yatra_destination_updated', [self::class, 'onDestinationUpdated'], 10, 1);
55 add_action('yatra_destination_deleted', [self::class, 'onDestinationDeleted'], 10, 1);
56
57 // Payment-related hooks
58 add_action('yatra_payment_created', [self::class, 'onPaymentCreated'], 10, 1);
59 add_action('yatra_payment_updated', [self::class, 'onPaymentUpdated'], 10, 1);
60
61 // General cache management hooks
62 add_action('yatra_cache_warm_up', [self::class, 'warmUpCache']);
63 add_action('yatra_cache_clear_all', [self::class, 'clearAllCaches']);
64
65 // WordPress hooks for related data changes
66 add_action('wp_insert_post', [self::class, 'onPostChange'], 10, 2);
67 add_action('wp_update_post', [self::class, 'onPostChange'], 10, 2);
68 add_action('wp_delete_post', [self::class, 'onPostDeleted'], 10, 1);
69
70 // Schedule cache cleanup
71 if (!wp_next_scheduled('yatra_cache_cleanup')) {
72 wp_schedule_event(time(), 'daily', 'yatra_cache_cleanup');
73 }
74 add_action('yatra_cache_cleanup', [self::class, 'cleanupExpiredCache']);
75 }
76
77 /**
78 * Trip event handlers
79 */
80 public static function onTripCreated(int $tripId): void
81 {
82 Cache::invalidateAfterTripWrite('create', $tripId);
83 Logger::info("Cache invalidated for trip creation", ['trip_id' => $tripId]);
84 }
85
86 public static function onTripUpdated(int $tripId): void
87 {
88 Cache::invalidateAfterTripWrite('update', $tripId);
89 Logger::info("Cache invalidated for trip update", ['trip_id' => $tripId]);
90 }
91
92 public static function onTripDeleted(int $tripId): void
93 {
94 Cache::invalidateAfterTripWrite('delete', $tripId);
95 Logger::info("Cache invalidated for trip deletion", ['trip_id' => $tripId]);
96 }
97
98 /**
99 * Booking event handlers
100 */
101 public static function onBookingCreated(int $bookingId): void
102 {
103 Cache::invalidateAfterBookingWrite($bookingId);
104 Logger::info("Cache invalidated for booking creation", ['booking_id' => $bookingId]);
105 }
106
107 public static function onBookingUpdated(int $bookingId): void
108 {
109 Cache::invalidateAfterBookingWrite($bookingId);
110 Logger::info("Cache invalidated for booking update", ['booking_id' => $bookingId]);
111 }
112
113 public static function onBookingDeleted(int $bookingId): void
114 {
115 Cache::invalidateAfterBookingWrite($bookingId);
116 Logger::info("Cache invalidated for booking deletion", ['booking_id' => $bookingId]);
117 }
118
119 public static function onBookingStatusChanged(int $bookingId, string $oldStatus, string $newStatus): void
120 {
121 Cache::invalidateAfterBookingWrite($bookingId);
122 Logger::info("Cache invalidated for booking status change", [
123 'booking_id' => $bookingId,
124 'old_status' => $oldStatus,
125 'new_status' => $newStatus,
126 ]);
127 }
128
129 /**
130 * Customer event handlers
131 */
132 public static function onCustomerCreated(int $customerId): void
133 {
134 self::invalidateStatsCaches();
135 Logger::info("Cache invalidated for customer creation", ['customer_id' => $customerId]);
136 }
137
138 public static function onCustomerUpdated(int $customerId): void
139 {
140 CacheService::invalidateEntity('customer', $customerId);
141 Logger::info("Cache invalidated for customer update", ['customer_id' => $customerId]);
142 }
143
144 public static function onCustomerDeleted(int $customerId): void
145 {
146 CacheService::invalidateEntity('customer', $customerId);
147 self::invalidateStatsCaches();
148 Logger::info("Cache invalidated for customer deletion", ['customer_id' => $customerId]);
149 }
150
151 /**
152 * Activity event handlers
153 */
154 public static function onActivityCreated(int $activityId): void
155 {
156 self::invalidateListingCaches();
157 Logger::info("Cache invalidated for activity creation", ['activity_id' => $activityId]);
158 }
159
160 public static function onActivityUpdated(int $activityId): void
161 {
162 CacheService::invalidateEntity('activity', $activityId);
163 self::invalidateListingCaches();
164 Logger::info("Cache invalidated for activity update", ['activity_id' => $activityId]);
165 }
166
167 public static function onActivityDeleted(int $activityId): void
168 {
169 CacheService::invalidateEntity('activity', $activityId);
170 self::invalidateListingCaches();
171 Logger::info("Cache invalidated for activity deletion", ['activity_id' => $activityId]);
172 }
173
174 /**
175 * Destination event handlers
176 */
177 public static function onDestinationCreated(int $destinationId): void
178 {
179 self::invalidateListingCaches();
180 Logger::info("Cache invalidated for destination creation", ['destination_id' => $destinationId]);
181 }
182
183 public static function onDestinationUpdated(int $destinationId): void
184 {
185 CacheService::invalidateEntity('destination', $destinationId);
186 self::invalidateListingCaches();
187 Logger::info("Cache invalidated for destination update", ['destination_id' => $destinationId]);
188 }
189
190 public static function onDestinationDeleted(int $destinationId): void
191 {
192 CacheService::invalidateEntity('destination', $destinationId);
193 self::invalidateListingCaches();
194 Logger::info("Cache invalidated for destination deletion", ['destination_id' => $destinationId]);
195 }
196
197 /**
198 * Payment event handlers
199 */
200 public static function onPaymentCreated(int $paymentId): void
201 {
202 self::invalidateStatsCaches();
203 Logger::info("Cache invalidated for payment creation", ['payment_id' => $paymentId]);
204 }
205
206 public static function onPaymentUpdated(int $paymentId): void
207 {
208 self::invalidateStatsCaches();
209 Logger::info("Cache invalidated for payment update", ['payment_id' => $paymentId]);
210 }
211
212 /**
213 * WordPress post change handler
214 */
215 public static function onPostChange(int $postId, \WP_Post $post): void
216 {
217 // Only handle Yatra-related post types
218 if (in_array($post->post_type, ['yatra_trip', 'yatra_booking', 'yatra_customer'])) {
219 self::invalidateListingCaches();
220 self::invalidateStatsCaches();
221 Logger::debug("Cache invalidated for WordPress post change", [
222 'post_id' => $postId,
223 'post_type' => $post->post_type
224 ]);
225 }
226 }
227
228 /**
229 * WordPress post deletion handler
230 */
231 public static function onPostDeleted(int $postId): void
232 {
233 // Clear all caches as we don't know the post type
234 self::invalidateListingCaches();
235 self::invalidateStatsCaches();
236 Logger::debug("Cache invalidated for WordPress post deletion", ['post_id' => $postId]);
237 }
238
239 /**
240 * Cache management handlers
241 */
242 public static function warmUpCache(): void
243 {
244 CacheService::warmUpCache();
245 Logger::info("Cache warm-up completed");
246 }
247
248 public static function clearAllCaches(): void
249 {
250 Cache::flush();
251 Logger::info("All caches cleared via hook");
252 }
253
254 /**
255 * Clean up expired cache entries
256 */
257 public static function cleanupExpiredCache(): void
258 {
259 // This would be implemented based on the cache backend
260 // For now, we'll just log the cleanup attempt
261 Logger::info("Cache cleanup scheduled task executed");
262
263 // Clear old log files
264 Logger::cleanup();
265 }
266
267 /**
268 * Helper methods for cache invalidation
269 */
270 private static function invalidateListingCaches(): void
271 {
272 Cache::invalidateListingCaches();
273 }
274
275 private static function invalidateStatsCaches(): void
276 {
277 Cache::clearByPrefix(Cache::PREFIX_STATS);
278 Cache::invalidateDashboardReportCaches();
279 }
280
281 /**
282 * Manual cache invalidation for admin use
283 */
284 public static function invalidateAll(): void
285 {
286 Cache::flush();
287 Logger::info("Manual cache invalidation triggered");
288 }
289
290 /**
291 * Get cache status for admin dashboard
292 */
293 public static function getCacheStatus(): array
294 {
295 return [
296 'enabled' => true,
297 'backends' => Cache::getAvailableBackends(),
298 'stats' => Cache::getCacheStats(),
299 'performance' => CacheService::getPerformanceMetrics(),
300 ];
301 }
302
303 /**
304 * Itinerary Day event handlers
305 */
306 public static function onItineraryDayCreated(int $dayId, array $data): void
307 {
308 self::invalidateListingCaches();
309 Cache::clearByPrefix('yatra_trip_');
310 self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), 0);
311 Logger::info("Cache invalidated for itinerary day creation", ['day_id' => $dayId]);
312 }
313
314 public static function onItineraryDayUpdated(int $dayId, array $data): void
315 {
316 Cache::delete(Cache::PREFIX_TRIP_DATA . ($data['trip_id'] ?? ''));
317 self::invalidateListingCaches();
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);
322 Logger::info("Cache invalidated for itinerary day update", ['day_id' => $dayId]);
323 }
324
325 public static function onItineraryDayDeleted(int $dayId): void
326 {
327 self::invalidateListingCaches();
328 Cache::clearByPrefix('yatra_trip_');
329 self::invalidateItineraryCaches(0, 0);
330 Logger::info("Cache invalidated for itinerary day deletion", ['day_id' => $dayId]);
331 }
332
333 /**
334 * Itinerary Activity event handlers
335 */
336 public static function onItineraryActivityCreated(int $activityId, array $data): void
337 {
338 self::invalidateListingCaches();
339 Cache::clearByPrefix('yatra_trip_');
340 self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), $activityId);
341 Logger::info("Cache invalidated for itinerary activity creation", ['activity_id' => $activityId]);
342 }
343
344 public static function onItineraryActivityUpdated(int $activityId, array $data): void
345 {
346 Cache::delete(Cache::PREFIX_TRIP_DATA . ($data['trip_id'] ?? ''));
347 self::invalidateListingCaches();
348 Cache::clearByPrefix('yatra_trip_');
349 self::invalidateItineraryCaches((int) ($data['trip_id'] ?? 0), $activityId);
350 Logger::info("Cache invalidated for itinerary activity update", ['activity_id' => $activityId]);
351 }
352
353 public static function onItineraryActivityDeleted(int $activityId): void
354 {
355 self::invalidateListingCaches();
356 Cache::clearByPrefix('yatra_trip_');
357 self::invalidateItineraryCaches(0, $activityId);
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 }
403 }
404 }
405