Sitemaps::anyEntryTypeHasSitemapUrls()   B
last analyzed

Complexity

Conditions 10
Paths 4

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 34
rs 7.6666
c 0
b 0
f 0
cc 10
nc 4
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\ElementInterface;
0 ignored issues
show
Bug introduced by
The type craft\base\ElementInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use craft\errors\SiteNotFoundException;
19
use craft\events\RegisterUrlRulesEvent;
20
use craft\models\Section;
21
use craft\web\UrlManager;
22
use nystudio107\seomatic\base\FrontendTemplate;
23
use nystudio107\seomatic\base\SitemapInterface;
24
use nystudio107\seomatic\helpers\UrlHelper;
25
use nystudio107\seomatic\models\FrontendTemplateContainer;
26
use nystudio107\seomatic\models\MetaBundle;
27
use nystudio107\seomatic\models\SitemapCustomTemplate;
28
use nystudio107\seomatic\models\SitemapIndexTemplate;
29
use nystudio107\seomatic\models\SitemapTemplate;
30
use nystudio107\seomatic\Seomatic;
31
use yii\base\Event;
32
use yii\base\Exception;
33
use yii\base\InvalidConfigException;
34
use yii\caching\TagDependency;
35
36
/**
37
 * @author    nystudio107
38
 * @package   Seomatic
39
 * @since     3.0.0
40
 */
