Passed
Push — develop ( 674324...27f087 )
by Andrew
40:51 queued 22:51
created

MetaItem::fields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 2
nop 0
dl 0
loc 19
ccs 0
cts 10
cp 0
crap 6
rs 9.8333
c 0
b 0
f 0
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) 2017 nystudio107
10
 */
11
12
namespace nystudio107\seomatic\base;
13
14
use Craft;
15
use nystudio107\seomatic\behaviors\MetaItemAttributeParserBehavior;
16
use nystudio107\seomatic\helpers\ArrayHelper;
17
use nystudio107\seomatic\helpers\Dependency;
18
use nystudio107\seomatic\models\MetaJsonLd;
19
use nystudio107\seomatic\Seomatic;
20
use yii\helpers\Inflector;
21
22
/**
23
 * @author    nystudio107
24
 * @package   Seomatic
25
 * @since     3.0.0
26
 */
27
abstract class MetaItem extends FluentModel implements MetaItemInterface
28
{
29
    // Traits
30
    // =========================================================================
31
32
    use MetaItemTrait;
33
34
    // Constants
35
    // =========================================================================
36
37
    const ARRAY_PROPERTIES = [
38
    ];
39
40
    // Public Methods
41
    // =========================================================================
42
43
    /**
44
     * @inheritdoc
45
     */
46 1
    public function init()
47
    {
48 1
        parent::init();
49
        // Set any per-environment attributes
50 1
        if (!Seomatic::$previewingMetaContainers || Seomatic::$headlessRequest) {
51 1
            $attributes = [];
52 1
            $envVars = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $envVars is dead and can be removed.
Loading history...
53
            try {
54 1
                $envVars = ArrayHelper::getValue($this->environment, Seomatic::$environment);
55
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
56
            }
57 1
            if (\is_array($envVars)) {
58
                foreach ($envVars as $key => $value) {
59
                    $attributes[$key] = $value;
60
                }
61
            }
62 1
            $this->setAttributes($attributes, false);
63
        }
64 1
    }
65
66
    /**
67
     * @inheritdoc
68
     */
69
    public function rules()
70
    {
71
        $rules = parent::rules();
72
        $rules = array_merge($rules, [
73
            [['key'], 'required'],
74
            [['include'], 'boolean'],
75
            [['key'], 'string'],
76
            [['environment'], 'safe'],
77
            [['dependencies'], 'safe'],
78
            [['tagAttrs'], 'safe'],
79
        ]);
80
81
        return $rules;
82
    }
83
84
    /**
85
     * @inheritdoc
86
     */
87
    public function fields()
88
    {
89
        $fields = parent::fields();
90
        switch ($this->scenario) {
91
            case 'render':
92
                $fields = array_diff_key(
93
                    $fields,
94
                    array_flip([
95
                        'include',
96
                        'key',
97
                        'environment',
98
                        'dependencies',
99
                        'tagAttrs',
100
                    ])
101
                );
102
                break;
103
        }
104
105
        return $fields;
106
    }
107
108
    /**
109
     * @inheritdoc
110
     */
111 1
    public function prepForRender(&$data): bool
112
    {
113 1
        if ($this->include) {
114 1
            return Dependency::validateDependencies($this->dependencies);
115
        }
116
117
        return false;
118
    }
119
120
    /**
121
     * @inheritdoc
122
     */
123
    public function render(array $params = []): string
124
    {
125
        return '';
126
    }
127
128
    /**
129
     * @inheritdoc
130
     */
131
    public function renderAttributes(array $params = []): array
132
    {
133
        return [];
134
    }
135
136
    /**
137
     * Add debug logging for the MetaItem
138
     *
139
     * @param string $errorLabel
140
     * @param array $scenarios
141
     */
142
    public function debugMetaItem(
143
        $errorLabel = 'Error: ',
144
        array $scenarios = ['default' => 'error']
145
    )
146
    {
147
        $isMetaJsonLdModel = false;
148
        if (is_subclass_of($this, MetaJsonLd::class)) {
149
            $isMetaJsonLdModel = true;
150
        }
151
        $modelScenarios = $this->scenarios();
152
        $scenarios = array_intersect_key($scenarios, $modelScenarios);
153
        foreach ($scenarios as $scenario => $logLevel) {
154
            $this->setScenario($scenario);
155
            if (!$this->validate()) {
156
                $extraInfo = '';
157
                // Add a URL to the schema.org type if this is a MetaJsonLD object
158
                if ($isMetaJsonLdModel) {
159
                    /** @var  $this MetaJsonLd */
160
                    $extraInfo = ' for http://schema.org/' . $this->type;
161
                }
162
                $errorMsg =
163
                    Craft::t('seomatic', 'Scenario: "')
164
                    . $scenario
165
                    . '"'
166
                    . $extraInfo
167
                    . PHP_EOL
168
                    . print_r($this->render(), true);
0 ignored issues
show
Bug introduced by
Are you sure print_r($this->render(), 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

168
                    . /** @scrutinizer ignore-type */ print_r($this->render(), true);
Loading history...
169
                Craft::info($errorMsg, __METHOD__);
170
                foreach ($this->errors as $param => $errors) {
171
                    $errorMsg = Craft::t('seomatic', $errorLabel) . $param;
172
                    /** @var array $errors */
173
                    foreach ($errors as $error) {
174
                        $errorMsg .= ' -> ' . $error;
175
                        // Change the error level depending on the error message if this is a MetaJsonLD object
176
                        if ($isMetaJsonLdModel) {
177
                            if (strpos($error, 'recommended') !== false) {
178
                                $logLevel = 'warning';
179
                            }
180
                            if (strpos($error, 'required') !== false
181
                                || strpos($error, 'Must be') !== false
182
                            ) {
183
                                $logLevel = 'error';
184
                            }
185
                        }
186
                    }
187
                    Craft::info(strtoupper($logLevel) . ' - ' . $errorMsg, __METHOD__);
188
                    // Extra debugging info for MetaJsonLd objects
189
                    if ($isMetaJsonLdModel) {
190
                        /** @var MetaJsonLd $className */
191
                        $className = \get_class($this);
192
                        if (!empty($className::$schemaPropertyDescriptions[$param])) {
0 ignored issues
show
Bug Best Practice introduced by
The property schemaPropertyDescriptions does not exist on nystudio107\seomatic\models\MetaJsonLd. Since you implemented __get, consider adding a @property annotation.
Loading history...
193
                            $errorMsg = Craft::t('seomatic', $errorLabel) . $param;
194
                            /** @var $className MetaJsonLd */
195
                            $errorMsg .= ' -> ' . $className::$schemaPropertyDescriptions[$param];
196
                            Craft::info($errorMsg, __METHOD__);
197
                        }
198
                    }
199
                }
200
            }
201
        }
202
    }
203
204
    /**
205
     * Return an array of tag attributes, normalizing the keys
206
     *
207
     * @return array
208
     */
209
    public function tagAttributes(): array
210
    {
211
        $attrs = $this->tagAttrs ?? [];
212
        if (!is_array($attrs)) {
0 ignored issues
show
introduced by
The condition is_array($attrs) is always true.
Loading history...
213
            $attrs = [];
214
        }
215
        if (!empty($attrs) && ArrayHelper::isIndexed($attrs, true)) {
216
            $attrs = [];
217
            foreach ($this->tagAttrs as $attr) {
218
                $attrs[$attr['name']] = $attr['value'];
219
            }
220
        }
221
        $tagAttributes = array_merge($this->toArray(), $attrs);
222
        $tagAttributes = array_filter($tagAttributes);
223
        foreach ($tagAttributes as $key => $value) {
224
            ArrayHelper::rename($tagAttributes, $key, Inflector::slug(Inflector::titleize($key)));
225
        }
226
        ksort($tagAttributes);
227
228
        return $tagAttributes;
229
    }
230
231
    /**
232
     * Return an array of arrays that contain the meta tag attributes
233
     *
234
     * @return array
235
     */
236
    public function tagAttributesArray(): array
237
    {
238
        $result = [];
239
        $optionsCount = 1;
240
        $scenario = $this->scenario;
241
        $this->setScenario('render');
242
        $options = $this->tagAttributes();
243
        $this->setScenario($scenario);
244
245
        // See if any of the potentially array properties actually are
246
        foreach (static::ARRAY_PROPERTIES as $arrayProperty) {
247
            if (!empty($options[$arrayProperty]) && \is_array($options[$arrayProperty])) {
248
                $optionsCount = \count($options[$arrayProperty]) > $optionsCount
249
                    ? \count($options[$arrayProperty]) : $optionsCount;
250
            }
251
        }
252
        // Return an array of resulting options
253
        while ($optionsCount--) {
254
            $resultOptions = $options;
255
            foreach ($resultOptions as $key => $value) {
256
                $resultOptions[$key] = (\is_array($value) && isset($value[$optionsCount]))
257
                    ? $value[$optionsCount] : $value;
258
            }
259
            $result[] = $resultOptions;
260
        }
261
262
        return $result;
263
    }
264
265
    /**
266
     * Validate the passed in $attribute as either an array or a string
267
     *
268
     * @param mixed $attribute the attribute currently being validated
269
     * @param mixed $params the value of the "params" given in the rule
270
     */
271
    public function validateStringOrArray(
272
        $attribute,
273
        $params
0 ignored issues
show
Unused Code introduced by
The parameter $params 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

273
        /** @scrutinizer ignore-unused */ $params

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...
274
    )
275
    {
276
        $validated = false;
277
        if (\is_string($attribute)) {
278
            $validated = true;
279
        }
280
        if (\is_array($attribute)) {
281
            $validated = true;
282
        }
283
        if (!$validated) {
284
            $this->addError($attribute, 'Must be either a string or an array');
285
        }
286
    }
287
288
    /**
289
     * @inheritdoc
290
     */
291
    protected function defineBehaviors(): array
292
    {
293
        return [
294
            'parser' => [
295
                'class' => MetaItemAttributeParserBehavior::class,
296
                'attributes' => [
297
                ],
298
            ],
299
        ];
300
    }
301
}
302