MetaBundles::deleteMetaBundleBySourceId()   A
last analyzed

Complexity

Conditions 5
Paths 8

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 21
c 0
b 0
f 0
dl 0
loc 29
ccs 0
cts 20
cp 0
rs 9.2728
cc 5
nc 8
nop 3
crap 30
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) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\services;
13
14
use Craft;
15
use craft\base\Component;
16
use craft\base\Element;
17
use craft\base\Model;
18
use craft\commerce\models\ProductType;
19
use craft\db\Query;
20
use craft\models\CategoryGroup;
21
use craft\models\Section;
22
use craft\models\Section_SiteSettings;
23
use craft\models\Site;
24
use DateTime;
25
use nystudio107\seomatic\base\SeoElementInterface;
26
use nystudio107\seomatic\fields\SeoSettings;
27
use nystudio107\seomatic\helpers\ArrayHelper;
28
use nystudio107\seomatic\helpers\Config as ConfigHelper;
29
use nystudio107\seomatic\helpers\MetaValue as MetaValueHelper;
30
use nystudio107\seomatic\helpers\Migration as MigrationHelper;
31
use nystudio107\seomatic\helpers\SiteHelper;
32
use nystudio107\seomatic\models\MetaBundle;
33
use nystudio107\seomatic\models\MetaScriptContainer;
34
use nystudio107\seomatic\models\metatag\RobotsTag;
35
use nystudio107\seomatic\models\MetaTagContainer;
36
use nystudio107\seomatic\records\MetaBundle as MetaBundleRecord;
37
use nystudio107\seomatic\Seomatic;
38
use nystudio107\seomatic\services\Tag as TagService;
39
use Throwable;
40
use yii\base\InvalidConfigException;
41
use function in_array;
42
43
/**
44
 * Meta bundle functions for SEOmatic
45
 * An instance of the service is available via [[`Seomatic::$plugin->metaBundles`|`seomatic.bundles`]]
46
 *
47
 * @author    nystudio107Meta bundle failed validation
48
 * @package   Seomatic
49
 * @since     3.0.0
50
 */
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(
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $metaBundle is correct as $this->createMetaBundleF...rceSiteId, $metaBundle) targeting nystudio107\seomatic\ser...aBundleFromSeoElement() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

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.

Loading history...
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
245
    {
246
        // Create a new meta bundle with propagated defaults
247
        $metaBundleDefaults = ArrayHelper::merge(
248
            ConfigHelper::getConfigFromFile('globalmeta/Bundle'),
249
            [
250
                'sourceSiteId' => $siteId,
251
            ]
252
        );
253
        // The computedType must be set before creating the bundle
254
        if ($baseConfig !== null) {
255
            $metaBundleDefaults['metaGlobalVars']['mainEntityOfPage'] = $baseConfig->metaGlobalVars->mainEntityOfPage;
256
            $metaBundleDefaults['metaSiteVars']['identity']['computedType'] =
257
                $baseConfig->metaSiteVars->identity->computedType;
258
            $metaBundleDefaults['metaSiteVars']['creator']['computedType'] =
259
                $baseConfig->metaSiteVars->creator->computedType;
260
        }
261
        $metaBundle = MetaBundle::create($metaBundleDefaults);
262
        if ($metaBundle !== null) {
263
            if ($baseConfig !== null) {
264
                $this->mergeMetaBundleSettings($metaBundle, $baseConfig);
265
            }
266
            $this->updateMetaBundle($metaBundle, $siteId);
267
        }
268
269
        return $metaBundle;
270
    }
271
272
    /**
273
     * @param MetaBundle $metaBundle
274
     * @param int $siteId
275
     */
276
    public function updateMetaBundle(MetaBundle $metaBundle, int $siteId)
