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

HandlesEloquentEvents::handleEloquentEvent()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 7
nop 2
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace Codefocus\ManagedCache\Traits;
4
5
use Codefocus\ManagedCache\Events\Event;
6
use Illuminate\Contracts\Events\Dispatcher;
7
use Illuminate\Database\Eloquent\Model;
8
9
trait HandlesEloquentEvents
10
{
11
    use IdentifiesEloquentModels;
12
13
    /**
14
     * @var Dispatcher
15
     */
16
    protected $dispatcher;
17
18
    /**
19
     * Register event listeners.
20
     *
21
     * @param Dispatcher $dispatcher
22
     */
23
    protected function registerEventListener(Dispatcher $dispatcher): void
24
    {
25
        $this->dispatcher = $dispatcher;
26
        //  Register Eloquent event listeners.
27
        foreach ($this->getObservableEvents() as $eventKey) {
28
            $this->dispatcher->listen($eventKey . ':*', [$this, 'handleEloquentEvent']);
29
        }
30
    }
31
32
    /**
33
     * Handle an Eloquent event.
34
     *
35
     * @param string $eventKey
36
     * @param mixed $payload
37
     */
38
    public function handleEloquentEvent($eventKey, $payload): void
39
    {
40
        //  Extract the basic event name and the model name from the event key.
41
        $regex = '/^(' . implode('|', $this->getObservableEvents()) . '): ([a-zA-Z0-9\\\\]+)$/';
42
        if ( ! preg_match($regex, $eventKey, $matches)) {
43
            return;
44
        }
45
        $eventName = $matches[1];
46
        $modelName = $matches[2];
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),
0 ignored issues
show
Bug introduced by
The type Codefocus\ManagedCache\Traits\Condition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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->forgetWhen($cacheTags)->flush();
0 ignored issues
show
Bug introduced by
It seems like forgetWhen() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

62
        $this->/** @scrutinizer ignore-call */ forgetWhen($cacheTags)->flush();
Loading history...
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
                (self::EVENT_ELOQUENT_DELETED === $eventName) ? self::EVENT_ELOQUENT_DETACHED : self::EVENT_ELOQUENT_ATTACHED,
0 ignored issues
show
Bug introduced by
The constant Codefocus\ManagedCache\T...EVENT_ELOQUENT_DETACHED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Codefocus\ManagedCache\T...:EVENT_ELOQUENT_DELETED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant Codefocus\ManagedCache\T...EVENT_ELOQUENT_ATTACHED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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