$tripId]); } /** * Get cached trip */ public static function getCachedTrip(int $tripId): ?\stdClass { return Cache::get(Cache::PREFIX_TRIP_DATA . $tripId); } /** * Cache booking data */ public static function cacheBooking(int $bookingId, \stdClass $bookingData): void { Cache::set( Cache::PREFIX_BOOKING_DATA . $bookingId, $bookingData, Cache::DURATION_BOOKING_DATA ); Logger::debug("Booking cached", ['booking_id' => $bookingId]); } /** * Get cached booking */ public static function getCachedBooking(int $bookingId): ?\stdClass { return Cache::get(Cache::PREFIX_BOOKING_DATA . $bookingId); } /** * Cache customer data */ public static function cacheCustomer(int $customerId, \stdClass $customerData): void { Cache::set( Cache::PREFIX_CUSTOMER_DATA . $customerId, $customerData, Cache::DURATION_CUSTOMER_DATA ); Logger::debug("Customer cached", ['customer_id' => $customerId]); } /** * Get cached customer */ public static function getCachedCustomer(int $customerId): ?\stdClass { return Cache::get(Cache::PREFIX_CUSTOMER_DATA . $customerId); } /** * Cache activity data */ public static function cacheActivity(int $activityId, \stdClass $activityData): void { Cache::set( Cache::PREFIX_ACTIVITY_DATA . $activityId, $activityData, Cache::DURATION_ACTIVITY_DATA ); Logger::debug("Activity cached", ['activity_id' => $activityId]); } /** * Get cached activity */ public static function getCachedActivity(int $activityId): ?\stdClass { return Cache::get(Cache::PREFIX_ACTIVITY_DATA . $activityId); } /** * Cache destination data */ public static function cacheDestination(int $destinationId, \stdClass $destinationData): void { Cache::set( Cache::PREFIX_DESTINATION_DATA . $destinationId, $destinationData, Cache::DURATION_DESTINATION_DATA ); Logger::debug("Destination cached", ['destination_id' => $destinationId]); } /** * Get cached destination */ public static function getCachedDestination(int $destinationId): ?\stdClass { return Cache::get(Cache::PREFIX_DESTINATION_DATA . $destinationId); } /** * Clear a single trip and listing/query caches (e.g. after attribute changes via {@see TripService}). */ public static function clearTripCache(int $tripId): void { if ($tripId <= 0) { return; } Cache::invalidateAfterTripWrite('update', $tripId); } /** * Invalidate entity cache when data changes */ public static function invalidateEntity(string $entityType, int $entityId): void { // Handle both entity types and service class names $normalizedType = self::normalizeEntityType($entityType); switch ($normalizedType) { case 'trip': $prefix = Cache::PREFIX_TRIP_DATA; break; case 'booking': $prefix = Cache::PREFIX_BOOKING_DATA; break; case 'customer': $prefix = Cache::PREFIX_CUSTOMER_DATA; break; case 'activity': $prefix = Cache::PREFIX_ACTIVITY_DATA; break; case 'destination': $prefix = Cache::PREFIX_DESTINATION_DATA; break; case 'difficulty': $prefix = Cache::PREFIX_QUERY_RESULT; // For difficulty levels, use query cache break; default: $prefix = null; break; } if ($prefix) { if ($normalizedType === 'difficulty') { // For difficulty and other non-entity types, clear by prefix Cache::clearByPrefix($prefix); } else { // For entities, clear specific entity cache Cache::delete($prefix . $entityId); } // Also clear related query caches Cache::clearByPrefix(Cache::PREFIX_QUERY_RESULT); Cache::clearByPrefix(Cache::PREFIX_STATS); Logger::info("Entity cache invalidated", [ 'entity_type' => $entityType, 'normalized_type' => $normalizedType, 'entity_id' => $entityId ]); } else { // If no specific prefix found, just clear query caches Cache::clearByPrefix(Cache::PREFIX_QUERY_RESULT); Cache::clearByPrefix(Cache::PREFIX_STATS); Logger::debug("Generic cache invalidation", [ 'entity_type' => $entityType, 'entity_id' => $entityId ]); } } /** * Normalize entity type from service class names */ private static function normalizeEntityType(string $entityType): string { // Convert service class names to entity types $entityType = strtolower($entityType); // Handle service class patterns if (str_contains($entityType, 'trip')) { return 'trip'; } if (str_contains($entityType, 'booking')) { return 'booking'; } if (str_contains($entityType, 'customer')) { return 'customer'; } if (str_contains($entityType, 'activity')) { return 'activity'; } if (str_contains($entityType, 'destination')) { return 'destination'; } if (str_contains($entityType, 'difficulty')) { return 'difficulty'; } // Return as-is if no pattern matches return $entityType; } /** * Warm up cache for frequently accessed entities */ public static function warmUpCache(): void { Logger::info("Starting cache warm-up"); // Warm up popular trips $tripRepository = new \Yatra\Repositories\TripRepository(); $popularTrips = $tripRepository->getPopularTrips(20); foreach ($popularTrips as $trip) { // This would trigger cache population when the trip is next accessed Logger::debug("Marked trip for cache warm-up", ['trip_id' => $trip->id]); } // Warm up recent bookings $bookingRepository = new \Yatra\Repositories\BookingRepository(); $recentBookings = $bookingRepository->getRecentBookings(7, 50); foreach ($recentBookings as $booking) { Logger::debug("Marked booking for cache warm-up", ['booking_id' => $booking->id]); } Logger::info("Cache warm-up completed", [ 'trips_marked' => count($popularTrips), 'bookings_marked' => count($recentBookings) ]); } /** * Get cache performance metrics */ public static function getPerformanceMetrics(): array { return [ 'cache_stats' => Cache::getCacheStats(), 'memory_usage' => [ 'current' => memory_get_usage(true), 'peak' => memory_get_peak_usage(true), 'formatted_current' => self::formatBytes(memory_get_usage(true)), 'formatted_peak' => self::formatBytes(memory_get_peak_usage(true)), ], 'cache_backends' => Cache::getAvailableBackends(), ]; } /** * Format bytes for human reading */ private static function formatBytes(int $bytes): string { $units = ['B', 'KB', 'MB', 'GB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1 << (10 * $pow)); return round($bytes, 2) . ' ' . $units[$pow]; } /** * Remember pattern - get from cache or execute callback and cache result * * @return mixed */ public static function remember(string $key, callable $callback, ?int $duration = null) { // Try to get from cache first $cached = Cache::get($key); if ($cached !== null) { Logger::debug("Cache hit", ['key' => $key]); return $cached; } // Execute callback to get fresh data $startTime = microtime(true); $result = $callback(); $executionTime = microtime(true) - $startTime; // Cache the result if it's not null if ($result !== null) { $cacheDuration = $duration ?? Cache::DURATION_QUERY_RESULT; Cache::set($key, $result, $cacheDuration); Logger::debug("Cache miss - data cached", [ 'key' => $key, 'execution_time' => round($executionTime * 1000, 2) . 'ms', 'duration' => $cacheDuration ]); } else { Logger::debug("Cache miss - null result not cached", [ 'key' => $key, 'execution_time' => round($executionTime * 1000, 2) . 'ms' ]); } return $result; } /** * Clear cache by prefix - delegates to Cache utility */ public static function clearByPrefix(string $prefix): void { Cache::clearByPrefix($prefix); Logger::debug("Cache cleared by prefix", ['prefix' => $prefix]); } /** * Clear all entity caches */ public static function clearAllEntityCaches(): void { $prefixes = [ Cache::PREFIX_TRIP_DATA, Cache::PREFIX_BOOKING_DATA, Cache::PREFIX_CUSTOMER_DATA, Cache::PREFIX_ACTIVITY_DATA, Cache::PREFIX_DESTINATION_DATA, ]; foreach ($prefixes as $prefix) { Cache::clearByPrefix($prefix); } Logger::info("All entity caches cleared"); } }