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
|
|||
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) { |
||
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 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths