Passed
Push — master ( 43d5cf...5e4a14 )
by Menno
02:19
created

ManagedCache::extractModelKeys()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Codefocus\ManagedCache;
4
5
use BadFunctionCallException;
6
use Codefocus\ManagedCache\Events\Event;
7
use Codefocus\ManagedCache\Traits\HandlesEloquentEvents;
8
use Exception;
9
use Illuminate\Cache\MemcachedStore;
10
use Illuminate\Cache\Repository as CacheRepository;
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Foundation\Application;
13
14
/**
15
 * ManagedCache.
16
 *
17
 * @method DefinitionChain forgetWhen(array $conditions)
18
 */
19
class ManagedCache
20
{
21
    use HandlesEloquentEvents;
22
23
    //  Cache keys.
24
    const TAG_MAP_CACHE_KEY = 'ManagedCache_TagMap';
25
26
    /**
27
     * @var Application
28
     */
29
    protected $application;
30
31
    /**
32
     * @var CacheRepository
33
     */
34
    protected $store;
35
36
    private $isDebugModeEnabled = false;
37
38
    /**
39
     * Maps cache keys to their tags.
40
     *
41
     * @var array
42
     */
43
    protected $tagMap = [];
44
45
    /**
46
     * Constructor.
47
     *
48
     * @param Application $application
49
     */
50
    public function __construct(Application $application)
51
    {
52
        //  Inject Application.
53
        $this->application = $application;
54
        //  Get the configured cache store.
55
        $this->store = $this->application['cache.store'];
56
        if ( ! ($this->store->getStore() instanceof MemcachedStore)) {
57
            throw new Exception('Memcached not configured. Cache store is "' . class_basename($this->store) . '"');
58
        }
59
        //  Register the event listeners.
60
        $this->registerEventListener($this->application['events']);
61
    }
62
63
    public function enableDebugMode()
64
    {
65
        $this->isDebugModeEnabled = true;
66
67
        return $this;
68
    }
69
70
    public function isDebugModeEnabled()
71
    {
72
        return $this->isDebugModeEnabled;
73
    }
74
75
    public function getTagMap(): array
76
    {
77
        if (empty($this->tagMap)) {
78
            $this->tagMap = $this->store->get(self::TAG_MAP_CACHE_KEY, []);
79
            if ( ! is_array($this->tagMap)) {
80
                $this->tagMap = [];
81
            }
82
        }
83
84
        return $this->tagMap;
85
    }
86
87
    public function getTagsForKey(string $key): array
88
    {
89
        $tagMap = $this->getTagMap();
90
        if ( ! isset($tagMap[$key])) {
91
            return [];
92
        }
93
94
        return $tagMap[$key];
95
    }
96
97
    public function setTagsForKey(string $key, array $tags): void
98
    {
99
        $this->getTagMap();
100
        $this->tagMap[$key] = $tags;
101
        $this->store->forever(self::TAG_MAP_CACHE_KEY, $this->tagMap);
102
    }
103
104
    public function deleteTagsForKey(string $key)
105
    {
106
        $this->getTagMap();
107
        if (isset($this->tagMap[$key])) {
108
            unset($this->tagMap[$key]);
109
            $this->store->forever(self::TAG_MAP_CACHE_KEY, $this->tagMap);
110
        }
111
    }
112
113
    /**
114
     * Returns the Cache store instance.
115
     *
116
     * @return CacheRepository
117
     */
118
    public function getStore(): CacheRepository
119
    {
120
        return $this->store;
121
    }
122
123
    /**
124
     * Returns a Condition instance that tags a cache to get invalidated
125
     * when a new Model of the specified class is created.
126
     *
127
     * @param string $modelClassName model class name
128
     *
129
     * @return Condition
130
     */
131
    public function created(string $modelClassName): Condition
132
    {
133
        return new Condition(
134
            Event::EVENT_ELOQUENT_CREATED,
135
            $modelClassName
136
        );
137
    }
138
139
    /**
140
     * Returns a Condition instance that tags a cache to get invalidated
141
     * when the specified Model instance, or any Model of the specified class
142
     * is updated.
143
     *
144
     * @param mixed $model model instance or class name
145
     * @param int|null $modelId (default: null) The Model id
146
     *
147
     * @return Condition
148
     */
149
    public function updated($model, ?int $modelId = null): Condition
150
    {
151
        if ($this->isModel($model)) {
152
            $modelClassName = get_class($model);
153
            $modelId = $model->getKey();
154
        } else {
155
            $modelClassName = $model;
156
        }
157
158
        return new Condition(
159
            Event::EVENT_ELOQUENT_UPDATED,
160
            $modelClassName,
161
            $modelId
162
        );
163
    }
164
165
    /**
166
     * Returns a Condition instance that tags a cache to get invalidated
167
     * when the specified Model instance, or any Model of the specified class
168
     * is saved.
169
     *
170
     * @param mixed $model model instance or class name
171
     * @param int|null $modelId (default: null) The Model id
172
     *
173
     * @return Condition
174
     */
175
    public function saved($model, ?int $modelId = null): Condition
176
    {
177
        if ($this->isModel($model)) {
178
            $modelClassName = get_class($model);
179
            $modelId = $model->getKey();
180
        } else {
181
            $modelClassName = $model;
182
        }
183
184
        return new Condition(
185
            Event::EVENT_ELOQUENT_SAVED,
186
            $modelClassName,
187
            $modelId
188
        );
189
    }
190
191
    /**
192
     * Returns a Condition instance that tags a cache to get invalidated
193
     * when the specified Model instance, or any Model of the specified class
194
     * is deleted.
195
     *
196
     * @param mixed $model model instance or class name
197
     * @param int|null $modelId (default: null) The Model id
198
     *
199
     * @return Condition
200
     */
201
    public function deleted($model, ?int $modelId = null): Condition
202
    {
203
        if ($this->isModel($model)) {
204
            $modelClassName = get_class($model);
205
            $modelId = $model->getKey();
206
        } else {
207
            $modelClassName = $model;
208
        }
209
210
        return new Condition(
211
            Event::EVENT_ELOQUENT_DELETED,
212
            $modelClassName,
213
            $modelId
214
        );
215
    }
216
217
    /**
218
     * Returns a Condition instance that tags a cache to get invalidated
219
     * when the specified Model instance, or any Model of the specified class
220
     * is restored.
221
     *
222
     * @param mixed $model model instance or class name
223
     * @param int|null $modelId (default: null) The Model id
224
     *
225
     * @return Condition
226
     */
227
    public function restored($model, ?int $modelId = null): Condition
228
    {
229
        if ($this->isModel($model)) {
230
            $modelClassName = get_class($model);
231
            $modelId = $model->getKey();
232
        } else {
233
            $modelClassName = $model;
234
        }
235
236
        return new Condition(
237
            Event::EVENT_ELOQUENT_RESTORED,
238
            $modelClassName,
239
            $modelId
240
        );
241
    }
242
243
    /**
244
     * Returns a Condition instance that tags a cache to get invalidated when
245
     * a related Model of the specified class is attached.
246
     *
247
     * @param mixed $model model instance or class name
248
     * @param int|null $modelId (default: null) the Model id, if $model is a class name
249
     * @param mixed|null $relatedModel (default: null) the related Model instance or class name
250
     * @param int|null $relatedModelId (default: null) the related Model id
251
     *
252
     * @return Condition
253
     */
254
    public function relationAttached($model, ?int $modelId = null, $relatedModel = null, ?int $relatedModelId = null): Condition
255
    {
256
        if ($this->isModel($model)) {
257
            $modelClassName = get_class($model);
258
            $modelId = $model->getKey();
259
        } else {
260
            $modelClassName = $model;
261
        }
262
        if ($this->isModel($relatedModel)) {
263
            $relatedModelClassName = get_class($relatedModel);
264
            $relatedModelId = $relatedModel->/* @scrutinizer ignore-call */getKey();
265
        } else {
266
            $relatedModelClassName = $relatedModel;
267
        }
268
269
        return new Condition(
270
            Event::EVENT_ELOQUENT_ATTACHED,
271
            $modelClassName,
272
            $modelId,
273
            $relatedModelClassName,
274
            $relatedModelId
275
        );
276
    }
277
278
    /**
279
     * Returns a Condition instance that tags a cache to get invalidated when
280
     * a related Model of the specified class is detached.
281
     *
282
     * @param mixed $model model instance or class name
283
     * @param int|null $modelId (default: null) the Model id, if $model is a class name
284
     * @param mixed|null $relatedModel (default: null) the related Model instance or class name
285
     * @param int|null $relatedModelId (default: null) the related Model id
286
     *
287
     * @return Condition
288
     */
289
    public function relationDetached($model, ?int $modelId = null, $relatedModel = null, ?int $relatedModelId = null): Condition
290
    {
291
        if ($this->isModel($model)) {
292
            $modelClassName = get_class($model);
293
            $modelId = $model->getKey();
294
        } else {
295
            $modelClassName = $model;
296
        }
297
        if ($this->isModel($relatedModel)) {
298
            $relatedModelClassName = get_class($relatedModel);
299
            $relatedModelId = $relatedModel->/* @scrutinizer ignore-call */getKey();
300
        } else {
301
            $relatedModelClassName = $relatedModel;
302
        }
303
304
        return new Condition(
305
            Event::EVENT_ELOQUENT_DETACHED,
306
            $modelClassName,
307
            $modelId,
308
            $relatedModelClassName,
309
            $relatedModelId
310
        );
311
    }
312
313
    /**
314
     * Returns a Condition instance that tags a cache to get invalidated when
315
     * a related Model of the specified class is updated.
316
     *
317
     * @param mixed $model model instance or class name
318
     * @param int|null $modelId (default: null) the Model id, if $model is a class name
319
     * @param mixed|null $relatedModel (default: null) the related Model instance or class name
320
     * @param int|null $relatedModelId (default: null) the related Model id
321
     *
322
     * @return Condition
323
     */
324
    public function relationUpdated($model, ?int $modelId = null, $relatedModel = null, ?int $relatedModelId = null): Condition
325
    {
326
        if ($this->isModel($model)) {
327
            $modelClassName = get_class($model);
328
            $modelId = $model->getKey();
329
        } else {
330
            $modelClassName = $model;
331
        }
332
        if ($this->isModel($relatedModel)) {
333
            $relatedModelClassName = get_class($relatedModel);
334
            $relatedModelId = $relatedModel->/* @scrutinizer ignore-call */getKey();
335
        } else {
336
            $relatedModelClassName = $relatedModel;
337
        }
338
339
        return new Condition(
340
            Event::EVENT_ELOQUENT_UPDATED,
341
            $modelClassName,
342
            $modelId,
343
            $relatedModelClassName,
344
            $relatedModelId
345
        );
346
    }
347
348
    /**
349
     * Route function calls to a new DefinitionChain.
350
     *
351
     * @param string $name
352
     * @param array $arguments
353
     *
354
     * @throws BadFunctionCallException
355
     */
356
    public function __call(string $name, array $arguments)
357
    {
358
        $definitionChain = new DefinitionChain($this);
359
        if ( ! method_exists($definitionChain, $name)) {
360
            throw new BadFunctionCallException('Function ' . $name . ' does not exist.');
361
        }
362
363
        return call_user_func_array([$definitionChain, $name], $arguments);
364
    }
365
}
366