Total Complexity | 137 |
Total Lines | 1030 |
Duplicated Lines | 0 % |
Changes | 9 | ||
Bugs | 4 | Features | 0 |
Complex classes like MetaBundles often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MetaBundles, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class MetaBundles extends Component |
||
52 | { |
||
53 | // Constants |
||
54 | // ========================================================================= |
||
55 | |||
56 | public const GLOBAL_META_BUNDLE = '__GLOBAL_BUNDLE__'; |
||
57 | public const FIELD_META_BUNDLE = 'field'; |
||
58 | |||
59 | public const IGNORE_DB_ATTRIBUTES = [ |
||
60 | 'id', |
||
61 | 'dateCreated', |
||
62 | 'dateUpdated', |
||
63 | 'uid', |
||
64 | ]; |
||
65 | |||
66 | public const ALWAYS_INCLUDED_SEO_SETTINGS_FIELDS = [ |
||
67 | 'twitterTitle', |
||
68 | 'twitterDescription', |
||
69 | 'twitterImage', |
||
70 | 'twitterImageDescription', |
||
71 | |||
72 | 'ogTitle', |
||
73 | 'ogDescription', |
||
74 | 'ogImage', |
||
75 | 'ogImageDescription', |
||
76 | ]; |
||
77 | |||
78 | public const COMPOSITE_INHERITANCE_CHILDREN = [ |
||
79 | 'seoImage' => [ |
||
80 | 'metaBundleSettings.seoImageTransformMode', |
||
81 | 'metaBundleSettings.seoImageTransform', |
||
82 | 'metaBundleSettings.seoImageSource', |
||
83 | 'metaBundleSettings.seoImageField', |
||
84 | 'metaBundleSettings.seoImageIds', |
||
85 | ], |
||
86 | 'ogImage' => [ |
||
87 | 'metaBundleSettings.ogImageTransformMode', |
||
88 | 'metaBundleSettings.ogImageTransform', |
||
89 | 'metaBundleSettings.ogImageSource', |
||
90 | 'metaBundleSettings.ogImageField', |
||
91 | 'metaBundleSettings.ogImageIds', |
||
92 | ], |
||
93 | 'twitterImage' => [ |
||
94 | 'metaBundleSettings.twitterImageTransformMode', |
||
95 | 'metaBundleSettings.twitterImageTransform', |
||
96 | 'metaBundleSettings.twitterImageSource', |
||
97 | 'metaBundleSettings.twitterImageField', |
||
98 | 'metaBundleSettings.twitterImageIds', |
||
99 | ], |
||
100 | ]; |
||
101 | |||
102 | public const PRESERVE_SCRIPT_SETTINGS = [ |
||
103 | 'include', |
||
104 | 'tagAttrs', |
||
105 | 'templateString', |
||
106 | 'position', |
||
107 | 'bodyTemplateString', |
||
108 | 'bodyPosition', |
||
109 | 'vars', |
||
110 | ]; |
||
111 | |||
112 | public const PRESERVE_FRONTEND_TEMPLATE_SETTINGS = [ |
||
113 | 'include', |
||
114 | 'templateString', |
||
115 | ]; |
||
116 | |||
117 | // Protected Properties |
||
118 | // ========================================================================= |
||
119 | |||
120 | /** |
||
121 | * @var MetaBundle[] indexed by [id] |
||
122 | */ |
||
123 | protected $metaBundles = []; |
||
124 | |||
125 | /** |
||
126 | * @var array indexed by [sourceId][sourceSiteId] = id |
||
127 | */ |
||
128 | protected $metaBundlesBySourceId = []; |
||
129 | |||
130 | /** |
||
131 | * @var array indexed by [sourceHandle][sourceSiteId] = id |
||
132 | */ |
||
133 | protected $metaBundlesBySourceHandle = []; |
||
134 | |||
135 | /** |
||
136 | * @var array indexed by [sourceSiteId] = id |
||
137 | */ |
||
138 | protected $globalMetaBundles = []; |
||
139 | |||
140 | /** |
||
141 | * @var array parent meta bundles for elements |
||
142 | */ |
||
143 | protected $elementContentMetaBundles = []; |
||
144 | |||
145 | // Public Methods |
||
146 | // ========================================================================= |
||
147 | |||
148 | /** |
||
149 | * Get the global meta bundle for the site |
||
150 | * |
||
151 | * @param int $sourceSiteId |
||
152 | * @param bool $parse Whether the resulting metabundle should be parsed |
||
153 | * |
||
154 | * @return null|MetaBundle |
||
155 | */ |
||
156 | public function getGlobalMetaBundle(int $sourceSiteId, $parse = true) |
||
157 | { |
||
158 | $metaBundle = null; |
||
159 | // See if we have the meta bundle cached |
||
160 | if (!empty($this->globalMetaBundles[$sourceSiteId])) { |
||
161 | return $this->globalMetaBundles[$sourceSiteId]; |
||
162 | } |
||
163 | $metaBundleArray = (new Query()) |
||
164 | ->from(['{{%seomatic_metabundles}}']) |
||
165 | ->where([ |
||
166 | 'sourceBundleType' => self::GLOBAL_META_BUNDLE, |
||
167 | 'sourceSiteId' => $sourceSiteId, |
||
168 | ]) |
||
169 | ->one(); |
||
170 | if (!empty($metaBundleArray)) { |
||
171 | // Get the attributes from the db |
||
172 | $metaBundleArray = array_diff_key($metaBundleArray, array_flip(self::IGNORE_DB_ATTRIBUTES)); |
||
173 | $metaBundle = MetaBundle::create($metaBundleArray, $parse); |
||
174 | if ($parse) { |
||
175 | $this->syncBundleWithConfig($metaBundle); |
||
176 | } |
||
177 | } else { |
||
178 | // If it doesn't exist, create it |
||
179 | $metaBundle = $this->createGlobalMetaBundleForSite($sourceSiteId); |
||
180 | } |
||
181 | if ($parse) { |
||
182 | // Cache it for future accesses |
||
183 | $this->globalMetaBundles[$sourceSiteId] = $metaBundle; |
||
184 | } |
||
185 | |||
186 | return $metaBundle; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Synchronize the passed in metaBundle with the seomatic-config files if |
||
191 | * there is a newer version of the MetaBundle bundleVersion in the config |
||
192 | * file |
||
193 | * |
||
194 | * @param MetaBundle $metaBundle |
||
195 | * @param bool $forceUpdate |
||
196 | */ |
||
197 | public function syncBundleWithConfig(MetaBundle &$metaBundle, bool $forceUpdate = false) |
||
198 | { |
||
199 | $prevMetaBundle = $metaBundle; |
||
200 | $config = []; |
||
201 | $sourceBundleType = $metaBundle->sourceBundleType; |
||
202 | if ($sourceBundleType === self::GLOBAL_META_BUNDLE) { |
||
203 | $config = ConfigHelper::getConfigFromFile('globalmeta/Bundle'); |
||
204 | } |
||
205 | $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType); |
||
206 | if ($seoElement) { |
||
207 | $configPath = $seoElement::configFilePath(); |
||
208 | $config = ConfigHelper::getConfigFromFile($configPath); |
||
209 | } |
||
210 | // If the config file has a newer version than the $metaBundleArray, merge them |
||
211 | $shouldUpdate = !empty($config) && version_compare($config['bundleVersion'], $metaBundle->bundleVersion, '>'); |
||
212 | if ($shouldUpdate || $forceUpdate) { |
||
213 | // Create a new meta bundle |
||
214 | if ($sourceBundleType === self::GLOBAL_META_BUNDLE) { |
||
215 | $metaBundle = $this->createGlobalMetaBundleForSite( |
||
216 | $metaBundle->sourceSiteId, |
||
217 | $metaBundle |
||
218 | ); |
||
219 | } else { |
||
220 | $sourceModel = $seoElement::sourceModelFromId($metaBundle->sourceId); |
||
221 | if ($sourceModel) { |
||
222 | $metaBundle = $this->createMetaBundleFromSeoElement( |
||
|
|||
223 | $seoElement, |
||
224 | $sourceModel, |
||
225 | $metaBundle->sourceSiteId, |
||
226 | $metaBundle |
||
227 | ); |
||
228 | } |
||
229 | } |
||
230 | } |
||
231 | |||
232 | // If for some reason we were unable to sync this meta bundle, return the old one |
||
233 | if ($metaBundle === null) { |
||
234 | $metaBundle = $prevMetaBundle; |
||
235 | } |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * @param int $siteId |
||
240 | * @param MetaBundle|null $baseConfig |
||
241 | * |
||
242 | * @return MetaBundle |
||
243 | */ |
||
244 | public function createGlobalMetaBundleForSite(int $siteId, $baseConfig = null): MetaBundle |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * @param MetaBundle $metaBundle |
||
274 | * @param int $siteId |
||
275 | */ |
||
276 | public function updateMetaBundle(MetaBundle $metaBundle, int $siteId) |
||
335 | ); |
||
336 | } |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * @param class-string<SeoElementInterface> $seoElement |
||
341 | * @param Model $sourceModel |
||
342 | * @param int $sourceSiteId |
||
343 | * @param ?MetaBundle $baseConfig |
||
344 | * @param bool $syncConfig |
||
345 | * @return MetaBundle|null |
||
346 | * @throws InvalidConfigException |
||
347 | */ |
||
348 | public function createMetaBundleFromSeoElement( |
||
349 | $seoElement, |
||
350 | $sourceModel, |
||
351 | int $sourceSiteId, |
||
352 | $baseConfig = null, |
||
353 | $syncConfig = false, |
||
354 | ) { |
||
355 | $metaBundle = null; |
||
356 | // Get the site settings and turn them into arrays |
||
357 | /** @var Section|CategoryGroup|ProductType $sourceModel */ |
||
358 | $siteSettings = $sourceModel->getSiteSettings(); |
||
359 | if (!empty($siteSettings[$sourceSiteId])) { |
||
360 | $siteSettingsArray = []; |
||
361 | /** @var Section_SiteSettings $siteSetting */ |
||
362 | foreach ($siteSettings as $siteSetting) { |
||
363 | if ($siteSetting->hasUrls && SiteHelper::siteEnabledWithUrls($sourceSiteId)) { |
||
364 | $siteSettingArray = $siteSetting->toArray(); |
||
365 | // Get the site language |
||
366 | $siteSettingArray['language'] = MetaValueHelper::getSiteLanguage($siteSetting->siteId); |
||
367 | $siteSettingsArray[] = $siteSettingArray; |
||
368 | } |
||
369 | } |
||
370 | $siteSettingsArray = ArrayHelper::index($siteSettingsArray, 'siteId'); |
||
371 | // Create a MetaBundle for this site |
||
372 | $siteSetting = $siteSettings[$sourceSiteId]; |
||
373 | if ($siteSetting->hasUrls && SiteHelper::siteEnabledWithUrls($sourceSiteId)) { |
||
374 | if ($syncConfig) { |
||
375 | // Get the most recent dateUpdated |
||
376 | $element = $seoElement::mostRecentElement($sourceModel, $sourceSiteId); |
||
377 | /** @var Element|null $element */ |
||
378 | if ($element) { |
||
379 | $dateUpdated = $element->dateUpdated ?? $element->dateCreated; |
||
380 | } else { |
||
381 | $dateUpdated = new DateTime(); |
||
382 | } |
||
383 | // Create a new meta bundle with propagated defaults |
||
384 | $metaBundleDefaults = ArrayHelper::merge( |
||
385 | $seoElement::metaBundleConfig($sourceModel), |
||
386 | [ |
||
387 | 'sourceTemplate' => (string)$siteSetting->template, |
||
388 | 'sourceSiteId' => $siteSetting->siteId, |
||
389 | 'sourceAltSiteSettings' => $siteSettingsArray, |
||
390 | 'sourceDateUpdated' => $dateUpdated, |
||
391 | ] |
||
392 | ); |
||
393 | // The mainEntityOfPage computedType must be set before creating the bundle |
||
394 | if ($baseConfig !== null && !empty($baseConfig->metaGlobalVars->mainEntityOfPage)) { |
||
395 | $metaBundleDefaults['metaGlobalVars']['mainEntityOfPage'] = |
||
396 | $baseConfig->metaGlobalVars->mainEntityOfPage; |
||
397 | } |
||
398 | // Merge in any migrated settings from an old Seomatic_Meta Field |
||
399 | if ($element !== null) { |
||
400 | /** @var Element $elementFromSite */ |
||
401 | $elementFromSite = Craft::$app->getElements()->getElementById($element->id, null, $sourceSiteId); |
||
402 | if ($element instanceof Element) { |
||
403 | $config = MigrationHelper::configFromSeomaticMeta( |
||
404 | $elementFromSite, |
||
405 | MigrationHelper::SECTION_MIGRATION_CONTEXT |
||
406 | ); |
||
407 | $metaBundleDefaults = ArrayHelper::merge( |
||
408 | $metaBundleDefaults, |
||
409 | $config |
||
410 | ); |
||
411 | } |
||
412 | } |
||
413 | $metaBundle = MetaBundle::create($metaBundleDefaults); |
||
414 | if ($baseConfig !== null) { |
||
415 | $this->mergeMetaBundleSettings($metaBundle, $baseConfig); |
||
416 | } |
||
417 | $this->updateMetaBundle($metaBundle, $sourceSiteId); |
||
418 | } |
||
419 | } |
||
420 | } |
||
421 | |||
422 | return $metaBundle; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * @param string $sourceBundleType |
||
427 | * @param string $sourceHandle |
||
428 | * @param int $sourceSiteId |
||
429 | * @param int|null $typeId |
||
430 | * |
||
431 | * @return null|MetaBundle |
||
432 | */ |
||
433 | public function getMetaBundleBySourceHandle(string $sourceBundleType, string $sourceHandle, int $sourceSiteId, $typeId = null) |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Invalidate the caches and data structures associated with this MetaBundle |
||
493 | * |
||
494 | * @param string $sourceBundleType |
||
495 | * @param int|null $sourceId |
||
496 | * @param bool $isNew |
||
497 | */ |
||
498 | public function invalidateMetaBundleById(string $sourceBundleType, ?int $sourceId, bool $isNew = false) |
||
499 | { |
||
500 | $metaBundleInvalidated = false; |
||
501 | $sites = Craft::$app->getSites()->getAllSites(); |
||
502 | foreach ($sites as $site) { |
||
503 | // See if this is a section we are tracking |
||
504 | $metaBundle = $this->getMetaBundleBySourceId($sourceBundleType, $sourceId, $site->id); |
||
505 | if ($metaBundle) { |
||
506 | Craft::info( |
||
507 | 'Invalidating meta bundle: ' |
||
508 | . $metaBundle->sourceHandle |
||
509 | . ' from siteId: ' |
||
510 | . $site->id, |
||
511 | __METHOD__ |
||
512 | ); |
||
513 | // Is this a new source? |
||
514 | if (!$isNew) { |
||
515 | $metaBundleInvalidated = true; |
||
516 | // Handle syncing up the sourceHandle |
||
517 | if ($sourceBundleType !== self::GLOBAL_META_BUNDLE) { |
||
518 | $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType); |
||
519 | if ($seoElement !== null) { |
||
520 | /** @var Section|CategoryGroup|ProductType $sourceModel */ |
||
521 | $sourceModel = $seoElement::sourceModelFromId($sourceId); |
||
522 | if ($sourceModel !== null) { |
||
523 | $metaBundle->sourceName = (string)$sourceModel->name; |
||
524 | $metaBundle->sourceHandle = $sourceModel->handle; |
||
525 | } |
||
526 | } |
||
527 | } |
||
528 | // Invalidate caches after an existing section is saved |
||
529 | Seomatic::$plugin->metaContainers->invalidateContainerCacheById( |
||
530 | $sourceId, |
||
531 | $sourceBundleType, |
||
532 | $metaBundle->sourceSiteId |
||
533 | ); |
||
534 | Seomatic::$plugin->sitemaps->invalidateSitemapCache( |
||
535 | $metaBundle->sourceHandle, |
||
536 | $metaBundle->sourceSiteId, |
||
537 | $metaBundle->sourceBundleType, |
||
538 | false |
||
539 | ); |
||
540 | // Update the meta bundle data |
||
541 | $this->updateMetaBundle($metaBundle, $site->id); |
||
542 | } |
||
543 | } |
||
544 | } |
||
545 | // If we've invalidated a meta bundle, we need to invalidate the sitemap index, too |
||
546 | if ($metaBundleInvalidated) { |
||
547 | Seomatic::$plugin->sitemaps->invalidateSitemapIndexCache(); |
||
548 | } |
||
549 | } |
||
550 | |||
551 | /** |
||
552 | * @param string $sourceBundleType |
||
553 | * @param int $sourceId |
||
554 | * @param int|null $sourceSiteId |
||
555 | * @param int|null $typeId |
||
556 | * |
||
557 | * @return null|MetaBundle |
||
558 | */ |
||
559 | public function getMetaBundleBySourceId(string $sourceBundleType, int $sourceId, ?int $sourceSiteId, $typeId = null) |
||
560 | { |
||
561 | $metaBundle = null; |
||
562 | $typeId = (int)$typeId; |
||
563 | // See if we have the meta bundle cached |
||
564 | if (!empty($this->metaBundlesBySourceId[$sourceBundleType][$sourceId][$sourceSiteId][$typeId])) { |
||
565 | $id = $this->metaBundlesBySourceId[$sourceBundleType][$sourceId][$sourceSiteId][$typeId]; |
||
566 | if (!empty($this->metaBundles[$id])) { |
||
567 | return $this->metaBundles[$id]; |
||
568 | } |
||
569 | } |
||
570 | // Look for a matching meta bundle in the db |
||
571 | $query = (new Query()) |
||
572 | ->from(['{{%seomatic_metabundles}}']) |
||
573 | ->where([ |
||
574 | 'sourceBundleType' => $sourceBundleType, |
||
575 | 'sourceId' => $sourceId, |
||
576 | 'sourceSiteId' => $sourceSiteId, |
||
577 | ]); |
||
578 | if (!empty($typeId)) { |
||
579 | $query |
||
580 | ->andWhere([ |
||
581 | 'typeId' => $typeId, |
||
582 | ]); |
||
583 | } |
||
584 | $metaBundleArray = $query |
||
585 | ->one(); |
||
586 | // If the specific query with a `typeId` returned nothing, try a more general query without `typeId` |
||
587 | if (empty($metaBundleArray)) { |
||
588 | $metaBundleArray = (new Query()) |
||
589 | ->from(['{{%seomatic_metabundles}}']) |
||
590 | ->where([ |
||
591 | 'sourceBundleType' => $sourceBundleType, |
||
592 | 'sourceId' => $sourceId, |
||
593 | 'sourceSiteId' => $sourceSiteId, |
||
594 | ]) |
||
595 | ->one(); |
||
596 | } |
||
597 | if (!empty($metaBundleArray)) { |
||
598 | // Get the attributes from the db |
||
599 | $metaBundleArray = array_diff_key($metaBundleArray, array_flip(self::IGNORE_DB_ATTRIBUTES)); |
||
600 | $metaBundle = MetaBundle::create($metaBundleArray); |
||
601 | $this->syncBundleWithConfig($metaBundle); |
||
602 | $id = count($this->metaBundles); |
||
603 | $this->metaBundles[$id] = $metaBundle; |
||
604 | $this->metaBundlesBySourceId[$sourceBundleType][$sourceId][$sourceSiteId][$typeId] = $id; |
||
605 | } else { |
||
606 | // If it doesn't exist, create it |
||
607 | $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType); |
||
608 | if ($seoElement !== null) { |
||
609 | $sourceModel = $seoElement::sourceModelFromId($sourceId); |
||
610 | if ($sourceModel) { |
||
611 | $metaBundle = $this->createMetaBundleFromSeoElement($seoElement, $sourceModel, $sourceSiteId, null, true); |
||
612 | } |
||
613 | } |
||
614 | } |
||
615 | |||
616 | return $metaBundle; |
||
617 | } |
||
618 | |||
619 | /** |
||
620 | * Resave all the meta bundles of a given type. |
||
621 | * |
||
622 | * @param string $metaBundleType |
||
623 | */ |
||
624 | public function resaveMetaBundles(string $metaBundleType) |
||
625 | { |
||
626 | // For all meta bundles of a given type |
||
627 | $metaBundleRows = (new Query()) |
||
628 | ->from(['{{%seomatic_metabundles}}']) |
||
629 | ->where(['sourceBundleType' => $metaBundleType]) |
||
630 | ->all(); |
||
631 | |||
632 | foreach ($metaBundleRows as $metaBundleRow) { |
||
633 | // Create it from the DB data |
||
634 | $metaBundleData = array_diff_key($metaBundleRow, array_flip(self::IGNORE_DB_ATTRIBUTES)); |
||
635 | $metaBundle = MetaBundle::create($metaBundleData); |
||
636 | if (!$metaBundle) { |
||
637 | continue; |
||
638 | } |
||
639 | // Sync it and update it. |
||
640 | Seomatic::$plugin->metaBundles->syncBundleWithConfig($metaBundle, true); |
||
641 | Seomatic::$plugin->metaBundles->updateMetaBundle($metaBundle, $metaBundle->sourceSiteId); |
||
642 | } |
||
643 | } |
||
644 | |||
645 | /** |
||
646 | * Invalidate the caches and data structures associated with this MetaBundle |
||
647 | * |
||
648 | * @param Element|null $element |
||
649 | * @param bool $isNew |
||
650 | */ |
||
651 | public function invalidateMetaBundleByElement($element, bool $isNew = false) |
||
652 | { |
||
653 | $metaBundleInvalidated = false; |
||
654 | $invalidateMetaBundle = true; |
||
655 | $sitemapInvalidated = false; |
||
656 | if ($element->getIsDraft() || $element->getIsRevision()) { |
||
657 | $invalidateMetaBundle = false; |
||
658 | } |
||
659 | if ($element && $invalidateMetaBundle) { |
||
660 | $uri = $element->uri ?? ''; |
||
661 | // Normalize the incoming URI to account for `__home__` |
||
662 | if ($element->uri) { |
||
663 | $uri = ($element->uri === '__home__') ? '' : $uri; |
||
664 | } |
||
665 | // Invalidate sitemap caches after an existing element is saved |
||
666 | list($sourceId, $sourceBundleType, $sourceHandle, $sourceSiteId, $typeId) |
||
667 | = $this->getMetaSourceFromElement($element); |
||
668 | if ($sourceId) { |
||
669 | Craft::info( |
||
670 | 'Invalidating meta bundle: ' |
||
671 | . $uri |
||
672 | . '/' |
||
673 | . $sourceSiteId, |
||
674 | __METHOD__ |
||
675 | ); |
||
676 | $metaBundleInvalidated = true; |
||
677 | Seomatic::$plugin->metaContainers->invalidateContainerCacheById($sourceId, $sourceBundleType); |
||
678 | // Invalidate the sitemap cache |
||
679 | $metaBundle = $this->getMetaBundleBySourceId($sourceBundleType, $sourceId, $sourceSiteId); |
||
680 | if ($metaBundle) { |
||
681 | $dateUpdated = $element->dateUpdated ?? $element->dateCreated; |
||
682 | $metaBundle->sourceDateUpdated = $dateUpdated; |
||
683 | // Update the meta bundle data |
||
684 | $this->updateMetaBundle($metaBundle, $sourceSiteId); |
||
685 | if ( |
||
686 | $metaBundle->metaSitemapVars->sitemapUrls |
||
687 | && !$element->resaving) { |
||
688 | $sitemapInvalidated = true; |
||
689 | Seomatic::$plugin->sitemaps->invalidateSitemapCache( |
||
690 | $metaBundle->sourceHandle, |
||
691 | $metaBundle->sourceSiteId, |
||
692 | $metaBundle->sourceBundleType, |
||
693 | false |
||
694 | ); |
||
695 | } |
||
696 | } |
||
697 | } |
||
698 | // If we've invalidated a meta bundle, we need to invalidate the sitemap index, too |
||
699 | if ($metaBundleInvalidated |
||
700 | && $sitemapInvalidated |
||
701 | && !$element->resaving) { |
||
702 | Seomatic::$plugin->sitemaps->invalidateSitemapIndexCache(); |
||
703 | } |
||
704 | } |
||
705 | } |
||
706 | |||
707 | /** |
||
708 | * @param Element $element |
||
709 | * |
||
710 | * @return array |
||
711 | */ |
||
712 | public function getMetaSourceFromElement(Element $element): array |
||
733 | } |
||
734 | |||
735 | /** |
||
736 | * Get all of the meta bundles for a given $sourceSiteId |
||
737 | * |
||
738 | * @param int|null $sourceSiteId |
||
739 | * |
||
740 | * @return array |
||
741 | */ |
||
742 | public function getContentMetaBundlesForSiteId($sourceSiteId, $filter = ''): array |
||
743 | { |
||
744 | $metaBundles = []; |
||
745 | $bundles = []; |
||
746 | // Since sectionIds, CategoryIds, etc. are not unique, we need to do separate queries and combine them |
||
747 | $seoElements = Seomatic::$plugin->seoElements->getAllSeoElementTypes(); |
||
748 | foreach ($seoElements as $seoElement) { |
||
749 | $subQuery = (new Query()) |
||
750 | ->from(['{{%seomatic_metabundles}}']) |
||
751 | ->where(['=', 'sourceBundleType', $seoElement::META_BUNDLE_TYPE]) |
||
752 | ->andWhere(['typeId' => null]); |
||
753 | |||
754 | if ((int)$sourceSiteId !== 0) { |
||
755 | $subQuery->andWhere(['sourceSiteId' => $sourceSiteId]); |
||
756 | } |
||
757 | if ($filter !== '') { |
||
758 | $subQuery->andWhere(['like', 'sourceName', $filter]); |
||
759 | } |
||
760 | $bundleQuery = (new Query()) |
||
761 | ->select(['mb.*']) |
||
762 | ->from(['mb' => $subQuery]) |
||
763 | ->leftJoin(['mb2' => $subQuery], [ |
||
764 | 'and', |
||
765 | '[[mb.sourceId]] = [[mb2.sourceId]]', |
||
766 | '[[mb.id]] < [[mb2.id]]', |
||
767 | ]) |
||
768 | ->where(['mb2.id' => null]); |
||
769 | $bundles = array_merge($bundles, $bundleQuery->all()); |
||
770 | } |
||
771 | foreach ($bundles as $bundle) { |
||
772 | $bundle = array_diff_key($bundle, array_flip(self::IGNORE_DB_ATTRIBUTES)); |
||
773 | $metaBundle = MetaBundle::create($bundle); |
||
774 | if ($metaBundle) { |
||
775 | $metaBundles[] = $metaBundle; |
||
776 | } |
||
777 | } |
||
778 | |||
779 | return $metaBundles; |
||
780 | } |
||
781 | |||
782 | /** |
||
783 | * Get the parent content meta bundle for a given element. |
||
784 | * |
||
785 | * @param Element $element |
||
786 | * @return mixed|MetaBundle|null |
||
787 | */ |
||
788 | public function getContentMetaBundleForElement(Element $element) |
||
789 | { |
||
790 | $source = $this->getMetaSourceFromElement($element); |
||
791 | $key = implode(".", $source) . '.' . $element->siteId; |
||
792 | |||
793 | if (empty($this->elementContentMetaBundles[$key])) { |
||
794 | $this->elementContentMetaBundles[$key] = $this->getMetaBundleBySourceId($source[1], $source[0], $element->siteId, $source[4]); |
||
795 | } |
||
796 | |||
797 | return $this->elementContentMetaBundles[$key]; |
||
798 | } |
||
799 | |||
800 | /** |
||
801 | * Set fields the user is unable to edit to an empty string, so they are |
||
802 | * filtered out when meta containers are combined |
||
803 | * |
||
804 | * @param MetaBundle $metaBundle |
||
805 | * @param string $fieldHandle |
||
806 | */ |
||
807 | public function pruneFieldMetaBundleSettings(MetaBundle $metaBundle, string $fieldHandle) |
||
808 | { |
||
809 | /** @var SeoSettings|null $seoSettingsField */ |
||
810 | $seoSettingsField = Craft::$app->getFields()->getFieldByHandle($fieldHandle); |
||
811 | if ($seoSettingsField) { |
||
812 | $seoSettingsEnabledFields = array_flip(array_merge( |
||
813 | (array)$seoSettingsField->generalEnabledFields, |
||
814 | (array)$seoSettingsField->twitterEnabledFields, |
||
815 | (array)$seoSettingsField->facebookEnabledFields, |
||
816 | (array)$seoSettingsField->sitemapEnabledFields |
||
817 | )); |
||
818 | // Always include some fields, as they are calculated even if not explicitly included |
||
819 | $seoSettingsEnabledFields = array_merge( |
||
820 | $seoSettingsEnabledFields, |
||
821 | array_flip(self::ALWAYS_INCLUDED_SEO_SETTINGS_FIELDS) |
||
822 | ); |
||
823 | // metaGlobalVars |
||
824 | $attributes = $metaBundle->metaGlobalVars->getAttributes(); |
||
825 | |||
826 | // Get a list of explicitly inherited values |
||
827 | $inherited = array_keys(ArrayHelper::remove($attributes, 'inherited', [])); |
||
828 | $emptyValues = array_fill_keys(array_keys(array_diff_key($attributes, $seoSettingsEnabledFields)), ''); |
||
829 | |||
830 | // Nullify the inherited values |
||
831 | $emptyValues = array_merge($emptyValues, array_fill_keys($inherited, '')); |
||
832 | foreach ($inherited as $inheritedAttribute) { |
||
833 | foreach (self::COMPOSITE_INHERITANCE_CHILDREN[$inheritedAttribute] ?? [] as $child) { |
||
834 | list($model, $attribute) = explode('.', $child); |
||
835 | $metaBundle->{$model}->$attribute = ''; |
||
836 | } |
||
837 | } |
||
838 | |||
839 | $attributes = array_merge($attributes, $emptyValues); |
||
840 | $metaBundle->metaGlobalVars->setAttributes($attributes, false); |
||
841 | |||
842 | |||
843 | // Handle the mainEntityOfPage |
||
844 | if (!in_array('mainEntityOfPage', (array)$seoSettingsField->generalEnabledFields, false)) { |
||
845 | $metaBundle->metaGlobalVars->mainEntityOfPage = ''; |
||
846 | } |
||
847 | // metaSiteVars |
||
848 | $attributes = $metaBundle->metaSiteVars->getAttributes(); |
||
849 | $emptyValues = array_fill_keys(array_keys(array_diff_key($attributes, $seoSettingsEnabledFields)), ''); |
||
850 | $attributes = array_merge($attributes, $emptyValues); |
||
851 | $metaBundle->metaSiteVars->setAttributes($attributes, false); |
||
852 | // metaSitemapVars |
||
853 | $attributes = $metaBundle->metaSitemapVars->getAttributes(); |
||
854 | |||
855 | // Get a list of explicitly inherited values |
||
856 | $inherited = array_keys(ArrayHelper::remove($attributes, 'inherited', [])); |
||
857 | $emptyValues = array_fill_keys(array_keys(array_diff_key($attributes, $seoSettingsEnabledFields)), ''); |
||
858 | |||
859 | // Nullify the inherited values |
||
860 | $emptyValues = array_merge($emptyValues, array_fill_keys($inherited, '')); |
||
861 | |||
862 | $attributes = array_merge($attributes, $emptyValues); |
||
863 | $metaBundle->metaSitemapVars->setAttributes($attributes, false); |
||
864 | } |
||
865 | } |
||
866 | |||
867 | /** |
||
868 | * Remove any meta bundles from the $metaBundles array that no longer |
||
869 | * correspond with an SeoElement |
||
870 | * |
||
871 | * @param array $metaBundles |
||
872 | */ |
||
873 | public function pruneVestigialMetaBundles(array &$metaBundles) |
||
874 | { |
||
875 | foreach ($metaBundles as $key => $metaBundle) { |
||
876 | $prune = $this->pruneVestigialMetaBundle($metaBundle); |
||
877 | /** @var MetaBundle $metaBundle */ |
||
878 | if ($prune) { |
||
879 | unset($metaBundles[$key]); |
||
880 | } |
||
881 | } |
||
882 | ArrayHelper::multisort($metaBundles, 'sourceName'); |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * Determine whether a given MetaBundle is vestigial or not |
||
887 | * |
||
888 | * @param $metaBundle |
||
889 | * |
||
890 | * @return bool |
||
891 | */ |
||
892 | public function pruneVestigialMetaBundle($metaBundle): bool |
||
893 | { |
||
894 | $prune = false; |
||
895 | $sourceBundleType = $metaBundle->sourceBundleType; |
||
896 | if ($sourceBundleType && $sourceBundleType !== self::GLOBAL_META_BUNDLE) { |
||
897 | $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType); |
||
898 | if ($seoElement) { |
||
899 | $sourceModel = $seoElement::sourceModelFromId($metaBundle->sourceId); |
||
900 | /** @var Section|CategoryGroup|ProductType|null $sourceModel */ |
||
901 | if ($sourceModel === null) { |
||
902 | $prune = true; |
||
903 | } else { |
||
904 | $prune = true; |
||
905 | $siteSettings = $sourceModel->getSiteSettings(); |
||
906 | if (!empty($siteSettings)) { |
||
907 | /** @var Section_SiteSettings $siteSetting */ |
||
908 | foreach ($siteSettings as $siteSetting) { |
||
909 | if ($siteSetting->siteId == $metaBundle->sourceSiteId && $siteSetting->hasUrls && SiteHelper::siteEnabledWithUrls($siteSetting->siteId)) { |
||
910 | $prune = false; |
||
911 | } |
||
912 | } |
||
913 | } |
||
914 | } |
||
915 | } else { |
||
916 | $prune = true; |
||
917 | } |
||
918 | } |
||
919 | |||
920 | return $prune; |
||
921 | } |
||
922 | |||
923 | /** |
||
924 | * Delete any meta bundles from the $metaBundles array that no longer |
||
925 | * correspond with an SeoElement |
||
926 | * |
||
927 | * @param array $metaBundles |
||
928 | */ |
||
929 | public function deleteVestigialMetaBundles(array $metaBundles) |
||
930 | { |
||
931 | foreach ($metaBundles as $key => $metaBundle) { |
||
932 | $prune = $this->pruneVestigialMetaBundle($metaBundle); |
||
933 | /** @var MetaBundle $metaBundle */ |
||
934 | if ($prune) { |
||
935 | $this->deleteMetaBundleBySourceId( |
||
936 | $metaBundle->sourceBundleType, |
||
937 | $metaBundle->sourceId, |
||
938 | $metaBundle->sourceSiteId |
||
939 | ); |
||
940 | } |
||
941 | } |
||
942 | } |
||
943 | |||
944 | /** |
||
945 | * Delete a meta bundle by $sourceId |
||
946 | * |
||
947 | * @param string $sourceBundleType |
||
948 | * @param int $sourceId |
||
949 | * @param int|null $siteId |
||
950 | */ |
||
951 | public function deleteMetaBundleBySourceId(string $sourceBundleType, int $sourceId, ?int $siteId = null) |
||
952 | { |
||
953 | $sites = []; |
||
954 | if ($siteId === null) { |
||
955 | $sites = Craft::$app->getSites()->getAllSites(); |
||
956 | } else { |
||
957 | $sites[] = Craft::$app->getSites()->getSiteById($siteId); |
||
958 | } |
||
959 | /** @var Site $site */ |
||
960 | foreach ($sites as $site) { |
||
961 | // Look for a matching meta bundle in the db |
||
962 | $metaBundleRecord = MetaBundleRecord::findOne([ |
||
963 | 'sourceBundleType' => $sourceBundleType, |
||
964 | 'sourceId' => $sourceId, |
||
965 | 'sourceSiteId' => $site->id, |
||
966 | ]); |
||
967 | |||
968 | if ($metaBundleRecord) { |
||
969 | try { |
||
970 | $metaBundleRecord->delete(); |
||
971 | } catch (Throwable $e) { |
||
972 | Craft::error($e->getMessage(), __METHOD__); |
||
973 | } |
||
974 | Craft::info( |
||
975 | 'Meta bundle deleted: ' |
||
976 | . $sourceId |
||
977 | . ' from siteId: ' |
||
978 | . $site->id, |
||
979 | __METHOD__ |
||
980 | ); |
||
981 | } |
||
982 | } |
||
983 | } |
||
984 | |||
985 | /** |
||
986 | * Get all of the data from $bundle in containers of $type |
||
987 | * |
||
988 | * @param MetaBundle $bundle |
||
989 | * @param string $type |
||
990 | * |
||
991 | * @return array |
||
992 | */ |
||
993 | public function getContainerDataFromBundle(MetaBundle $bundle, string $type): array |
||
994 | { |
||
995 | $containerData = []; |
||
996 | foreach ($bundle->metaContainers as $metaContainer) { |
||
997 | if ($metaContainer::CONTAINER_TYPE === $type) { |
||
998 | foreach ($metaContainer->data as $dataHandle => $data) { |
||
999 | $containerData[$dataHandle] = $data; |
||
1000 | } |
||
1001 | } |
||
1002 | } |
||
1003 | |||
1004 | return $containerData; |
||
1005 | } |
||
1006 | |||
1007 | /** |
||
1008 | * Create all of the content meta bundles |
||
1009 | */ |
||
1010 | public function createContentMetaBundles() |
||
1011 | { |
||
1012 | $seoElements = Seomatic::$plugin->seoElements->getAllSeoElementTypes(); |
||
1013 | foreach ($seoElements as $seoElement) { |
||
1014 | /** @var SeoElementInterface $seoElement */ |
||
1015 | $seoElement::createAllContentMetaBundles(); |
||
1016 | } |
||
1017 | } |
||
1018 | |||
1019 | /** |
||
1020 | * Create the default global meta bundles |
||
1021 | */ |
||
1022 | public function createGlobalMetaBundles() |
||
1027 | } |
||
1028 | } |
||
1029 | |||
1030 | // Protected Methods |
||
1031 | // ========================================================================= |
||
1032 | |||
1033 | /** |
||
1034 | * Preserve user settings from the meta bundle when updating it from the |
||
1035 | * config |
||
1036 | * |
||
1037 | * @param MetaBundle $metaBundle The new meta bundle |
||
1038 | * @param MetaBundle $baseConfig The existing meta bundle to preserve |
||
1039 | * settings from |
||
1040 | */ |
||
1041 | protected function mergeMetaBundleSettings(MetaBundle $metaBundle, MetaBundle $baseConfig) |
||
1081 | } |
||
1082 | } |
||
1083 | } |
||
1084 | } |
||
1085 | } |
||
1086 | } |
||
1087 | } |
||
1088 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.