1 | <?php |
||||||
2 | /** |
||||||
3 | * SEOmatic plugin for Craft CMS |
||||||
4 | * |
||||||
5 | * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful, |
||||||
6 | * and flexible |
||||||
7 | * |
||||||
8 | * @link https://nystudio107.com |
||||||
9 | * @copyright Copyright (c) 2019 nystudio107 |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace nystudio107\seomatic\seoelements; |
||||||
13 | |||||||
14 | use Craft; |
||||||
15 | use craft\base\ElementInterface; |
||||||
0 ignored issues
–
show
|
|||||||
16 | use craft\base\Model; |
||||||
17 | use craft\elements\db\ElementQueryInterface; |
||||||
18 | use craft\events\DefineHtmlEvent; |
||||||
19 | use craft\models\Site; |
||||||
20 | use Exception; |
||||||
21 | use nystudio107\seomatic\assetbundles\seomatic\SeomaticAsset; |
||||||
22 | use nystudio107\seomatic\base\GqlSeoElementInterface; |
||||||
23 | use nystudio107\seomatic\base\SeoElementInterface; |
||||||
24 | use nystudio107\seomatic\helpers\ArrayHelper; |
||||||
25 | use nystudio107\seomatic\helpers\Config as ConfigHelper; |
||||||
26 | use nystudio107\seomatic\helpers\PluginTemplate; |
||||||
27 | use nystudio107\seomatic\models\MetaBundle; |
||||||
28 | use nystudio107\seomatic\Seomatic; |
||||||
29 | use Solspace\Calendar\Bundles\GraphQL\Interfaces\EventInterface; |
||||||
30 | use Solspace\Calendar\Calendar as CalendarPlugin; |
||||||
31 | use Solspace\Calendar\Elements\Db\EventQuery; |
||||||
32 | use Solspace\Calendar\Elements\Event; |
||||||
33 | use Solspace\Calendar\Events\DeleteModelEvent; |
||||||
34 | use Solspace\Calendar\Events\SaveModelEvent; |
||||||
35 | use Solspace\Calendar\Models\CalendarModel; |
||||||
36 | use Solspace\Calendar\Services\CalendarsService; |
||||||
37 | use yii\base\Event as BaseEvent; |
||||||
38 | |||||||
39 | /** |
||||||
40 | * @author nystudio107 |
||||||
41 | * @package Seomatic |
||||||
42 | * @since 3.2.0 |
||||||
43 | */ |
||||||
44 | class SeoEvent implements SeoElementInterface, GqlSeoElementInterface |
||||||
45 | { |
||||||
46 | // Constants |
||||||
47 | // ========================================================================= |
||||||
48 | |||||||
49 | public const META_BUNDLE_TYPE = 'event'; |
||||||
50 | public const ELEMENT_CLASSES = [ |
||||||
51 | Event::class, |
||||||
52 | ]; |
||||||
53 | public const REQUIRED_PLUGIN_HANDLE = 'calendar'; |
||||||
54 | public const CONFIG_FILE_PATH = 'eventmeta/Bundle'; |
||||||
55 | |||||||
56 | // Public Static Methods |
||||||
57 | // ========================================================================= |
||||||
58 | |||||||
59 | /** |
||||||
60 | * Returns an array of the element classes that are handled by this SeoElement |
||||||
61 | * |
||||||
62 | * @return array |
||||||
63 | */ |
||||||
64 | public static function getElementClasses(): array |
||||||
65 | { |
||||||
66 | return self::ELEMENT_CLASSES; |
||||||
67 | } |
||||||
68 | |||||||
69 | /** |
||||||
70 | * Return the handle to a required plugin for this SeoElement type |
||||||
71 | * |
||||||
72 | * @return null|string |
||||||
73 | */ |
||||||
74 | public static function getRequiredPluginHandle() |
||||||
75 | { |
||||||
76 | return self::REQUIRED_PLUGIN_HANDLE; |
||||||
77 | } |
||||||
78 | |||||||
79 | /** |
||||||
80 | * Install any event handlers for this SeoElement type |
||||||
81 | */ |
||||||
82 | public static function installEventHandlers() |
||||||
83 | { |
||||||
84 | $request = Craft::$app->getRequest(); |
||||||
85 | |||||||
86 | // Install for all requests |
||||||
87 | BaseEvent::on( |
||||||
88 | CalendarsService::class, |
||||||
89 | CalendarsService::EVENT_AFTER_SAVE, |
||||||
90 | function(SaveModelEvent $event) { |
||||||
0 ignored issues
–
show
The parameter
$event is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
91 | Craft::debug( |
||||||
92 | 'CalendarsService::EVENT_AFTER_DELETE', |
||||||
93 | __METHOD__ |
||||||
94 | ); |
||||||
95 | Seomatic::$plugin->metaBundles->resaveMetaBundles(self::META_BUNDLE_TYPE); |
||||||
96 | } |
||||||
97 | ); |
||||||
98 | BaseEvent::on( |
||||||
99 | CalendarsService::class, |
||||||
100 | CalendarsService::EVENT_AFTER_DELETE, |
||||||
101 | function(DeleteModelEvent $event) { |
||||||
0 ignored issues
–
show
The parameter
$event is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
102 | Craft::debug( |
||||||
103 | 'CalendarsService::EVENT_AFTER_DELETE', |
||||||
104 | __METHOD__ |
||||||
105 | ); |
||||||
106 | Seomatic::$plugin->metaBundles->resaveMetaBundles(self::META_BUNDLE_TYPE); |
||||||
107 | } |
||||||
108 | ); |
||||||
109 | |||||||
110 | // Install for all non-console requests |
||||||
111 | if (!$request->getIsConsoleRequest()) { |
||||||
112 | // Handler: CalendarsService::EVENT_AFTER_SAVE |
||||||
113 | BaseEvent::on( |
||||||
114 | CalendarsService::class, |
||||||
115 | CalendarsService::EVENT_AFTER_SAVE, |
||||||
116 | function(SaveModelEvent $event) { |
||||||
117 | Craft::debug( |
||||||
118 | 'CalendarsService::EVENT_AFTER_SAVE', |
||||||
119 | __METHOD__ |
||||||
120 | ); |
||||||
121 | /** @var CalendarModel $calendarModel */ |
||||||
122 | $calendarModel = $event->getModel(); |
||||||
123 | if ($calendarModel !== null && $calendarModel->id !== null) { |
||||||
124 | Seomatic::$plugin->metaBundles->invalidateMetaBundleById( |
||||||
125 | SeoEvent::getMetaBundleType(), |
||||||
126 | $calendarModel->id, |
||||||
127 | $event->isNew() |
||||||
128 | ); |
||||||
129 | // Create the meta bundles for this Event if it's new |
||||||
130 | if ($event->isNew()) { |
||||||
131 | SeoEvent::createContentMetaBundle($calendarModel); |
||||||
132 | Seomatic::$plugin->sitemaps->submitSitemapIndex(); |
||||||
133 | } |
||||||
134 | } |
||||||
135 | } |
||||||
136 | ); |
||||||
137 | // Handler: CalendarsService::EVENT_AFTER_DELETE |
||||||
138 | BaseEvent::on( |
||||||
139 | CalendarsService::class, |
||||||
140 | CalendarsService::EVENT_AFTER_DELETE, |
||||||
141 | function(DeleteModelEvent $event) { |
||||||
142 | Craft::debug( |
||||||
143 | 'CalendarsService::EVENT_AFTER_DELETE', |
||||||
144 | __METHOD__ |
||||||
145 | ); |
||||||
146 | /** @var CalendarModel $calendarModel */ |
||||||
147 | $calendarModel = $event->getModel(); |
||||||
148 | if ($calendarModel !== null && $calendarModel->id !== null) { |
||||||
149 | Seomatic::$plugin->metaBundles->invalidateMetaBundleById( |
||||||
150 | SeoEvent::getMetaBundleType(), |
||||||
151 | $calendarModel->id, |
||||||
152 | false |
||||||
153 | ); |
||||||
154 | // Delete the meta bundles for this Event |
||||||
155 | Seomatic::$plugin->metaBundles->deleteMetaBundleBySourceId( |
||||||
156 | SeoEvent::getMetaBundleType(), |
||||||
157 | $calendarModel->id |
||||||
158 | ); |
||||||
159 | } |
||||||
160 | } |
||||||
161 | ); |
||||||
162 | } |
||||||
163 | |||||||
164 | // Install only for non-console site requests |
||||||
165 | if ($request->getIsSiteRequest() && !$request->getIsConsoleRequest()) { |
||||||
166 | } |
||||||
167 | |||||||
168 | // Handler: Entry::EVENT_DEFINE_SIDEBAR_HTML |
||||||
169 | BaseEvent::on( |
||||||
170 | Event::class, |
||||||
171 | Event::EVENT_DEFINE_SIDEBAR_HTML, |
||||||
172 | static function(DefineHtmlEvent $event) { |
||||||
173 | Craft::debug( |
||||||
174 | 'Entry::EVENT_DEFINE_SIDEBAR_HTML', |
||||||
175 | __METHOD__ |
||||||
176 | ); |
||||||
177 | $html = ''; |
||||||
178 | Seomatic::$view->registerAssetBundle(SeomaticAsset::class); |
||||||
0 ignored issues
–
show
The method
registerAssetBundle() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
179 | /** @var Event $eventElement */ |
||||||
180 | $eventElement = $event->sender ?? null; |
||||||
181 | if ($eventElement !== null && $eventElement->uri !== null) { |
||||||
182 | Seomatic::$plugin->metaContainers->previewMetaContainers($eventElement->uri, $eventElement->siteId, true); |
||||||
183 | // Render our preview sidebar template |
||||||
184 | if (Seomatic::$settings->displayPreviewSidebar) { |
||||||
185 | $html .= PluginTemplate::renderPluginTemplate('_sidebars/event-preview.twig'); |
||||||
186 | } |
||||||
187 | // Render our analysis sidebar template |
||||||
188 | // @TODO: This will be added an upcoming 'pro' edition |
||||||
189 | // if (Seomatic::$settings->displayAnalysisSidebar) { |
||||||
190 | // $html .= PluginTemplate::renderPluginTemplate('_sidebars/event-analysis.twig'); |
||||||
191 | // } |
||||||
192 | } |
||||||
193 | $event->html .= $html; |
||||||
194 | } |
||||||
195 | ); |
||||||
196 | } |
||||||
197 | |||||||
198 | /** |
||||||
199 | * Return the sourceBundleType for that this SeoElement handles |
||||||
200 | * |
||||||
201 | * @return string |
||||||
202 | */ |
||||||
203 | public static function getMetaBundleType(): string |
||||||
204 | { |
||||||
205 | return self::META_BUNDLE_TYPE; |
||||||
206 | } |
||||||
207 | |||||||
208 | /** |
||||||
209 | * Create a MetaBundle in the db for each site, from the passed in $sourceModel |
||||||
210 | * |
||||||
211 | * @param Model $sourceModel |
||||||
212 | */ |
||||||
213 | public static function createContentMetaBundle(Model $sourceModel) |
||||||
214 | { |
||||||
215 | /** @var CalendarModel $sourceModel */ |
||||||
216 | $sites = Craft::$app->getSites()->getAllSites(); |
||||||
217 | /** @var Site $site */ |
||||||
218 | foreach ($sites as $site) { |
||||||
219 | $seoElement = self::class; |
||||||
220 | Seomatic::$plugin->metaBundles->createMetaBundleFromSeoElement($seoElement, $sourceModel, $site->id, null, true); |
||||||
221 | } |
||||||
222 | } |
||||||
223 | |||||||
224 | /** |
||||||
225 | * Return the refHandle (e.g.: `entry` or `category`) for the SeoElement |
||||||
226 | * |
||||||
227 | * @return string |
||||||
228 | */ |
||||||
229 | public static function getElementRefHandle(): string |
||||||
230 | { |
||||||
231 | return Event::refHandle(); |
||||||
232 | } |
||||||
233 | |||||||
234 | /** |
||||||
235 | * Return an ElementQuery for the sitemap elements for the given MetaBundle |
||||||
236 | * |
||||||
237 | * @param MetaBundle $metaBundle |
||||||
238 | * |
||||||
239 | * @return ElementQueryInterface |
||||||
240 | */ |
||||||
241 | public static function sitemapElementsQuery(MetaBundle $metaBundle): ElementQueryInterface |
||||||
242 | { |
||||||
243 | /** @var EventQuery $query */ |
||||||
244 | $query = Event::find(); |
||||||
245 | $query |
||||||
246 | ->setCalendar($metaBundle->sourceHandle) |
||||||
247 | ->setLoadOccurrences(false) |
||||||
248 | ->siteId((int)$metaBundle->sourceSiteId) |
||||||
249 | ->limit((int)$metaBundle->metaSitemapVars->sitemapLimit); |
||||||
250 | |||||||
251 | return $query; |
||||||
252 | } |
||||||
253 | |||||||
254 | /** |
||||||
255 | * Return an ElementInterface for the sitemap alt element for the given MetaBundle |
||||||
256 | * and Element ID |
||||||
257 | * |
||||||
258 | * @param MetaBundle $metaBundle |
||||||
259 | * @param int $elementId |
||||||
260 | * @param int $siteId |
||||||
261 | * |
||||||
262 | * @return null|ElementInterface |
||||||
263 | */ |
||||||
264 | public static function sitemapAltElement( |
||||||
265 | MetaBundle $metaBundle, |
||||||
266 | int $elementId, |
||||||
267 | int $siteId, |
||||||
268 | ) { |
||||||
269 | return Event::find() |
||||||
0 ignored issues
–
show
|
|||||||
270 | ->id($elementId) |
||||||
271 | ->siteId($siteId) |
||||||
272 | ->limit(1) |
||||||
273 | ->one(); |
||||||
274 | } |
||||||
275 | |||||||
276 | /** |
||||||
277 | * Return a preview URI for a given $sourceHandle and $siteId |
||||||
278 | * This just returns the first element |
||||||
279 | * |
||||||
280 | * @param string $sourceHandle |
||||||
281 | * @param int|null $siteId |
||||||
282 | * @param int|string|null $typeId |
||||||
283 | * |
||||||
284 | * @return ?string |
||||||
285 | */ |
||||||
286 | public static function previewUri(string $sourceHandle, $siteId, $typeId = null): ?string |
||||||
287 | { |
||||||
288 | $uri = null; |
||||||
289 | /** @var EventQuery $query */ |
||||||
290 | $query = Event::find(); |
||||||
291 | $element = $query |
||||||
292 | ->setCalendar($sourceHandle) |
||||||
293 | ->siteId($siteId) |
||||||
294 | ->one(); |
||||||
295 | if ($element) { |
||||||
296 | /** @var ElementInterface $element */ |
||||||
297 | $uri = $element->uri; |
||||||
298 | } |
||||||
299 | |||||||
300 | return $uri; |
||||||
301 | } |
||||||
302 | |||||||
303 | /** |
||||||
304 | * Return an array of FieldLayouts from the $sourceHandle |
||||||
305 | * |
||||||
306 | * @param string $sourceHandle |
||||||
307 | * @param int|string|null $typeId |
||||||
308 | * |
||||||
309 | * @return array |
||||||
310 | */ |
||||||
311 | public static function fieldLayouts(string $sourceHandle, $typeId = null): array |
||||||
312 | { |
||||||
313 | $layouts = []; |
||||||
314 | $calendar = CalendarPlugin::getInstance(); |
||||||
315 | if ($calendar !== null) { |
||||||
316 | $layoutId = null; |
||||||
317 | try { |
||||||
318 | $calendarModel = $calendar->calendars->getCalendarByHandle($sourceHandle); |
||||||
319 | if ($calendarModel) { |
||||||
320 | $layoutId = $calendarModel->fieldLayoutId; |
||||||
321 | } |
||||||
322 | } catch (Exception $e) { |
||||||
323 | $layoutId = null; |
||||||
324 | } |
||||||
325 | if ($layoutId) { |
||||||
0 ignored issues
–
show
The expression
$layoutId of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||||
326 | $layouts[] = Craft::$app->getFields()->getLayoutById($layoutId); |
||||||
327 | } |
||||||
328 | } |
||||||
329 | |||||||
330 | return $layouts; |
||||||
331 | } |
||||||
332 | |||||||
333 | /** |
||||||
334 | * Return the (entry) type menu as a $id => $name associative array |
||||||
335 | * |
||||||
336 | * @param string $sourceHandle |
||||||
337 | * |
||||||
338 | * @return array |
||||||
339 | */ |
||||||
340 | public static function typeMenuFromHandle(string $sourceHandle): array |
||||||
341 | { |
||||||
342 | return []; |
||||||
343 | } |
||||||
344 | |||||||
345 | /** |
||||||
346 | * Return the source model of the given $sourceId |
||||||
347 | * |
||||||
348 | * @param int $sourceId |
||||||
349 | * |
||||||
350 | * @return CalendarModel|null |
||||||
351 | */ |
||||||
352 | public static function sourceModelFromId(int $sourceId) |
||||||
353 | { |
||||||
354 | $calendarModel = null; |
||||||
355 | $calendar = CalendarPlugin::getInstance(); |
||||||
356 | if ($calendar !== null) { |
||||||
357 | $calendarModel = $calendar->calendars->getCalendarById($sourceId); |
||||||
358 | } |
||||||
359 | |||||||
360 | return $calendarModel; |
||||||
361 | } |
||||||
362 | |||||||
363 | /** |
||||||
364 | * Return the source model of the given $sourceId |
||||||
365 | * |
||||||
366 | * @param string $sourceHandle |
||||||
367 | * |
||||||
368 | * @return CalendarModel|null |
||||||
369 | */ |
||||||
370 | public static function sourceModelFromHandle(string $sourceHandle) |
||||||
371 | { |
||||||
372 | $calendarModel = null; |
||||||
373 | $calendar = CalendarPlugin::getInstance(); |
||||||
374 | if ($calendar !== null) { |
||||||
375 | $calendarModel = $calendar->calendars->getCalendarByHandle($sourceHandle); |
||||||
376 | } |
||||||
377 | |||||||
378 | return $calendarModel; |
||||||
379 | } |
||||||
380 | |||||||
381 | /** |
||||||
382 | * Return the most recently updated Element from a given source model |
||||||
383 | * |
||||||
384 | * @param Model $sourceModel |
||||||
385 | * @param int $sourceSiteId |
||||||
386 | * |
||||||
387 | * @return null|ElementInterface |
||||||
388 | */ |
||||||
389 | public static function mostRecentElement(Model $sourceModel, int $sourceSiteId) |
||||||
390 | { |
||||||
391 | /** @var CalendarModel $sourceModel */ |
||||||
392 | /** @phpstan-ignore-next-line */ |
||||||
393 | return Event::find() |
||||||
0 ignored issues
–
show
|
|||||||
394 | ->setCalendar($sourceModel->handle) |
||||||
395 | ->siteId($sourceSiteId) |
||||||
396 | ->limit(1) |
||||||
397 | ->orderBy(['elements.dateUpdated' => SORT_DESC]) |
||||||
398 | ->one(); |
||||||
399 | } |
||||||
400 | |||||||
401 | /** |
||||||
402 | * Return a meta bundle config array for the given $sourceModel |
||||||
403 | * |
||||||
404 | * @param Model $sourceModel |
||||||
405 | * |
||||||
406 | * @return array |
||||||
407 | */ |
||||||
408 | public static function metaBundleConfig(Model $sourceModel): array |
||||||
409 | { |
||||||
410 | /** @var CalendarModel $sourceModel */ |
||||||
411 | return ArrayHelper::merge( |
||||||
412 | ConfigHelper::getConfigFromFile(self::configFilePath()), |
||||||
413 | [ |
||||||
414 | 'sourceId' => $sourceModel->id, |
||||||
415 | 'sourceName' => (string)$sourceModel->name, |
||||||
416 | 'sourceHandle' => $sourceModel->handle, |
||||||
417 | ] |
||||||
418 | ); |
||||||
419 | } |
||||||
420 | |||||||
421 | /** |
||||||
422 | * Return the path to the config file directory |
||||||
423 | * |
||||||
424 | * @return string |
||||||
425 | */ |
||||||
426 | public static function configFilePath(): string |
||||||
427 | { |
||||||
428 | return self::CONFIG_FILE_PATH; |
||||||
429 | } |
||||||
430 | |||||||
431 | /** |
||||||
432 | * Return the source id from the $element |
||||||
433 | * |
||||||
434 | * @param ElementInterface $element |
||||||
435 | * |
||||||
436 | * @return int|null |
||||||
437 | */ |
||||||
438 | public static function sourceIdFromElement(ElementInterface $element) |
||||||
439 | { |
||||||
440 | /** @var Event $element */ |
||||||
441 | return $element->calendarId; |
||||||
442 | } |
||||||
443 | |||||||
444 | /** |
||||||
445 | * Return the (entry) type id from the $element |
||||||
446 | * |
||||||
447 | * @param ElementInterface $element |
||||||
448 | * |
||||||
449 | * @return int|null |
||||||
450 | */ |
||||||
451 | public static function typeIdFromElement(ElementInterface $element) |
||||||
452 | { |
||||||
453 | /** @var Event $element */ |
||||||
454 | return null; |
||||||
455 | } |
||||||
456 | |||||||
457 | /** |
||||||
458 | * Return the source handle from the $element |
||||||
459 | * |
||||||
460 | * @param ElementInterface $element |
||||||
461 | * |
||||||
462 | * @return string|null |
||||||
463 | */ |
||||||
464 | public static function sourceHandleFromElement(ElementInterface $element) |
||||||
465 | { |
||||||
466 | $sourceHandle = ''; |
||||||
0 ignored issues
–
show
|
|||||||
467 | /** @var Event $element */ |
||||||
468 | try { |
||||||
469 | $sourceHandle = $element->getCalendar()->handle; |
||||||
470 | } catch (Exception $e) { |
||||||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||||||
471 | } |
||||||
472 | |||||||
473 | return $sourceHandle; |
||||||
474 | } |
||||||
475 | |||||||
476 | /** |
||||||
477 | * Create all the MetaBundles in the db for this Seo Element |
||||||
478 | */ |
||||||
479 | public static function createAllContentMetaBundles() |
||||||
480 | { |
||||||
481 | $calendar = CalendarPlugin::getInstance(); |
||||||
482 | if ($calendar !== null) { |
||||||
483 | // Get all of the calendars with URLs |
||||||
484 | $calendarModels = $calendar->calendars->getAllCalendars(); |
||||||
485 | foreach ($calendarModels as $calendarModel) { |
||||||
486 | self::createContentMetaBundle($calendarModel); |
||||||
487 | } |
||||||
488 | } |
||||||
489 | } |
||||||
490 | |||||||
491 | /** |
||||||
492 | * @inheritdoc |
||||||
493 | */ |
||||||
494 | public static function getGqlInterfaceTypeName() |
||||||
495 | { |
||||||
496 | return EventInterface::getName(); |
||||||
497 | } |
||||||
498 | } |
||||||
499 |
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