Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
84.00% |
21 / 25 |
|
69.23% |
9 / 13 |
CRAP | |
0.00% |
0 / 1 |
| CacheService | |
84.00% |
21 / 25 |
|
69.23% |
9 / 13 |
20.48 | |
0.00% |
0 / 1 |
| keyProduct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| keyQuickView | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| keyCategoryProducts | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| remember | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| forgetProduct | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
5.05 | |||
| forgetProductsList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| forgetCategory | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| forgetSetting | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| forgetSocialSettings | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| forgetAppSettings | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| forgetMenusBuilder | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| forgetWidgets | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| forgetThemes | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Service; |
| 4 | |
| 5 | use Illuminate\Support\Facades\Cache; |
| 6 | |
| 7 | /** |
| 8 | * Central place for every storefront cache key this app uses, plus the |
| 9 | * invalidation logic that runs whenever the underlying data changes. |
| 10 | * |
| 11 | * IMPORTANT: CACHE_DRIVER in .env is "file", which does NOT support cache |
| 12 | * tags (Cache::tags() only works on the redis/memcached drivers). So instead |
| 13 | * of tags, every cacheable query gets one well-known key here, and the |
| 14 | * model hooks in Product/ProductVariation/Category/Setting/SocialSetting |
| 15 | * explicitly forget the exact keys that could have changed — the moment a |
| 16 | * row is created, updated, or deleted (see each model's boot() method). |
| 17 | * |
| 18 | * The TTL below is only a safety net in case some write path is ever missed |
| 19 | * (e.g. a raw DB::table()->update() that bypasses Eloquent events) — under |
| 20 | * normal use the cache is refreshed immediately by the model hooks, not by |
| 21 | * waiting for expiry. |
| 22 | */ |
| 23 | class CacheService |
| 24 | { |
| 25 | const TTL = 21600; // 6 hours |
| 26 | |
| 27 | // ── Key names ────────────────────────────────────────────────────── |
| 28 | const KEY_MENUS = 'spos:menus'; |
| 29 | |
| 30 | const KEY_MENUS_BUILDER = 'spos:menus_builder'; |
| 31 | |
| 32 | const KEY_WIDGETS = 'spos:widgets'; |
| 33 | |
| 34 | const KEY_THEMES = 'spos:themes'; |
| 35 | |
| 36 | const KEY_CATEGORIES = 'spos:categories'; |
| 37 | |
| 38 | const KEY_PRODUCTS_ALL = 'spos:products_all'; |
| 39 | |
| 40 | const KEY_SETTING = 'spos:setting'; |
| 41 | |
| 42 | const KEY_SOCIAL_SETTINGS = 'spos:social_settings'; |
| 43 | |
| 44 | const KEY_APP_SETTINGS = 'spos:app_settings'; |
| 45 | |
| 46 | const KEY_COLORS = 'spos:colors'; |
| 47 | |
| 48 | const KEY_SIZES = 'spos:sizes'; |
| 49 | |
| 50 | const KEY_BRANDS = 'spos:brands'; |
| 51 | |
| 52 | const KEY_SECTIONS = 'spos:sections'; |
| 53 | |
| 54 | public static function keyProduct($id): string |
| 55 | { |
| 56 | return "spos:product:{$id}"; |
| 57 | } |
| 58 | |
| 59 | public static function keyQuickView($id): string |
| 60 | { |
| 61 | return "spos:quickview:{$id}"; |
| 62 | } |
| 63 | |
| 64 | public static function keyCategoryProducts($id): string |
| 65 | { |
| 66 | return "spos:category_products:{$id}"; |
| 67 | } |
| 68 | |
| 69 | /** Thin wrapper around Cache::remember using our shared TTL, so every call site stays consistent. */ |
| 70 | public static function remember(string $key, \Closure $callback) |
| 71 | { |
| 72 | return Cache::remember($key, self::TTL, $callback); |
| 73 | } |
| 74 | |
| 75 | // ── Invalidation ───────────────────────────────────────────────── |
| 76 | |
| 77 | /** |
| 78 | * Call whenever a product (or one of its variations) is created, |
| 79 | * updated, or deleted. Pass the category id(s) too so the category |
| 80 | * listing page(s) that show this product get refreshed as well. |
| 81 | */ |
| 82 | public static function forgetProduct($productId, $categoryId = null, $previousCategoryId = null): void |
| 83 | { |
| 84 | Cache::forget(self::KEY_PRODUCTS_ALL); |
| 85 | |
| 86 | if ($productId) { |
| 87 | Cache::forget(self::keyProduct($productId)); |
| 88 | Cache::forget(self::keyQuickView($productId)); |
| 89 | } |
| 90 | |
| 91 | if ($categoryId) { |
| 92 | Cache::forget(self::keyCategoryProducts($categoryId)); |
| 93 | } |
| 94 | if ($previousCategoryId && $previousCategoryId != $categoryId) { |
| 95 | Cache::forget(self::keyCategoryProducts($previousCategoryId)); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * Call for any bulk product write that does NOT go through a single |
| 101 | * Eloquent model instance (so the boot() hooks below never fire) — |
| 102 | * e.g. ProductController::updatePositions(), which uses |
| 103 | * Product::where(...)->update([...]) in a loop. |
| 104 | */ |
| 105 | public static function forgetProductsList(): void |
| 106 | { |
| 107 | Cache::forget(self::KEY_PRODUCTS_ALL); |
| 108 | } |
| 109 | |
| 110 | /** Call whenever a category is created, updated, or deleted. */ |
| 111 | public static function forgetCategory($categoryId = null, $parentId = null): void |
| 112 | { |
| 113 | Cache::forget(self::KEY_MENUS); |
| 114 | Cache::forget(self::KEY_CATEGORIES); |
| 115 | |
| 116 | if ($categoryId) { |
| 117 | Cache::forget(self::keyCategoryProducts($categoryId)); |
| 118 | } |
| 119 | if ($parentId) { |
| 120 | // A subcategory's parent listing page also shows the subcategory's products. |
| 121 | Cache::forget(self::keyCategoryProducts($parentId)); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | public static function forgetSetting(): void |
| 126 | { |
| 127 | Cache::forget(self::KEY_SETTING); |
| 128 | } |
| 129 | |
| 130 | public static function forgetSocialSettings(): void |
| 131 | { |
| 132 | Cache::forget(self::KEY_SOCIAL_SETTINGS); |
| 133 | } |
| 134 | |
| 135 | public static function forgetAppSettings(): void |
| 136 | { |
| 137 | Cache::forget(self::KEY_APP_SETTINGS); |
| 138 | } |
| 139 | |
| 140 | /** Call whenever a builder Menu or MenuItem changes. */ |
| 141 | public static function forgetMenusBuilder(): void |
| 142 | { |
| 143 | Cache::forget(self::KEY_MENUS_BUILDER); |
| 144 | } |
| 145 | |
| 146 | /** Call whenever a Widget changes. */ |
| 147 | public static function forgetWidgets(): void |
| 148 | { |
| 149 | Cache::forget(self::KEY_WIDGETS); |
| 150 | } |
| 151 | |
| 152 | /** Call whenever a Theme row changes. */ |
| 153 | public static function forgetThemes(): void |
| 154 | { |
| 155 | Cache::forget(self::KEY_THEMES); |
| 156 | } |
| 157 | } |