41
class Sitemaps extends Component implements SitemapInterface
42
{
43
    // Constants
44
    // =========================================================================
45
46
    public const SEOMATIC_SITEMAPINDEX_CONTAINER = Seomatic::SEOMATIC_HANDLE . SitemapIndexTemplate::TEMPLATE_TYPE;
47
48
    public const SEOMATIC_SITEMAP_CONTAINER = Seomatic::SEOMATIC_HANDLE . SitemapTemplate::TEMPLATE_TYPE;
49
50
    public const SEOMATIC_SITEMAPCUSTOM_CONTAINER = Seomatic::SEOMATIC_HANDLE . SitemapCustomTemplate::TEMPLATE_TYPE;
51
52
    public const SEARCH_ENGINE_SUBMISSION_URLS = [
53
    ];
54
55
    // Protected Properties
56
    // =========================================================================
57
58
    /**
59
     * @var FrontendTemplateContainer
60
     */
61
    protected $sitemapTemplateContainer;
62
63
    // Public Methods
64
    // =========================================================================
65
66
    /**
67
     * Load in the sitemap frontend template containers
68
     */
69
    public function loadSitemapContainers()
70
    {
71
        if (Seomatic::$settings->sitemapsEnabled) {
72
            $this->sitemapTemplateContainer = FrontendTemplateContainer::create();
73
            // The Sitemap Index
74
            $sitemapIndexTemplate = SitemapIndexTemplate::create();
75
            $this->sitemapTemplateContainer->addData($sitemapIndexTemplate, self::SEOMATIC_SITEMAPINDEX_CONTAINER);
76
            // A custom sitemap
77
            $sitemapCustomTemplate = SitemapCustomTemplate::create();
78
            $this->sitemapTemplateContainer->addData($sitemapCustomTemplate, self::SEOMATIC_SITEMAPCUSTOM_CONTAINER);
79
            // A generic sitemap
80
            $sitemapTemplate = SitemapTemplate::create();
81
            $this->sitemapTemplateContainer->addData($sitemapTemplate, self::SEOMATIC_SITEMAP_CONTAINER);
82
            // Handler: UrlManager::EVENT_REGISTER_SITE_URL_RULES
83
            Event::on(
84
                UrlManager::class,
85
                UrlManager::EVENT_REGISTER_SITE_URL_RULES,
86
                function(RegisterUrlRulesEvent $event) {
87
                    Craft::debug(
88
                        'UrlManager::EVENT_REGISTER_SITE_URL_RULES',
89
                        __METHOD__
90
                    );
91
                    // Register our sitemap routes
92
                    $event->rules = array_merge(
93
                        $event->rules,
94
                        $this->sitemapRouteRules()
95
                    );
96
                }
97
            );
98
        }
99
    }
100
101
    /**
102
     * @return array
103
     */
104
    public function sitemapRouteRules(): array
105
    {
106
        $rules = [];
107
        $groups = Craft::$app->getSites()->getAllGroups();
108
        $groupId = $groups[0]->id;
0 ignored issues
show
Unused Code introduced by
The assignment to $groupId is dead and can be removed.
Loading history...
109
        $currentSite = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $currentSite is dead and can be removed.
Loading history...
110
        try {
111
            $currentSite = Craft::$app->getSites()->getCurrentSite();
112
        } catch (SiteNotFoundException $e) {
113
            Craft::error($e->getMessage(), __METHOD__);
114
        }
115
        if ($currentSite) {
116
            try {
117
                $groupId = $currentSite->getGroup()->id;
118
            } catch (InvalidConfigException $e) {
119
                Craft::error($e->getMessage(), __METHOD__);
120
            }
121
        }
122
        // Add the route to redirect sitemap.xml to the actual sitemap
123
        $route =
124
            Seomatic::$plugin->handle
125
            . '/'
126
            . 'sitemap'
127
            . '/'
128
            . 'sitemap-index-redirect';
129
        $rules['sitemap.xml'] = [
130
            'route' => $route,
131
        ];
132
        // Add the route for the sitemap.xsl styles
133
        $route =
134
            Seomatic::$plugin->handle
135
            . '/'
136
            . 'sitemap'
137
            . '/'
138
            . 'sitemap-styles';
139
        $rules['sitemap.xsl'] = [
140
            'route' => $route,
141
        ];
142
        // Add the route for the sitemap-empty.xsl styles
143
        $route =
144
            Seomatic::$plugin->handle
145
            . '/'
146
            . 'sitemap'
147
            . '/'
148
            . 'sitemap-empty-styles';
149
        $rules['sitemap-empty.xsl'] = [
150
            'route' => $route,
151
        ];
152
        // Add all of the frontend container routes
153
        foreach ($this->sitemapTemplateContainer->data as $sitemapTemplate) {
154
            $rules = array_merge(
155
                $rules,
156
                $sitemapTemplate->routeRules()
157
            );
158
        }
159
160
        return $rules;
161
    }
162
163
    /**
164
     * See if any of the entry types have robots enable and sitemap urls enabled
165
     *
166
     * @param MetaBundle $metaBundle
167
     * @return bool
168
     */
169
    public function anyEntryTypeHasSitemapUrls(MetaBundle $metaBundle): bool
170
    {
171
        $result = false;
172
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($metaBundle->sourceBundleType);
173
        if ($seoElement) {
174
            if (!empty($seoElement::typeMenuFromHandle($metaBundle->sourceHandle))) {
175
                /** @var Section|null $section */
176
                $section = $seoElement::sourceModelFromHandle($metaBundle->sourceHandle);
177
                if ($section !== null) {
178
                    $entryTypes = $section->getEntryTypes();
179
                    // Fetch each meta bundle for each entry type to see if _any_ of them have sitemap URLs
180
                    foreach ($entryTypes as $entryType) {
181
                        $entryTypeBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceId(
182
                            $metaBundle->sourceBundleType,
183
                            $metaBundle->sourceId,
184
                            $metaBundle->sourceSiteId,
185
                            $entryType->id
186
                        );
187
                        if ($entryTypeBundle) {
188
                            $robotsEnabled = true;
189
                            if (!empty($entryTypeBundle->metaGlobalVars->robots)) {
190
                                $robotsEnabled = $entryTypeBundle->metaGlobalVars->robots !== 'none' &&
191
                                    $entryTypeBundle->metaGlobalVars->robots !== 'noindex';
192
                            }
193
                            if ($entryTypeBundle->metaSitemapVars->sitemapUrls && $robotsEnabled) {
194
                                $result = true;
195
                            }
196
                        }
197
                    }
198
                }
199
            }
200
        }
201
202
        return $result;
203
    }
204
205
    /**
206
     * @param string $template
207
     * @param array $params
208
     *
209
     * @return string
210
     */
211
    public function renderTemplate(string $template, array $params = []): string
212
    {
213
        $html = '';
214
        if (!empty($this->sitemapTemplateContainer->data[$template])) {
215
            /** @var FrontendTemplate $sitemapTemplate */
216
            $sitemapTemplate = $this->sitemapTemplateContainer->data[$template];
217
            $html = $sitemapTemplate->render($params);
218
        }
219
220
        return $html;
221
    }
222
223
    /**
224
     * Submit the sitemap index to the search engine services
225
     */
226
    public function submitSitemapIndex()
227
    {
228
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
229
            // Submit the sitemap to each search engine
230
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
231
            // Array is currently empty, but leave the code in place in case submission urls return
232
            /** @phpstan-ignore-next-line */
233
            foreach ($searchEngineUrls as &$url) {
234
                $groups = Craft::$app->getSites()->getAllGroups();
235
                foreach ($groups as $group) {
236
                    $groupSiteIds = $group->getSiteIds();
237
                    if (!empty($groupSiteIds)) {
238
                        $siteId = $groupSiteIds[0];
239
                        $sitemapIndexUrl = $this->sitemapIndexUrlForSiteId($siteId);
240
                        if (!empty($sitemapIndexUrl)) {
241
                            $submissionUrl = $url . urlencode($sitemapIndexUrl);
242
                            // create new guzzle client
243
                            $guzzleClient = Craft::createGuzzleClient(['timeout' => 5, 'connect_timeout' => 5]);
244
                            // Submit the sitemap index to each search engine
245
                            try {
246
                                $guzzleClient->post($submissionUrl);
247
                                Craft::info(
248
                                    'Sitemap index submitted to: ' . $submissionUrl,
249
                                    __METHOD__
250
                                );
251
                            } catch (\Exception $e) {
252
                                Craft::error(
253
                                    'Error submitting sitemap index to: ' . $submissionUrl . ' - ' . $e->getMessage(),
254
                                    __METHOD__
255
                                );
256
                            }
257
                        }
258
                    }
259
                }
260
            }
261
        }
262
    }
