Issues (7)

src/CacheFlag.php (2 issues)

1
<?php
2
3
namespace mmikkel\cacheflag;
4
5
use Craft;
6
use craft\base\Element;
7
use craft\base\ElementActionInterface;
8
use craft\base\ElementInterface;
0 ignored issues
show
The type craft\base\ElementInterface 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...
9
use craft\base\Plugin;
10
use craft\elements\actions\SetStatus;
11
use craft\elements\db\ElementQueryInterface;
12
use craft\events\ElementEvent;
13
use craft\events\ElementActionEvent;
14
use craft\events\MoveElementEvent;
15
use craft\events\PluginEvent;
16
use craft\events\RegisterCacheOptionsEvent;
17
use craft\events\RegisterComponentTypesEvent;
18
use craft\helpers\ElementHelper;
19
use craft\services\Elements;
20
use craft\services\Plugins;
21
use craft\services\ProjectConfig;
22
use craft\services\Structures;
23
use craft\services\Utilities;
24
use craft\utilities\ClearCaches;
25
26
use yii\base\Event;
27
28
use mmikkel\cacheflag\services\CacheFlagService;
29
use mmikkel\cacheflag\services\ProjectConfig as CacheFlagProjectConfigService;
30
use mmikkel\cacheflag\services\TemplateCachesService;
31
use mmikkel\cacheflag\twigextensions\Extension as CacheFlagTwigExtension;
32
use mmikkel\cacheflag\utilities\CacheFlagUtility;
33
34
/**
35
 * Class CacheFlag
36
 *
37
 * @author    Mats Mikkel Rummelhoff
38
 * @package   CacheFlag
39
 * @since     1.0.0
40
 *
41
 * @property CacheFlagService $cacheFlag
42
 * @property CacheFlagProjectConfigService $projectConfig
43
 * @property TemplateCachesService $templateCaches
44
 */
45
class CacheFlag extends Plugin
46
{
47
48
    /** @var string */
49
    public string $schemaVersion = '1.0.2';
50
51
    /** @var bool */
52
    public bool $hasCpSection = false;
53
54
    /** @var bool */
55
    public bool $hasCpSettings = false;
56
57
    /** @inheritdoc */
58
    public function init(): void
59
    {
60
        parent::init();
61
62
        // Register services
63
        $this->setComponents([
64
            'cacheFlag' => CacheFlagService::class,
65
            'projectConfig' => CacheFlagProjectConfigService::class,
66
            'templateCaches' => TemplateCachesService::class,
67
        ]);
68
69
        $this->_initProjectConfig();
70
        $this->_addElementEventListeners();
71
72
        // Register custom Twig extension
73
        Craft::$app->getView()->registerTwigExtension(new CacheFlagTwigExtension());
74
75
        // Add tag option to the Clear Caches utility to invalidate all flagged caches
76
        Event::on(ClearCaches::class, ClearCaches::EVENT_REGISTER_TAG_OPTIONS,
77
            static function (RegisterCacheOptionsEvent $event) {
78
                $event->options[] = [
79
                    'key' => 'cacheflag-flagged-caches',
80
                    'label' => Craft::t('cache-flag', 'Flagged template caches'),
81
                    'tag' => 'cacheflag',
82
                    'info' => Craft::t('cache-flag', 'Template caches flagged using Cache Flag'),
83
                ];
84
            }
85
        );
86
87
        // Register utility
88
        Event::on(
89
            Utilities::class,
90
            Utilities::EVENT_REGISTER_UTILITIES,
91
            static function(RegisterComponentTypesEvent $event) {
92
                $event->types[] = CacheFlagUtility::class;
93
            }
94
        );
95
96
    }
97
98
    /**
99
     * @return void
100
     */
101
    private function _initProjectConfig(): void
102
    {
103
        Event::on(
104
            ProjectConfig::class,
105
            ProjectConfig::EVENT_REBUILD,
106
            [$this->projectConfig, 'onProjectConfigRebuild']
107
        );
108
109
        Craft::$app->getProjectConfig()
110
            ->onAdd('cacheFlags.{uid}', [$this->projectConfig, 'onProjectConfigChange'])
111
            ->onUpdate('cacheFlags.{uid}', [$this->projectConfig, 'onProjectConfigChange'])
112
            ->onRemove('cacheFlags.{uid}', [$this->projectConfig, 'onProjectConfigDelete']);
113
114
        // Flush the project config when the plugin is uninstalled
115
        Event::on(
116
            Plugins::class,
117
            Plugins::EVENT_AFTER_UNINSTALL_PLUGIN,
118
            function (PluginEvent $event) {
119
                if ($event->plugin === $this) {
0 ignored issues
show
The condition $event->plugin === $this is always false.
Loading history...
120
                    Craft::$app->getProjectConfig()->remove('cacheFlags');
121
                }
122
            }
123
        );
124
    }
125
126
    /**
127
     * @return void
128
     */
129
    private function _addElementEventListeners(): void
130
    {
131
        // Invalidate flagged caches when elements are saved
132
        Event::on(
133
            Elements::class,
134
            Elements::EVENT_AFTER_SAVE_ELEMENT,
135
            function (ElementEvent $event) {
136
                $this->_maybeInvalidateFlaggedCachesByElement($event->element);
137
            }
138
        );
139
140
        // Invalidate flagged caches when elements are deleted
141
        Event::on(
142
            Elements::class,
143
            Elements::EVENT_BEFORE_DELETE_ELEMENT,
144
            function (ElementEvent $event) {
145
                $this->_maybeInvalidateFlaggedCachesByElement($event->element);
146
            }
147
        );
148
149
        // Invalidate flagged caches when structure entries are moved
150
        Event::on(
151
            Structures::class,
152
            Structures::EVENT_AFTER_MOVE_ELEMENT,
153
            function (MoveElementEvent $event) {
154
                $this->_maybeInvalidateFlaggedCachesByElement($event->element);
155
            }
156
        );
157
158
        // Invalidate flagged caches when elements change status
159
        Event::on(
160
            Elements::class,
161
            Elements::EVENT_AFTER_PERFORM_ACTION,
162
            function (ElementActionEvent $event) {
163
164
                /* @var ElementActionInterface|null $action */
165
                $action = $event->action;
166
                if (!$action instanceof SetStatus) {
167
                    return;
168
                }
169
170
                /* @var ElementQueryInterface|null $criteria */
171
                $criteria = $event->criteria;
172
                if (empty($criteria)) {
173
                    return;
174
                }
175
176
                /** @var ElementInterface[] $elements */
177
                $elements = $criteria->all();
178
                foreach ($elements as $element) {
179
                    $this->_maybeInvalidateFlaggedCachesByElement($element);
180
                }
181
            }
182
        );
183
    }
184
185
    /**
186
     * @param ElementInterface|null $element
187
     * @return void
188
     */
189
    private function _maybeInvalidateFlaggedCachesByElement(?ElementInterface $element): void
190
    {
191
        /** @var Element $element */
192
        // This try/catch is introduced to mitigate an edge case where a nested element could have an invalid (deleted) owner ID.
193
        // See https://github.com/mmikkel/CacheFlag-Craft3/issues/21
194
        try {
195
            if (ElementHelper::isDraftOrRevision($element)) {
196
                return;
197
            }
198
        } catch (\Throwable) {
199
            // We don't care about handling this exception
200
        }
201
        $this->cacheFlag->invalidateFlaggedCachesByElement($element);
202
    }
203
204
}
205