277
    {
278
        $metaBundle->sourceName = (string)$metaBundle->sourceName;
279
        $metaBundle->sourceTemplate = (string)$metaBundle->sourceTemplate;
280
        // Make sure it validates
281
        if ($metaBundle->validate(null, true)) {
282
            // Save it out to a record
283
            $params = [
284
                'sourceBundleType' => $metaBundle->sourceBundleType,
285
                'sourceId' => $metaBundle->sourceId,
286
                'sourceSiteId' => $siteId,
287
            ];
288
            if ($metaBundle->typeId !== null) {
289
                $metaBundle->typeId = (int)$metaBundle->typeId;
290
            }
291
            if (!empty($metaBundle->typeId)) {
292
                $params['typeId'] = $metaBundle->typeId;
293
            } else {
294
                $metaBundle->typeId = null;
295
            }
296
            $metaBundleRecord = MetaBundleRecord::findOne($params);
297
298
            if (!$metaBundleRecord) {
0 ignored issues
show
introduced by
$metaBundleRecord is of type yii\db\ActiveRecord, thus it always evaluated to true.
Loading history...
299
                $metaBundleRecord = new MetaBundleRecord();
300
            }
301
302
            // @TODO remove this hack that doesn't allow environment-transformed settings to be saved in a meta bundle with a proper system to address it
303
            // The issue was that the containers were getting saved to the db with a hard-coded setting in them, because they'd
304
            // been set that way by the environment, whereas to be changeable via the GUI, it needs to be set to {seomatic.meta.robots}
305
            /** @var RobotsTag|null $robotsTag */
306
            $robotsTag = $metaBundle->metaContainers[MetaTagContainer::CONTAINER_TYPE . TagService::GENERAL_HANDLE]->data['robots'] ?? null;
307
            if (!empty($robotsTag)) {
308
                $robotsTag->content = $robotsTag->environment['live']['content'] ?? '{{ seomatic.meta.robots }}';
309
            }
310
311
            $metaBundleRecord->setAttributes($metaBundle->getAttributes(), false);
312
313
            if ($metaBundleRecord->save()) {
314
                Craft::info(
315
                    'Meta bundle updated: '
316
                    . $metaBundle->sourceBundleType
317
                    . ' id: '
318
                    . $metaBundle->sourceId
319
                    . ' from siteId: '
320
                    . $metaBundle->sourceSiteId,
321
                    __METHOD__
322
                );
323
            }
324
        } else {
325
            Craft::error(
326
                'Meta bundle failed validation: '
327
                . print_r($metaBundle->getErrors(), true)
0 ignored issues
show
Bug introduced by
Are you sure print_r($metaBundle->getErrors(), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

327
                . /** @scrutinizer ignore-type */ print_r($metaBundle->getErrors(), true)
Loading history...
328
                . ' type: '
329
                . $metaBundle->sourceType
330
                . ' id: '
331
                . $metaBundle->sourceId
332
                . ' from siteId: '
333
                . $metaBundle->sourceSiteId,
334
                __METHOD__
335
            );
336
        }
337
    }
338
339
    /**
340
     * @param class-string<SeoElementInterface> $seoElement
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SeoElementInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SeoElementInterface>.
Loading history...
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)
434
    {
435
        $metaBundle = null;
436
        $typeId = (int)$typeId;
437
        // See if we have the meta bundle cached
438
        if (!empty($this->metaBundlesBySourceHandle[$sourceBundleType][$sourceHandle][$sourceSiteId][$typeId])) {
439
            $id = $this->metaBundlesBySourceHandle[$sourceBundleType][$sourceHandle][$sourceSiteId][$typeId];
440
            if (!empty($this->metaBundles[$id])) {
441
                return $this->metaBundles[$id];
442
            }
443
        }
444
        // Look for a matching meta bundle in the db
445
        $query = (new Query())
446
            ->from(['{{%seomatic_metabundles}}'])
447
            ->where([
448
                'sourceBundleType' => $sourceBundleType,
449
                'sourceHandle' => $sourceHandle,
450
                'sourceSiteId' => $sourceSiteId,
451
            ]);
452
        if (!empty($typeId)) {
453
            $query
454
                ->andWhere([
455
                    'typeId' => $typeId,
456
                ]);
457
        }
458
        $metaBundleArray = $query
459
            ->one();
460
        // If the specific query with a `typeId` returned nothing, try a more general query without `typeId`
461
        if (empty($metaBundleArray)) {
462
            $metaBundleArray = (new Query())
463
                ->from(['{{%seomatic_metabundles}}'])
464
                ->where([
465
                    'sourceBundleType' => $sourceBundleType,
466
                    'sourceHandle' => $sourceHandle,
467
                    'sourceSiteId' => $sourceSiteId,
468
                ])
469
                ->one();
470
        }
471
        if (!empty($metaBundleArray)) {
472
            $metaBundleArray = array_diff_key($metaBundleArray, array_flip(self::IGNORE_DB_ATTRIBUTES));
473
            $metaBundle = MetaBundle::create($metaBundleArray);
474
            $id = count($this->metaBundles);
475
            $this->metaBundles[$id] = $metaBundle;
476
            $this->metaBundlesBySourceHandle[$sourceBundleType][$sourceHandle][$sourceSiteId][$typeId] = $id;
477
        } else {
478
            // If it doesn't exist, create it
479
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
480
            if ($seoElement !== null) {
481
                $sourceModel = $seoElement::sourceModelFromHandle($sourceHandle);
482
                if ($sourceModel) {
483
                    $metaBundle = $this->createMetaBundleFromSeoElement($seoElement, $sourceModel, $sourceSiteId, null, true);
484
                }
485
            }
486
        }
487
488
        return $metaBundle;
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);
0 ignored issues
show
Bug introduced by
It seems like $sourceId can also be of type null; however, parameter $sourceId of nystudio107\seomatic\ser...tMetaBundleBySourceId() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

504
            $metaBundle = $this->getMetaBundleBySourceId($sourceBundleType, /** @scrutinizer ignore-type */ $sourceId, $site->id);
