Issues (231)

src/services/SeoElements.php (2 issues)

1
<?php
2
/**
3
 * SEOmatic plugin for Craft CMS 3.x
4
 *
5
 * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful,
6
 * and flexible
7
 *
8
 * @link      https://nystudio107.com
9
 * @copyright Copyright (c) 2019 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\services;
13
14
use Craft;
15
use craft\base\Component;
16
use craft\base\ElementInterface;
17
use craft\events\RegisterComponentTypesEvent;
18
use nystudio107\seomatic\base\GqlSeoElementInterface;
19
use nystudio107\seomatic\base\SeoElementInterface;
20
use nystudio107\seomatic\seoelements\SeoCampaign;
21
use nystudio107\seomatic\seoelements\SeoCategory;
22
use nystudio107\seomatic\seoelements\SeoDigitalProduct;
23
use nystudio107\seomatic\seoelements\SeoEntry;
24
use nystudio107\seomatic\seoelements\SeoEvent;
25
use nystudio107\seomatic\seoelements\SeoProduct;
26
use nystudio107\seomatic\Seomatic;
27
28
/**
29
 * @author    nystudio107
30
 * @package   Seomatic
31
 * @since     3.2.0
32
 */
33
class SeoElements extends Component
34
{
35
    // Constants
36
    // =========================================================================
37
38
    /**
39
     * @event RegisterComponentTypesEvent The event that is triggered when
40
     *        registering SeoElement types
41
     *
42
     * SeoElement types must implement [[SeoElementInterface]]
43
     *
44
     * ```php
45
     * use nystudio107\seomatic\services\SeoElements;
46
     * use craft\events\RegisterComponentTypesEvent;
47
     * use yii\base\Event;
48
     *
49
     * Event::on(SeoElements::class,
50
     *     SeoElements::EVENT_REGISTER_SEO_ELEMENT_TYPES,
51
     *     function(RegisterComponentTypesEvent $event) {
52
     *         $event->types[] = MySeoElement::class;
53
     *     }
54
     * );
55
     * ```
56
     */
57
    const EVENT_REGISTER_SEO_ELEMENT_TYPES = 'registerSeoElementTypes';
58
59
    const DEFAULT_SEO_ELEMENT_TYPES = [
60
        SeoCampaign::class,
61
        SeoCategory::class,
62
        SeoDigitalProduct::class,
63
        SeoEntry::class,
64
        SeoEvent::class,
65
        SeoProduct::class,
66
    ];
67
68
    // Protected Properties
69
    // =========================================================================
70
71
    /**
72
     * @var string[] indexed by [sourceType]
73
     */
74
    protected $seoElements;
75
76
    // Public Methods
77
    // =========================================================================
78
79
    /**
80
     * @param null|string $metaBundleType
81
     *
82
     * @return class-string<SeoElementInterface>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<SeoElementInterface>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<SeoElementInterface>|null.
Loading history...
83
     */
84
    public function getSeoElementByMetaBundleType($metaBundleType)
85
    {
86
        if ($metaBundleType === null) {
87
            return null;
88
        }
89
        $seoElements = $this->getAllSeoElementTypes();
90
91
        return $seoElements[$metaBundleType] ?? null;
92
    }
93
94
    /**
95
     * Returns all available field type classes.
96
     *
97
     * @return class-string<SeoElementInterface>[] The available field type classes
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...
98
     */
99
    public function getAllSeoElementTypes(bool $useCache = true): array
100
    {
101
        // Return the memoized version if available
102
        if ($useCache && !empty($this->seoElements)) {
103
            return $this->seoElements;
104
        }
105
        // Merge in the built-in types with the types defined in the config.php
106
        $seoElementTypes = array_unique(array_merge(
107
            SeoMatic::$plugin->getSettings()->defaultSeoElementTypes ?? [],
108
            self::DEFAULT_SEO_ELEMENT_TYPES
109
        ), SORT_REGULAR);
110
        // Throw an event to allow other modules/plugins to define their own types
111
        $event = new RegisterComponentTypesEvent([
112
            'types' => $seoElementTypes,
113
        ]);
114
        $this->trigger(self::EVENT_REGISTER_SEO_ELEMENT_TYPES, $event);
115
        // Index the array by META_BUNDLE_TYPE
116
        /** @var SeoElementInterface $seoElement */
117
        foreach ($event->types as $seoElement) {
118
            $requiredPlugin = $seoElement::getRequiredPluginHandle();
119
            if ($requiredPlugin === null || Craft::$app->getPlugins()->getPlugin($requiredPlugin)) {
120
                $this->seoElements[$seoElement::getMetaBundleType()] = $seoElement;
121
                $seoElement::installEventHandlers();
122
            }
123
        }
124
125
        return $this->seoElements;
126
    }
127
128
    /**
129
     * Return the Meta Bundle type for a given element
130
     *
131
     * @param ElementInterface $element
132
     *
133
     * @return string|null
134
     */
135
    public function getMetaBundleTypeFromElement(ElementInterface $element)
136
    {
137
        $seoElements = $this->getAllSeoElementTypes();
138
        /** @var SeoElementInterface $seoElement */
139
        foreach ($seoElements as $metaBundleType => $seoElement) {
140
            foreach ($seoElement::getElementClasses() as $elementClass) {
141
                if ($element instanceof $elementClass) {
142
                    return $metaBundleType;
143
                }
144
            }
145
        }
146
147
        return null;
148
    }
149
150
    /**
151
     * Returns all available known GQL interfaces used by SEO elements.
152
     *
153
     * @return string[] List of known GQL interface names.
154
     */
155
    public function getAllSeoElementGqlInterfaceNames(): array
156
    {
157
        $seoElements = $this->getAllSeoElementTypes();
158
        $interfaceNames = [];
159
160
        foreach ($seoElements as $seoElement) {
161
            if (is_subclass_of($seoElement, GqlSeoElementInterface::class)) {
162
                $interfaceNames[] = $seoElement::getGqlInterfaceTypeName();
163
            }
164
        }
165
166
        return array_filter($interfaceNames);
167
    }
168
}
169