263
264
    /**
265
     * Get the URL to the $siteId's sitemap index
266
     *
267
     * @param int|null $siteId
268
     *
269
     * @return string
270
     */
271
    public function sitemapIndexUrlForSiteId(int $siteId = null): string
272
    {
273
        $url = '';
274
        $sites = Craft::$app->getSites();
275
        if ($siteId === null) {
276
            $siteId = $sites->currentSite->id ?? 1;
277
        }
278
        $site = $sites->getSiteById($siteId);
279
        if ($site !== null) {
280
            try {
281
                $url = UrlHelper::siteUrl(
282
                    '/sitemaps-'
283
                    . $site->groupId
284
                    . '-sitemap.xml',
285
                    null,
286
                    null,
287
                    $siteId
288
                );
289
            } catch (Exception $e) {
290
                Craft::error($e->getMessage(), __METHOD__);
291
            }
292
        }
293
294
        return $url;
295
    }
296
297
    /**
298
     * Submit the bundle sitemap to the search engine services
299
     *
300
     * @param ElementInterface $element
301
     */
302
    public function submitSitemapForElement(ElementInterface $element)
303
    {
304
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
305
            /** @var Element $element */
306
            list($sourceId, $sourceBundleType, $sourceHandle, $sourceSiteId, $typeId)
307
                = Seomatic::$plugin->metaBundles->getMetaSourceFromElement($element);
308
            // Submit the sitemap to each search engine
309
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
310
            // Array is currently empty, but leave the code in place in case submission urls return
311
            /** @phpstan-ignore-next-line */
312
            foreach ($searchEngineUrls as &$url) {
313
                $sitemapUrl = $this->sitemapUrlForBundle($sourceBundleType, $sourceHandle, $sourceSiteId);
314
                if (!empty($sitemapUrl)) {
315
                    $submissionUrl = $url . urlencode($sitemapUrl);
316
                    // create new guzzle client
317
                    $guzzleClient = Craft::createGuzzleClient(['timeout' => 5, 'connect_timeout' => 5]);
318
                    // Submit the sitemap index to each search engine
319
                    try {
320
                        $guzzleClient->post($submissionUrl);
321
                        Craft::info(
322
                            'Sitemap index submitted to: ' . $submissionUrl,
323
                            __METHOD__
324
                        );
325
                    } catch (\Exception $e) {
326
                        Craft::error(
327
                            'Error submitting sitemap index to: ' . $submissionUrl . ' - ' . $e->getMessage(),
328
                            __METHOD__
329
                        );
330
                    }
331
                }
332
            }
333
        }
