Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.00% covered (warning)
84.00%
21 / 25
69.23% covered (warning)
69.23%
9 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
CacheService
84.00% covered (warning)
84.00%
21 / 25
69.23% covered (warning)
69.23%
9 / 13
20.48
0.00% covered (danger)
0.00%
0 / 1
 keyProduct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 keyQuickView
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 keyCategoryProducts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 remember
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 forgetProduct
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
5.05
 forgetProductsList
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 forgetCategory
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 forgetSetting
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 forgetSocialSettings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 forgetAppSettings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 forgetMenusBuilder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 forgetWidgets
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 forgetThemes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Service;
4
5use 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 */
23class 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}