HandlesEloquentEvents   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 132
rs 10
c 0
b 0
f 0
wmc 16

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getObservableEvents() 0 11 1
A extractModelKeys() 0 13 3
B getModelEventTags() 0 28 5
A registerEventListener() 0 6 2
B handleEloquentEvent() 0 24 5
1
<?php
2
3
namespace Codefocus\ManagedCache\Traits;
4
5
use Codefocus\ManagedCache\Condition;
6
use Codefocus\ManagedCache\Events\Event;
7
use Illuminate\Contracts\Events\Dispatcher;
8
use Illuminate\Database\Eloquent\Model;
9
10
trait HandlesEloquentEvents
11
{
12
    use IdentifiesEloquentModels;
13
14
    /**
15
     * @var Dispatcher
16
     */
17
    protected $dispatcher;
18
19
    /**
20
     * Register event listeners.
21
     *
22
     * @param Dispatcher $dispatcher
23
     */
24
    protected function registerEventListener(Dispatcher $dispatcher): void
25
    {
26
        $this->dispatcher = $dispatcher;
27
        //  Register Eloquent event listeners.
28
        foreach ($this->getObservableEvents() as $eventKey) {
29
            $this->dispatcher->listen($eventKey . ':*', [$this, 'handleEloquentEvent']);
30
        }
31
    }
32
33
    /**
34
     * Handle an Eloquent event.
35
     *
36
     * @param string $eventKey
37
     * @param mixed $payload
38
     */
39
    public function handleEloquentEvent($eventKey, $payload): void
40
    {
41
        //  Extract the basic event name and the model name from the event key.
42
        $regex = '/^(' . implode('|', $this->getObservableEvents()) . '): ([a-zA-Z0-9\\\\]+)$/';
43
        if ( ! preg_match($regex, $eventKey, $matches)) {
44
            return;
45
        }
46
        list(, $eventName, $modelName) = $matches;
47
        //  Ensure $payload is always an array.
48
        $payload = (is_array($payload)) ? $payload : [$payload];
49
        //  Create a tag to flush stores tagged with:
50
        //  -   this Eloquent event, AND
51
        //  -   this Model class
52
        $cacheTags = [
53
            new Condition($eventName, $modelName),
54
        ];
55
        foreach ($payload as $model) {
56
            if ( ! $this->isModel($model)) {
57
                continue;
58
            }
59
            $cacheTags += $this->getModelEventTags($model, $eventName);
60
        }
61
        //	Flush all stores with these tags
62
        $this->/* @scrutinizer ignore-call */forgetWhen($cacheTags)->flush();
63
    }
64
65
    /**
66
     * Returns an array of tags based on a Model Event.
67
     *
68
     * @param Model $model
69
     * @param string $eventName
70
     *
71
     * @return array
72
     */
73
    private function getModelEventTags(Model $model, string $eventName): array
74
    {
75
        $modelId = $model->getKey();
76
        if (empty($modelId) || ! is_numeric($modelId)) {
77
            return [];
78
        }
79
        $modelId = (int) $modelId;
80
        $modelName = get_class($model);
81
        //  Create a tag to flush stores tagged with:
82
        //  -   this Eloquent event, AND
83
        //  -   this Model instance
84
        $cacheTags = [
85
            new Condition($eventName, $modelName, $modelId),
86
        ];
87
        //	Create tags for related models.
88
        foreach ($this->extractModelKeys($model) as $relatedModelName => $relatedModelId) {
89
            //	Flush cached items that are tagged through a relation
90
            //	with this model.
91
            $cacheTags[] = new Condition(
92
                (Event::EVENT_ELOQUENT_DELETED === $eventName) ? Event::EVENT_ELOQUENT_DETACHED : Event::EVENT_ELOQUENT_ATTACHED,
93
                $modelName,
94
                $modelId,
95
                $relatedModelName,
96
                $relatedModelId
97
            );
98
        }
99
100
        return $cacheTags;
101
    }
102
103
    /**
104
     * Get the observable event names.
105
     *
106
     * @return array
107
     */
108
    private function getObservableEvents(): array
109
    {
110
        return [
111
            Event::EVENT_ELOQUENT_CREATED,
112
            Event::EVENT_ELOQUENT_UPDATED,
113
            Event::EVENT_ELOQUENT_SAVED,
114
            Event::EVENT_ELOQUENT_DELETED,
115
            Event::EVENT_ELOQUENT_RESTORED,
116
            //  @TODO:  Verify that these are emitted by Laravel too.
117
            Event::EVENT_ELOQUENT_ATTACHED,
118
            Event::EVENT_ELOQUENT_DETACHED,
119
        ];
120
    }
121
122
    /**
123
     * Extract attributes that act as foreign keys.
124
     *
125
     * @param Model $model An Eloquent Model instance
126
     *
127
     * @return array
128
     */
129
    private function extractModelKeys(Model $model): array
130
    {
131
        $modelKeys = [];
132
        foreach ($model->getAttributes() as $attributeName => $value) {
133
            if (preg_match('/([^_]+)_id/', $attributeName, $matches)) {
134
                //	This field is a key
135
                $modelKeys[strtolower($matches[1])] = $value;
136
            }
137
        }
138
        //	Ensure our model keys are always in the same order.
139
        ksort($modelKeys);
140
141
        return $modelKeys;
142
    }
143
}
144