334
    }
335
336
    /**
337
     * @param string $sourceBundleType
338
     * @param string $sourceHandle
339
     * @param int|null $siteId
340
     *
341
     * @return string
342
     */
343
    public function sitemapUrlForBundle(string $sourceBundleType, string $sourceHandle, int $siteId = null, int $page = null): string
344
    {
345
        $url = '';
346
        $sites = Craft::$app->getSites();
347
        if ($siteId === null) {
348
            $siteId = $sites->currentSite->id ?? 1;
349
        }
350
        $site = $sites->getSiteById($siteId);
351
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
352
            $sourceBundleType,
353
            $sourceHandle,
354
            $siteId
355
        );
356
        if ($site && $metaBundle && $metaBundle->metaSitemapVars->sitemapUrls) {
357
            try {
358
                $url = UrlHelper::siteUrl(
359
                    '/sitemaps-'
360
                    . $site->groupId
361
                    . '-'
362
                    . $metaBundle->sourceBundleType
363
                    . '-'
364
                    . $metaBundle->sourceHandle
365
                    . '-'
366
                    . $metaBundle->sourceSiteId
367
                    . '-sitemap'
368
                    . (!empty($page) ? '-p' . $page : '')
369
                    . '.xml',
370
                    null,
371
                    null,
372
                    $siteId
373
                );
374
            } catch (Exception $e) {
375
                Craft::error($e->getMessage(), __METHOD__);
376
            }
377
        }
378
379
        return $url;
380
    }
381
382
    /**
383
     * Submit the bundle sitemap to the search engine services
384
     *
385
     * @param int $siteId
386
     */
387
    public function submitCustomSitemap(int $siteId)
388
    {
389
        if (Seomatic::$settings->sitemapsEnabled && Seomatic::$environment === 'live' && Seomatic::$settings->submitSitemaps) {
390
            // Submit the sitemap to each search engine
391
            $searchEngineUrls = self::SEARCH_ENGINE_SUBMISSION_URLS;
392
            // Array is currently empty, but leave the code in place in case submission urls return
393
            /** @phpstan-ignore-next-line */
394
            foreach ($searchEngineUrls as &$url) {
395
                $sitemapUrl = $this->sitemapCustomUrlForSiteId($siteId);
396
                if (!empty($sitemapUrl)) {
397
                    $submissionUrl = $url . urlencode($sitemapUrl);
398
                    // create new guzzle client
399
                    $guzzleClient = Craft::createGuzzleClient(['timeout' => 5, 'connect_timeout' => 5]);
400
                    // Submit the sitemap index to each search engine
401
                    try {
402
                        $guzzleClient->post($submissionUrl);
403
                        Craft::info(
404
                            'Sitemap Custom submitted to: ' . $submissionUrl,
405
                            __METHOD__
406
                        );
407
                    } catch (\Exception $e) {
408
                        Craft::error(
409
                            'Error submitting sitemap index to: ' . $submissionUrl . ' - ' . $e->getMessage(),
410
                            __METHOD__
411
                        );
412
                    }
413
                }
414
            }
415
        }
416
    }
417
418
    /**
419
     * @param int|null $siteId
420
     *
421
     * @return string
422
     */
423
    public function sitemapCustomUrlForSiteId(int $siteId = null)