Loading history...
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,
0 ignored issues
show
Bug introduced by
It seems like $sourceId can also be of type null; however, parameter $sourceId of nystudio107\seomatic\ser...ateContainerCacheById() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

530
                        /** @scrutinizer ignore-type */ $sourceId,
Loading history...
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) {
0 ignored issues
show
introduced by
The condition $metaBundleInvalidated is always false.
Loading history...
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);
0 ignored issues
show
Bug introduced by
It seems like $sourceSiteId can also be of type null; however, parameter $sourceSiteId of nystudio107\seomatic\ser...aBundleFromSeoElement() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

611
                    $metaBundle = $this->createMetaBundleFromSeoElement($seoElement, $sourceModel, /** @scrutinizer ignore-type */ $sourceSiteId, null, true);
Loading history...
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)
0 ignored issues
show
Unused Code introduced by
The parameter $isNew 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 ignore-unused  annotation

651
    public function invalidateMetaBundleByElement($element, /** @scrutinizer ignore-unused */ bool $isNew = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
652
    {
653
        $metaBundleInvalidated = false;
654
        $invalidateMetaBundle = true;
655
        $sitemapInvalidated = false;
656
        if ($element->getIsDraft() || $element->getIsRevision()) {
0 ignored issues
show
Bug introduced by
The method getIsDraft() 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 ignore-call  annotation

656
        if ($element->/** @scrutinizer ignore-call */ getIsDraft() || $element->getIsRevision()) {

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.

Loading history...
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
713
    {
714
        $sourceId = 0;
715
        $typeId = null;
716
        $sourceSiteId = 0;
717
        $sourceHandle = '';
718
        // See if this is a section we are tracking
719
        $sourceBundleType = Seomatic::$plugin->seoElements->getMetaBundleTypeFromElement($element);
720
        if ($sourceBundleType) {
721
            $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($sourceBundleType);
722
            if ($seoElement) {
723
                $sourceId = $seoElement::sourceIdFromElement($element);
724
                $typeId = $seoElement::typeIdFromElement($element);
725
                $sourceHandle = $seoElement::sourceHandleFromElement($element);
726
                $sourceSiteId = $element->siteId;
727
            }
728
        } else {
729
            $sourceBundleType = '';
730
        }
731
732
        return [$sourceId, $sourceBundleType, $sourceHandle, $sourceSiteId, $typeId];
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();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

824
            /** @scrutinizer ignore-call */ 
825
            $attributes = $metaBundle->metaGlobalVars->getAttributes();

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.

Loading history...
825
826
            // Get a list of explicitly inherited values
827
            $inherited = array_keys(ArrayHelper::remove($attributes, 'inherited', []));
0 ignored issues
show
Bug introduced by
It seems like nystudio107\seomatic\hel..., 'inherited', array()) can also be of type null; however, parameter $array of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

827
            $inherited = array_keys(/** @scrutinizer ignore-type */ ArrayHelper::remove($attributes, 'inherited', []));
Loading history...
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();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

848
            /** @scrutinizer ignore-call */ 
849
            $attributes = $metaBundle->metaSiteVars->getAttributes();

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.

Loading history...
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();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

853
            /** @scrutinizer ignore-call */ 
854
            $attributes = $metaBundle->metaSitemapVars->getAttributes();

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.

Loading history...
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()
1023
    {
1024
        $sites = Craft::$app->getSites()->getAllSites();
1025
        foreach ($sites as $site) {
1026
            $this->createGlobalMetaBundleForSite($site->id);
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)
1042
    {
1043
        // Preserve the metaGlobalVars
1044
        $attributes = $baseConfig->metaGlobalVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

1044
        /** @scrutinizer ignore-call */ 
1045
        $attributes = $baseConfig->metaGlobalVars->getAttributes();

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.

Loading history...
1045
        $metaBundle->metaGlobalVars->setAttributes($attributes);
1046
        // Preserve the metaSiteVars
1047
        if ($baseConfig->metaSiteVars !== null) {
1048
            $attributes = $baseConfig->metaSiteVars->getAttributes();
1049
            $metaBundle->metaSiteVars->setAttributes($attributes);
1050
            if ($baseConfig->metaSiteVars->identity !== null) {
1051
                $attributes = $baseConfig->metaSiteVars->identity->getAttributes();
1052
                $metaBundle->metaSiteVars->identity->setAttributes($attributes);
0 ignored issues
show
Bug introduced by
The method setAttributes() 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 ignore-call  annotation

1052
                $metaBundle->metaSiteVars->identity->/** @scrutinizer ignore-call */ 
1053
                                                     setAttributes($attributes);

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.

Loading history...
1053
            }
1054
            if ($baseConfig->metaSiteVars->creator !== null) {
1055
                $attributes = $baseConfig->metaSiteVars->creator->getAttributes();
1056
                $metaBundle->metaSiteVars->creator->setAttributes($attributes);
0 ignored issues
show
Bug introduced by
The method setAttributes() 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 ignore-call  annotation

1056
                $metaBundle->metaSiteVars->creator->/** @scrutinizer ignore-call */ 
1057
                                                    setAttributes($attributes);

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.

Loading history...
1057
            }
1058
        }
1059
        // Preserve the Frontend Templates container user settings, but update everything else
1060
        foreach ($baseConfig->frontendTemplatesContainer->data as $baseMetaContainerName => $baseMetaContainer) {
1061
            $attributes = $baseMetaContainer->getAttributes();
1062
            if (!empty($metaBundle->frontendTemplatesContainer->data[$baseMetaContainerName])) {
1063
                foreach (self::PRESERVE_FRONTEND_TEMPLATE_SETTINGS as $frontendTemplateSetting) {
1064
                    $metaBundle->frontendTemplatesContainer->data[$baseMetaContainerName]->$frontendTemplateSetting = $attributes[$frontendTemplateSetting] ?? '';
1065
                }
1066
            }
1067
        }
1068
        // Preserve the metaSitemapVars
1069
        $attributes = $baseConfig->metaSitemapVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

1069
        /** @scrutinizer ignore-call */ 
1070
        $attributes = $baseConfig->metaSitemapVars->getAttributes();

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.

Loading history...
1070
        $metaBundle->metaSitemapVars->setAttributes($attributes);
1071
        // Preserve the metaBundleSettings
1072
        $attributes = $baseConfig->metaBundleSettings->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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 ignore-call  annotation

1072
        /** @scrutinizer ignore-call */ 
1073
        $attributes = $baseConfig->metaBundleSettings->getAttributes();

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.

Loading history...
1073
        $metaBundle->metaBundleSettings->setAttributes($attributes);
0 ignored issues
show
Bug introduced by
The method setAttributes() 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 ignore-call  annotation

1073
        $metaBundle->metaBundleSettings->/** @scrutinizer ignore-call */ 
1074
                                         setAttributes($attributes);

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.

Loading history...
1074
        // Preserve the Script container user settings, but update everything else
1075
        foreach ($baseConfig->metaContainers as $baseMetaContainerName => $baseMetaContainer) {
1076
            if ($baseMetaContainer::CONTAINER_TYPE === MetaScriptContainer::CONTAINER_TYPE) {
1077
                foreach ($baseMetaContainer->data as $key => $value) {
1078
                    if (!empty($metaBundle->metaContainers[$baseMetaContainerName])) {
1079
                        foreach (self::PRESERVE_SCRIPT_SETTINGS as $scriptSetting) {
1080
                            $metaBundle->metaContainers[$baseMetaContainerName]->data[$key][$scriptSetting] = $value[$scriptSetting] ?? '';
1081
                        }
1082
                    }
1083
                }
1084
            }
1085
        }
1086
    }
1087
}
1088