424
    {
425
        $url = '';
426
        $sites = Craft::$app->getSites();
427
        if ($siteId === null) {
428
            $siteId = $sites->currentSite->id ?? 1;
429
        }
430
        $site = $sites->getSiteById($siteId);
431
        if ($site) {
432
            try {
433
                $url = UrlHelper::siteUrl(
434
                    '/sitemaps-'
435
                    . $site->groupId
436
                    . '-'
437
                    . SitemapCustomTemplate::CUSTOM_SCOPE
438
                    . '-'
439
                    . SitemapCustomTemplate::CUSTOM_HANDLE
440
                    . '-'
441
                    . $siteId
442
                    . '-sitemap.xml',
443
                    null,
444
                    null,
445
                    $siteId
446
                );
447
            } catch (Exception $e) {
448
                Craft::error($e->getMessage(), __METHOD__);
449
            }
450
        }
451
452
        return $url;
453
    }
454
455
    /**
456
     * Return all of the sitemap indexes the current group of sites
457
     *
458
     * @return string
459
     */
460
    public function sitemapIndex(): string
461
    {
462
        $result = '';
463
        $sites = [];
464
        // If sitemaps aren't enabled globally, return nothing for the sitemap index
465
        if (!Seomatic::$settings->sitemapsEnabled) {
466
            return '';
467
        }
468
        if (Seomatic::$settings->siteGroupsSeparate) {
469
            // Get only the sites that are in the current site's group
470
            try {
471
                $siteGroup = Craft::$app->getSites()->getCurrentSite()->getGroup();
472
            } catch (InvalidConfigException $e) {
473
                $siteGroup = null;
474
                Craft::error($e->getMessage(), __METHOD__);
475
            }
476
            // If we can't get a group, just use the current site
477
            if ($siteGroup === null) {
478
                $sites = [Craft::$app->getSites()->getCurrentSite()];
479
            } else {
480
                $sites = $siteGroup->getSites();
481
            }
482
        } else {
483
            $sites = Craft::$app->getSites()->getAllSites();
484
        }
485
486
        foreach ($sites as $site) {
487
            $result .= 'sitemap: ' . $this->sitemapIndexUrlForSiteId($site->id) . PHP_EOL;
488
        }
489
490
        return rtrim($result, PHP_EOL);
491
    }
492
493
    /**
494
     * Invalidate all of the sitemap caches
495
     */
496
    public function invalidateCaches()
497
    {
498
        $cache = Craft::$app->getCache();
499
        TagDependency::invalidate($cache, self::GLOBAL_SITEMAP_CACHE_TAG);
500
        Craft::info(
501
            'All sitemap caches cleared',
502
            __METHOD__
503
        );
504
    }
505
506
    /**
507
     * Invalidate the sitemap cache passed in $handle
508
     *
509
     * @param string $handle
510
     * @param int|null $siteId
511
     * @param string $type
512
     * @param bool $invalidateCache
513
     */
514
    public function invalidateSitemapCache(string $handle, ?int $siteId, string $type, bool $invalidateCache = true)
0 ignored issues
show
Unused Code introduced by
The parameter $type 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

514
    public function invalidateSitemapCache(string $handle, ?int $siteId, /** @scrutinizer ignore-unused */ string $type, bool $invalidateCache = true)

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...
Unused Code introduced by
The parameter $invalidateCache 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

514
    public function invalidateSitemapCache(string $handle, ?int $siteId, string $type, /** @scrutinizer ignore-unused */ bool $invalidateCache = true)

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...
515
    {
516
        // Always just invalidate the sitemap cache now, since we're doing paginated sitemaps
517
        $cache = Craft::$app->getCache();
518
        TagDependency::invalidate($cache, SitemapTemplate::SITEMAP_CACHE_TAG . $handle . $siteId);
519
        Craft::info(
520
            'Sitemap cache cleared: ' . $handle,
521
            __METHOD__
522
        );
523
    }
524
525
    /**
526
     * Invalidate the sitemap index cache
527
     */
528
    public function invalidateSitemapIndexCache()
529
    {
530
        $cache = Craft::$app->getCache();
531
        TagDependency::invalidate($cache, SitemapIndexTemplate::SITEMAP_INDEX_CACHE_TAG);
532
        Craft::info(
533
            'Sitemap index cache cleared',
534
            __METHOD__
535
        );
536
    }
537
}
538